Skip to content

Commit 1babe21

Browse files
Faet/Fix #4973, #4974, #4975
Signed-off-by: Lukasz Gryglicki <lgryglicki@cncf.io> Assisted by [OpenAI](https://platform.openai.com/) Assisted by [GitHub Copilot](https://github.com/features/copilot)
1 parent 04dccf9 commit 1babe21

21 files changed

Lines changed: 429 additions & 92 deletions

File tree

.github/workflows/cypress-functional-pr.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ on:
1111
permissions:
1212
contents: read
1313

14+
concurrency:
15+
group: easycla-shared-dev-cypress-functional
16+
cancel-in-progress: false
17+
1418
jobs:
1519
cypress-functional:
1620
if: ${{ github.event.pull_request.head.repo.fork == false }}
@@ -65,6 +69,7 @@ jobs:
6569
APP_URL=https://api-gw.dev.platform.linuxfoundation.org/
6670
AUTH0_TOKEN_API=https://linuxfoundation-dev.auth0.com/oauth/token
6771
CYPRESS_ENV=dev
72+
CYPRESS_E2E_RUN_ID=${{ github.run_id }}-${{ github.run_attempt }}
6873
6974
AUTH0_USER_NAME=${{ secrets.AUTH0_USER_NAME }}
7075
AUTH0_PASSWORD=${{ secrets.AUTH0_PASSWORD }}

cla-backend-go/auth/authorizer.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ func (a Authorizer) SecurityAuth(token string, scopes []string) (*user.CLAUser,
134134
}
135135
return nil, err
136136
}
137+
138+
if name != "" {
139+
lfuser.Name = name
140+
}
137141
//log.WithFields(f).Debugf("user loaded : %+v with scopes : %+v", lfuser, scopes)
138142

139143
for _, scope := range scopes {

cla-backend-go/cmd/server.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,27 @@ func responseLoggingMiddleware(next http.Handler) http.Handler {
746746
})
747747
}
748748

749+
func refreshStoredUserName(usersService users.Service, userModel *models.User, claUser *user.CLAUser) *models.User {
750+
if userModel == nil || claUser == nil || claUser.Name == "" || userModel.Username == claUser.Name {
751+
return userModel
752+
}
753+
754+
updatedUser, err := usersService.UpdateUser(userModel.UserID, map[string]interface{}{
755+
"user_name": claUser.Name,
756+
"date_modified": time.Now().UTC().Format(time.RFC3339),
757+
})
758+
if err != nil {
759+
log.WithFields(logrus.Fields{
760+
"functionName": "cmd.refreshStoredUserName",
761+
"userID": userModel.UserID,
762+
"lfUsername": claUser.LFUsername,
763+
}).WithError(err).Warn("unable to refresh stored user_name from current identity claims")
764+
return userModel
765+
}
766+
767+
return updatedUser
768+
}
769+
749770
// create user form http authorization token
750771
// this function creates user if user does not exist and token is valid
751772
func createUserFromRequest(authorizer auth.Authorizer, usersService users.Service, eventsService events.Service, r *http.Request) *http.Request {
@@ -788,8 +809,9 @@ func createUserFromRequest(authorizer auth.Authorizer, usersService users.Servic
788809
return r
789810
}
790811
}
791-
// If found - just return
812+
// If found - refresh the stored display name and return
792813
if userModel != nil {
814+
userModel = refreshStoredUserName(usersService, userModel, claUser)
793815
if !needToStoreUser {
794816
return r
795817
}
@@ -807,8 +829,9 @@ func createUserFromRequest(authorizer auth.Authorizer, usersService users.Servic
807829
return r
808830
}
809831
}
810-
// If found - just return
832+
// If found - refresh the stored display name and return
811833
if userModel != nil {
834+
userModel = refreshStoredUserName(usersService, userModel, claUser)
812835
if !needToStoreUser {
813836
return r
814837
}

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,28 @@ func (s *Service) InitiateSignRequest(ctx context.Context, req *http.Request, gi
888888
return &consoleURL, nil
889889
}
890890

891+
func (s *Service) refreshGitLabUserName(ctx context.Context, claUser *models.User, gitlabUser *goGitLab.User) *models.User {
892+
if claUser == nil || gitlabUser == nil || gitlabUser.Name == "" || claUser.Username == gitlabUser.Name {
893+
return claUser
894+
}
895+
896+
updatedUser, err := s.userService.UpdateUser(claUser.UserID, map[string]interface{}{
897+
"user_name": gitlabUser.Name,
898+
"date_modified": time.Now().UTC().Format(time.RFC3339),
899+
})
900+
if err != nil {
901+
log.WithFields(logrus.Fields{
902+
"functionName": "v2.gitlab_organizations.service.refreshGitLabUserName",
903+
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
904+
"userID": claUser.UserID,
905+
"gitlabUserID": gitlabUser.ID,
906+
}).WithError(err).Warn("unable to refresh stored GitLab user_name")
907+
return claUser
908+
}
909+
910+
return updatedUser
911+
}
912+
891913
func (s *Service) getOrCreateUser(ctx context.Context, gitlabClient *goGitLab.Client, eventsService events.Service) (*models.User, error) {
892914

893915
f := logrus.Fields{
@@ -927,7 +949,7 @@ func (s *Service) getOrCreateUser(ctx context.Context, gitlabClient *goGitLab.Cl
927949
})
928950
return claUser, nil
929951
}
930-
return claUser, nil
952+
return s.refreshGitLabUserName(ctx, claUser, gitlabUser), nil
931953
}
932954

