Skip to content

Commit 56ce6b4

Browse files
Merge pull request #4961 from linuxfoundation/unicron-4939-handle-lf_email-prod
Handle lf email per provider (read only) - prod
2 parents a22d36d + 871041f commit 56ce6b4

7 files changed

Lines changed: 127 additions & 30 deletions

File tree

cla-backend-go/v2/sign/service.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,7 +1333,7 @@ func (s *service) RequestIndividualSignature(ctx context.Context, input *models.
13331333

13341334
// creating individual default values
13351335
log.WithFields(f).Debugf("creating individual default values...")
1336-
defaultValues := s.createDefaultIndividualValues(user, preferredEmail)
1336+
defaultValues := s.createDefaultIndividualValues(user, preferredEmail, false)
13371337
log.WithFields(f).Debugf("default values: %+v", defaultValues)
13381338

13391339
// 4. Generate signature callback url
@@ -2057,11 +2057,12 @@ func (s *service) populateUserDetails(ctx context.Context, signatureReferenceTyp
20572057
return nil, errors.New(msg)
20582058
}
20592059

2060+
allowLFEmail := strings.EqualFold(latestSignature.SignatureReturnURLType, "gerrit")
20602061
if userModel.Username != "" {
20612062
userSignDetails.userSignatureName = userModel.Username
20622063
}
2063-
if getUserEmail(userModel, preferredEmail) != "" {
2064-
userSignDetails.userSignatureEmail = getUserEmail(userModel, preferredEmail)
2064+
if userEmail := getUserEmail(userModel, preferredEmail, allowLFEmail); userEmail != "" {
2065+
userSignDetails.userSignatureEmail = userEmail
20652066
}
20662067
}
20672068
} else {
@@ -2161,13 +2162,16 @@ func getTabsFromDocument(document *v1Models.ClaGroupDocument, documentID string,
21612162
}
21622163

21632164
// helper function to get user email
2164-
func getUserEmail(user *v1Models.User, preferredEmail string) string {
2165+
func getUserEmail(user *v1Models.User, preferredEmail string, allowLFEmail bool) string {
2166+
if user == nil {
2167+
return ""
2168+
}
21652169
if preferredEmail != "" {
2166-
if utils.StringInSlice(preferredEmail, user.Emails) || user.LfEmail == strfmt.Email(preferredEmail) {
2170+
if utils.StringInSlice(preferredEmail, user.Emails) || (allowLFEmail && user.LfEmail == strfmt.Email(preferredEmail)) {
21672171
return preferredEmail
21682172
}
21692173
}
2170-
if user.LfEmail != "" {
2174+
if allowLFEmail && user.LfEmail != "" {
21712175
return string(user.LfEmail)
21722176
}
21732177
if len(user.Emails) > 0 {
@@ -2219,7 +2223,7 @@ func getActiveSignatureReturnURL(userID string, metadata map[string]interface{})
22192223

22202224
}
22212225

2222-
func (s *service) createDefaultIndividualValues(user *v1Models.User, preferredEmail string) map[string]interface{} {
2226+
func (s *service) createDefaultIndividualValues(user *v1Models.User, preferredEmail string, allowLFEmail bool) map[string]interface{} {
22232227
f := logrus.Fields{
22242228
"functionName": "sign.createDefaultIndiviualValues",
22252229
}
@@ -2234,10 +2238,8 @@ func (s *service) createDefaultIndividualValues(user *v1Models.User, preferredEm
22342238
}
22352239
}
22362240

2237-
if preferredEmail != "" {
2238-
if utils.StringInSlice(preferredEmail, user.Emails) || user.LfEmail == strfmt.Email(preferredEmail) {
2239-
defaultValues["email"] = preferredEmail
2240-
}
2241+
if userEmail := getUserEmail(user, preferredEmail, allowLFEmail); userEmail != "" {
2242+
defaultValues["email"] = userEmail
22412243
}
22422244

22432245
return defaultValues
@@ -2336,7 +2338,7 @@ func (s *service) RequestIndividualSignatureGerrit(ctx context.Context, input *m
23362338
preferredEmail = user.Emails[0]
23372339
}
23382340

2339-
defaultValues := s.createDefaultIndividualValues(user, preferredEmail)
2341+
defaultValues := s.createDefaultIndividualValues(user, preferredEmail, true)
23402342

23412343
log.WithFields(f).Debugf("defaultValues: %+v", defaultValues)
23422344

cla-backend/cla/models/docusign_models.py

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -177,18 +177,32 @@ def request_individual_signature(self, project_id, user_id, return_url=None, ret
177177
format(project))
178178

179179
cla.log.debug('Individual Signature - creating default individual values for user: {}'.format(user))
180-
default_cla_values = create_default_individual_values(user)
180+
provider_type = (return_url_type or '').lower()
181+
if provider_type not in ('github', 'gitlab'):
182+
provider_label = provider_type or 'missing'
183+
cla.log.warning('Individual Signature - unsupported provider_type "{}" for {}'.format(
184+
provider_label, request_info))
185+
return {'errors': {'return_url_type': 'unsupported provider_type: {}'.format(provider_label)}}
186+
default_cla_values = create_default_individual_values(user, preferred_email=preferred_email,
187+
provider_type=provider_type)
181188
cla.log.debug('Individual Signature - created default individual values: {}'.format(default_cla_values))
182189

190+
resolved_email = default_cla_values.get('email')
191+
if provider_type in ('github', 'gitlab'):
192+
resolved_email = resolved_email.strip() if isinstance(resolved_email, str) else None
193+
if not resolved_email:
194+
return {'errors': {'user_id': f'no {provider_type} user_emails found'}}
195+
default_cla_values['email'] = resolved_email
196+
183197
# Generate signature callback url
184198
cla.log.debug('Individual Signature - get active signature metadata')
185199
signature_metadata = cla.utils.get_active_signature_metadata(user_id)
186200
cla.log.debug('Individual Signature - get active signature metadata: {}'.format(signature_metadata))
187201

188202
cla.log.debug('Individual Signature - get individual signature callback url')
189-
if return_url_type.lower() == "github":
203+
if provider_type == "github":
190204
callback_url = cla.utils.get_individual_signature_callback_url(user_id, signature_metadata)
191-
elif return_url_type.lower() == "gitlab":
205+
elif provider_type == "gitlab":
192206
callback_url = cla.utils.get_individual_signature_callback_url_gitlab(user_id, signature_metadata)
193207

194208
cla.log.debug('Individual Signature - get individual signature callback url: {}'.format(callback_url))
@@ -261,12 +275,12 @@ def request_individual_signature(self, project_id, user_id, return_url=None, ret
261275
signature_return_url=return_url,
262276
signature_callback_url=callback_url)
263277
# Set signature ACL
264-
if return_url_type.lower() == "github":
278+
if provider_type == "github":
265279
acl = user.get_user_github_id()
266-
elif return_url_type.lower() == "gitlab":
280+
elif provider_type == "gitlab":
267281
acl = user.get_user_gitlab_id()
268-
cla.log.debug('Individual Signature - setting ACL using user {} id: {}'.format(return_url_type, acl))
269-
signature.set_signature_acl('{}:{}'.format(return_url_type.lower(),acl))
282+
cla.log.debug('Individual Signature - setting ACL using provider {} id: {}'.format(provider_type, acl))
283+
signature.set_signature_acl('{}:{}'.format(provider_type,acl))
270284

271285
# Populate sign url
272286
self.populate_sign_url(signature, callback_url, default_values=default_cla_values,
@@ -1346,14 +1360,16 @@ def populate_sign_url(self, signature, callback_url=None,
13461360
cla.log.debug(f'{fn} - {sig_type} - '
13471361
f'loading user by reference id: {signature.get_signature_reference_id()}')
13481362
user.load(signature.get_signature_reference_id())
1363+
provider_type = (signature.get_signature_return_url_type() or '').lower()
1364+
resolved_user_email = resolve_individual_user_email(user, provider_type, preferred_email)
13491365
cla.log.debug(f'{fn} - {sig_type} - loaded user by '
13501366
f'id: {user.get_user_id()}, '
13511367
f'name: {user.get_user_name()}, '
1352-
f'email: {user.get_user_email()}')
1368+
f'email: {resolved_user_email}')
13531369
if not user.get_user_name() is None:
13541370
user_signature_name = user.get_user_name()
1355-
if not user.get_user_email() is None:
1356-
user_signature_email = user.get_user_email()
1371+
if resolved_user_email is not None:
1372+
user_signature_email = resolved_user_email
13571373
except DoesNotExist:
13581374
cla.log.warning(f'{fn} - {sig_type} - no user associated with this signature '
13591375
f'id: {signature.get_signature_reference_id()} - can not sign ICLA')
@@ -2406,7 +2422,32 @@ def create_default_company_values(company: Company,
24062422
return values
24072423

24082424

2409-
def create_default_individual_values(user: User, preferred_email: str = None) -> Dict[str, Any]:
2425+
def resolve_individual_user_email(user: User, provider_type: str = None,
2426+
preferred_email: str = None) -> Optional[str]:
2427+
if user is None:
2428+
return None
2429+
2430+
provider_type = (provider_type or '').lower()
2431+
user_emails = user.get_user_emails() or set()
2432+
2433+
if preferred_email and preferred_email in user_emails:
2434+
return preferred_email
2435+
2436+
if provider_type not in ('github', 'gitlab'):
2437+
lf_email = user.get_lf_email()
2438+
if preferred_email and lf_email is not None and preferred_email == lf_email:
2439+
return preferred_email
2440+
if lf_email is not None:
2441+
return lf_email
2442+
2443+
if len(user_emails) > 0:
2444+
return next(iter(user_emails), None)
2445+
2446+
return None
2447+
2448+
2449+
def create_default_individual_values(user: User, preferred_email: str = None,
2450+
provider_type: str = None) -> Dict[str, Any]:
24102451
values = {}
24112452

24122453
if user is None:
@@ -2416,8 +2457,9 @@ def create_default_individual_values(user: User, preferred_email: str = None) ->
24162457
values['full_name'] = user.get_user_name()
24172458
values['public_name'] = user.get_user_name()
24182459

2419-
if user.get_user_email(preferred_email=preferred_email) is not None:
2420-
values['email'] = user.get_user_email()
2460+
resolved_email = resolve_individual_user_email(user, provider_type, preferred_email)
2461+
if resolved_email is not None:
2462+
values['email'] = resolved_email
24212463

24222464
return values
24232465

cla-backend/cla/tests/unit/test_docusign_models.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from cla.models.docusign_models import (ClaSignatoryEmailParams,
77
cla_signatory_email_content,
88
create_default_company_values,
9+
create_default_individual_values,
910
document_signed_email_content,
1011
populate_signature_from_ccla_callback,
1112
populate_signature_from_icla_callback)
@@ -802,3 +803,25 @@ def test_cla_signatory_email_content():
802803
assert "<p>After you sign, john (as the initial CLA Manager for your company)" in email_body
803804
assert "and if you approve john as your initial CLA Manager" in email_body
804805
assert "contact the requester at john@example.com" in email_body
806+
807+
808+
def test_create_default_individual_values_github_ignores_lf_email():
809+
user = User()
810+
user.set_user_name('Example User')
811+
user.set_user_emails(['git@example.com'])
812+
user.set_lf_email('lf@example.com')
813+
814+
values = create_default_individual_values(user, preferred_email='lf@example.com', provider_type='github')
815+
816+
assert values['email'] == 'git@example.com'
817+
818+
819+
def test_create_default_individual_values_gerrit_allows_lf_email():
820+
user = User()
821+
user.set_user_name('Example User')
822+
user.set_user_emails(['git@example.com'])
823+
user.set_lf_email('lf@example.com')
824+
825+
values = create_default_individual_values(user, provider_type='gerrit')
826+
827+
assert values['email'] == 'lf@example.com'

tests/functional/cypress/e2e/v4/github-repositories.cy.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,30 @@ describe('To Validate github-repositories API call', function () {
5757
}
5858
});
5959

60+
// Shared DEV fixture repair:
61+
// this repository is sometimes left disabled by earlier/parallel E2E runs.
62+
// Re-enable it before the happy-path tests start, but do not assert on the
63+
// POST body because the API returns { list: null } when the repo is already
64+
// enabled and the add operation becomes a no-op.
65+
before(() => {
66+
cy.request({
67+
method: 'POST',
68+
url: `${claEndpoint}`,
69+
timeout: timeout,
70+
failOnStatusCode: allowFail,
71+
headers: getXACLHeader(),
72+
auth: { bearer: bearerToken },
73+
body: {
74+
cla_group_id: '1baf67ab-d894-4edf-b6fc-c5f939db59f7',
75+
github_organization_name: 'ApiAutomStandaloneOrg',
76+
repository_github_id: '507892593',
77+
repository_github_ids: ['507892593'],
78+
},
79+
}).then((response) => {
80+
validate_200_Status(response);
81+
});
82+
});
83+
6084
it('Get the GitHub repositories of the project which are CLA Enforced- Record should return 200 Response', function () {
6185
cy.task('log', `--> GET ${claEndpoint}`);
6286

tests/functional/package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/functional/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
"@types/node": "^20.5.0",
2727
"cypress": "^12.17.3",
2828
"cypress-dotenv": "^3.0.1",
29-
"typescript": "^5.1.6"
29+
"typescript": "^5.1.6",
30+
"dotenv": "^16.4.5"
3031
},
3132
"dependencies": {
3233
"ajv": "^8.18.0",

tests/functional/yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,11 @@ dotenv-parse-variables@^2.0.0:
582582
debug "^4.3.1"
583583
is-string-and-not-blank "^0.0.2"
584584

585+
dotenv@^16.4.5:
586+
version "16.6.1"
587+
resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz"
588+
integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==
589+
585590
dunder-proto@^1.0.1:
586591
version "1.0.1"
587592
resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz"

0 commit comments

Comments
 (0)