933955
func buildInstallationURL(gitlabOrgID string, authStateNonce string) *strfmt.URI {

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

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,28 @@ func (s service) InitiateSignRequest(ctx context.Context, req *http.Request, git
181181
return &consoleURL, nil
182182
}
183183

184+
func (s service) refreshGitLabUserName(ctx context.Context, claUser *models.User, gitlabUser *gitlab.User) *models.User {
185+
if claUser == nil || gitlabUser == nil || gitlabUser.Name == "" || claUser.Username == gitlabUser.Name {
186+
return claUser
187+
}
188+
189+
updatedUser, err := s.userService.UpdateUser(claUser.UserID, map[string]interface{}{
190+
"user_name": gitlabUser.Name,
191+
"date_modified": time.Now().UTC().Format(time.RFC3339),
192+
})
193+
if err != nil {
194+
log.WithFields(logrus.Fields{
195+
"functionName": "v2.gitlab_sign.service.refreshGitLabUserName",
196+
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
197+
"userID": claUser.UserID,
198+
"gitlabUserID": gitlabUser.ID,
199+
}).WithError(err).Warn("unable to refresh stored GitLab user_name")
200+
return claUser
201+
}
202+
203+
return updatedUser
204+
}
205+
184206
func (s service) getOrCreateUser(ctx context.Context, gitlabClient *gitlab.Client, eventsService events.Service) (*models.User, error) {
185207
f := logrus.Fields{
186208
"functionName": "v2.gitlab_sign.service.getOrCreateUser",
@@ -197,23 +219,23 @@ func (s service) getOrCreateUser(ctx context.Context, gitlabClient *gitlab.Clien
197219
claUser, err := s.userService.GetUserByGitlabID(gitlabUser.ID)
198220
if err == nil && claUser != nil {
199221
log.WithFields(f).Debugf("found user by GitLab ID: %d", gitlabUser.ID)
200-
return claUser, nil
222+
return s.refreshGitLabUserName(ctx, claUser, gitlabUser), nil
201223
}
202-
log.WithFields(f).Debugf("unable to lookup user by github ID: %d, error: %+v ", gitlabUser.ID, err)
224+
log.WithFields(f).Debugf("unable to lookup user by GitLab ID: %d, error: %+v ", gitlabUser.ID, err)
203225

204226
log.WithFields(f).Debugf("looking up user by GitLab username: %s", gitlabUser.Username)
205227
claUser, err = s.userService.GetUserByGitLabUsername(gitlabUser.Username)
206228
if err == nil && claUser != nil {
207229
log.WithFields(f).Debugf("found user by GitLab username: %s", gitlabUser.Username)
208-
return claUser, nil
230+
return s.refreshGitLabUserName(ctx, claUser, gitlabUser), nil
209231
}
210-
log.WithFields(f).Debugf("unable to lookup user by github username: %s, error: %+v ", gitlabUser.Username, err)
232+
log.WithFields(f).Debugf("unable to lookup user by GitLab username: %s, error: %+v ", gitlabUser.Username, err)
211233

212234
log.WithFields(f).Debugf("looking up user by GitLab email: %s", gitlabUser.Email)
213235
claUser, err = s.userService.GetUserByEmail(gitlabUser.Email)
214236
if err == nil && claUser != nil {
215237
log.WithFields(f).Debugf("found user by GitLab email: %s", gitlabUser.Email)
216-
return claUser, nil
238+
return s.refreshGitLabUserName(ctx, claUser, gitlabUser), nil
217239
}
218240

219241
log.WithFields(f).Infof("unable to locate GitLab user - creating a new user record for GitLab user : %+v ", gitlabUser)
@@ -225,8 +247,8 @@ func (s service) getOrCreateUser(ctx context.Context, gitlabClient *gitlab.Clien
225247
Username: gitlabUser.Name,
226248
}
227249
claUser, userErr := s.userService.CreateUser(user, nil)
228-
if err != nil {
229-
log.WithFields(f).Debugf("unable to create claUser with details : %+v, error: %+v", user, userErr)
250+
if userErr != nil {
251+
log.WithFields(f).Debugf("unable to create claUser with details: %+v, error: %+v", user, userErr)
230252
return nil, userErr
231253
}
232254

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/linuxfoundation/easycla/cla-backend-go/gen/v2/restapi/operations/sign"
2525
"github.com/linuxfoundation/easycla/cla-backend-go/utils"
2626
"github.com/linuxfoundation/easycla/cla-backend-go/v2/organization-service/client/organizations"
27+
user_service "github.com/linuxfoundation/easycla/cla-backend-go/v2/user-service"
2728

2829
"github.com/go-openapi/runtime"
2930
)
@@ -77,6 +78,8 @@ func CCLADocusignMiddleware(next http.Handler) http.Handler {
7778
}
7879

7980
// Configure API call
81+
//
82+
//nolint:gocyclo
8083
func Configure(api *operations.EasyclaAPI, service Service, userService users.Service) {
8184
// Retrieve a list of available templates
8285
api.SignClearCachesHandler = sign.ClearCachesHandlerFunc(
@@ -180,12 +183,36 @@ func Configure(api *operations.EasyclaAPI, service Service, userService users.Se
180183
if userErr != nil {
181184
return sign.NewRequestIndividualSignatureBadRequest().WithPayload(errorResponse(reqId, userErr))
182185
}
183-
if len(user.Emails) == 0 {
186+
if user == nil {
187+
msg := fmt.Sprintf("user not found: %s", *params.Input.UserID)
188+
log.WithFields(f).Warn(msg)
189+
return sign.NewRequestIndividualSignatureBadRequest().WithPayload(errorResponse(reqId, errors.New(msg)))
190+
}
191+
userServiceClient := user_service.GetClient()
192+
if userServiceClient != nil && userServiceClient.IsConfigured() && user.LfUsername != "" {
193+
platformUser, platformUserErr := userServiceClient.GetUserByUsername(user.LfUsername)
194+
if platformUserErr != nil {
195+
log.WithFields(f).WithError(platformUserErr).Warn("unable to fetch platform user for primary email lookup")
196+
} else if platformUser != nil {
197+
for _, email := range platformUser.Emails {
198+
if email != nil && email.IsPrimary != nil && *email.IsPrimary && email.EmailAddress != nil && *email.EmailAddress != "" {
199+
preferredEmail = *email.EmailAddress
200+
break
201+
}
202+
}
203+
}
204+
}
205+
206+
if preferredEmail == "" && len(user.Emails) > 0 {
207+
preferredEmail = user.Emails[0]
208+
}
209+
210+
if preferredEmail == "" {
184211
msg := "no emails found"
185212
log.WithFields(f).Warn(msg)
186213
return sign.NewRequestIndividualSignatureBadRequest().WithPayload(errorResponse(reqId, errors.New(msg)))
187214
}
188-
preferredEmail = user.Emails[0]
215+
189216
log.WithFields(f).Debug("requesting individual signature for github/gitlab")
190217
resp, err = service.RequestIndividualSignature(ctx, params.Input, preferredEmail)
191218
} else if strings.ToLower(params.Input.ReturnURLType) == "gerrit" {

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2170,6 +2170,12 @@ func getUserEmail(user *v1Models.User, preferredEmail string, allowLFEmail bool)
21702170
if utils.StringInSlice(preferredEmail, user.Emails) || (allowLFEmail && user.LfEmail == strfmt.Email(preferredEmail)) {
21712171
return preferredEmail
21722172
}
2173+
// For GitHub/GitLab individual-signature flows, preferredEmail comes
2174+
// from upstream identity data and may not exist in the locally cached
2175+
// EasyCLA emails yet. In those flows allowLFEmail is false.
2176+
if !allowLFEmail {
2177+
return preferredEmail
2178+
}
21732179
}
21742180
if allowLFEmail && user.LfEmail != "" {
21752181
return string(user.LfEmail)

cla-backend-go/v2/user-service/client.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ func GetClient() *Client {
6161
return userServiceClient
6262
}
6363

64+
// IsConfigured reports whether the user-service client has a usable API gateway URL.
65+
func (usc *Client) IsConfigured() bool {
66+
return usc != nil && strings.TrimSpace(usc.apiGwURL) != ""
67+
}
68+
6469
// GetUsersByUsernames search users by lf username
6570
func (usc *Client) GetUsersByUsernames(lfUsernames []string) ([]*models.User, error) {
6671
f := logrus.Fields{

cla-backend/cla/controllers/signing.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,42 @@ def request_individual_signature(project_id, user_id, return_url_type, return_ur
4242
github = get_repository_service("github")
4343
primary_user_email = github.get_primary_user_email(request)
4444
elif return_url_type.lower() == "gitlab":
45+
primary_user_email = None
46+
4547
try:
4648
cla.log.debug(f"Fetching user details for: {user_id}")
4749
user = User()
4850
user.load(user_id)
4951
except DoesNotExist as err:
5052
cla.log.warning('Individual Signature - user ID was NOT found for: {}'.format(user_id))
5153
return {'errors': {'user_id': str(err)}}
52-
primary_user_email = user.get_user_email()
54+
55+
lf_username = user.get_lf_username()
56+
if lf_username:
57+
try:
58+
platform_users = UserService.get_users_by_username(lf_username) or []
59+
except Exception as err:
60+
cla.log.warning(
61+
f'Individual Signature - unable to fetch platform user by username: {lf_username}, error: {err}'
62+
)
63+
platform_users = []
64+
65+
for platform_user in platform_users:
66+
if not platform_user:
67+
continue
68+
for email in (platform_user.get('Emails') or []):
69+
if email and email.get('IsPrimary') and email.get('EmailAddress'):
70+
primary_user_email = email.get('EmailAddress')
71+
break
72+
if primary_user_email:
73+
break
74+
75+
if not primary_user_email:
76+
primary_user_email = next(iter(user.get_user_emails() or []), None)
77+
5378
return signing_service.request_individual_signature(str(project_id), str(user_id), return_url, return_url_type,
5479
preferred_email=primary_user_email)
5580

56-
5781
def request_corporate_signature(auth_user,
5882
project_id: str,
5983
company_id: str,

cla-backend/cla/controllers/user.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,5 +512,10 @@ def get_or_create_user(auth_user):
512512

513513
return user
514514

515-
# Just return the first matching record
516-
return users[0]
515+
# Just return the first matching record, but refresh the display name when it changed upstream.
516+
user = users[0]
517+
user_name = auth_user.name.strip() if auth_user.name else None
518+
if user_name and user.get_user_name() != user_name:
519+
user.set_user_name(user_name)
520+
user.save()
521+
return user

0 commit comments

Comments
 (0)