Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions cla-backend-go/swagger/cla.v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2445,6 +2445,49 @@ paths:
tags:
- signatures

/user/{userID}/active-signature:
parameters:
- $ref: "#/parameters/x-request-id"
- name: userID
description: the user ID
in: path
type: string
required: true
pattern: '^[a-fA-F0-9]{8}-?[a-fA-F0-9]{4}-?4[a-fA-F0-9]{3}-?[89ab][a-fA-F0-9]{3}-?[a-fA-F0-9]{12}$' # uuidv4
get:
summary: |
Returns all metadata associated with a user's active signature.
{
'user_id': <user-uuid>,
'project_id': <project-uuid>,
'repository_id': <repository-id> (as string),
'pull_request_id': <PR> (PR number as string),
*'merge_request_id': <MR> (optional MR number as string - this property can be missing in JSON),
'return_url': <url-where-user-initiated-signature-from> (for example itHub PR path)'
}
Returns null if the user does not have an active signature.
security: [ ]
operationId: getUserActiveSignature
responses:
'200':
description: 'Success'
headers:
x-request-id:
type: string
description: The unique request ID value - assigned/set by the API Gateway based on the session
schema:
$ref: '#/definitions/user-active-signature'
'400':
$ref: '#/responses/invalid-request'
'401':
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
'404':
$ref: '#/responses/not-found'
tags:
- sign

/signatures/project/{projectSFID}/company/{companyID}/employee:
get:
summary: Get project company signatures for the employees
Expand Down Expand Up @@ -5006,6 +5049,9 @@ definitions:
user:
$ref: './common/user.yaml'

user-active-signature:
$ref: './common/user-active-signature.yaml'

signatures:
$ref: './common/signatures.yaml'

Expand Down
34 changes: 34 additions & 0 deletions cla-backend-go/swagger/common/user-active-signature.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright The Linux Foundation and each contributor to CommunityBridge.
# SPDX-License-Identifier: MIT

type: object
x-nullable: true
title: User Active Signature
description: >
Returns all metadata associated with a user's active signature.
Returns `null` if the user does not have an active signature.
properties:
user_id:
$ref: './common/properties/internal-id.yaml'
description: The unique internal UUID of the user
project_id:
$ref: './common/properties/internal-id.yaml'
description: The unique UUID of the associated project
repository_id:
type: string
description: The unique ID of the associated repository (number stored as string)
example: '168926425'
pull_request_id:
type: string
description: The pull request ID related to the signature (number stored as string)
example: '456'
merge_request_id:
type: string
description: The merge request ID related to the signature (optional number stored as string, thsi property can be missing in JSON)
example: '456'
x-nullable: true
return_url:
type: string
format: uri
description: The return URL where the user initiated the signature (for example GitHub PR path)
example: https://github.com/veer-missingid2/repo03/pull/3
31 changes: 31 additions & 0 deletions cla-backend-go/v2/sign/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
"github.com/linuxfoundation/easycla/cla-backend-go/gen/v2/restapi/operations/sign"
"github.com/linuxfoundation/easycla/cla-backend-go/utils"
"github.com/linuxfoundation/easycla/cla-backend-go/v2/organization-service/client/organizations"

"github.com/go-openapi/runtime"
)

var (
Expand Down Expand Up @@ -248,6 +250,35 @@ func Configure(api *operations.EasyclaAPI, service Service, userService users.Se
return sign.NewCclaCallbackOK()
})

api.SignGetUserActiveSignatureHandler = sign.GetUserActiveSignatureHandlerFunc(
func(params sign.GetUserActiveSignatureParams) middleware.Responder {
reqId := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTIDKey, reqId)
f := logrus.Fields{
"functionName": "v2.sign.handlers.SignGetUserActiveSignatureHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"userID": params.UserID,
}
var resp *models.UserActiveSignature
var err error

log.WithFields(f).Debug("getting user active signature")
resp, err = service.GetUserActiveSignature(ctx, params.UserID)
if err != nil {
return sign.NewGetUserActiveSignatureBadRequest().WithPayload(errorResponse(reqId, err))
}
if resp == nil {
return middleware.ResponderFunc(func(w http.ResponseWriter, _ runtime.Producer) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
if _, err := w.Write([]byte("null")); err != nil {
log.WithFields(f).WithError(err).Warn("failed to write null response")
}
})
}
return sign.NewGetUserActiveSignatureOK().WithPayload(resp)
})

}

type codedResponse interface {
Expand Down
147 changes: 139 additions & 8 deletions cla-backend-go/v2/sign/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type Service interface {
SignedIndividualCallbackGitlab(ctx context.Context, payload []byte, userID, organizationID, repositoryID, mergeRequestID string) error
SignedIndividualCallbackGerrit(ctx context.Context, payload []byte, userID string) error
SignedCorporateCallback(ctx context.Context, payload []byte, companyID, projectID string) error
GetUserActiveSignature(ctx context.Context, userID string) (*models.UserActiveSignature, error)
}

// service
Expand Down Expand Up @@ -1539,7 +1540,7 @@ func (s *service) getIndividualSignatureCallbackURLGitlab(ctx context.Context, u
if found, ok := metadata["merge_request_id"].(string); ok {
mergeRequestID = found
} else {
log.WithFields(f).WithError(err).Warnf("unable to get pull request ID for user: %s", userID)
log.WithFields(f).WithError(err).Warnf("unable to get merge request ID for user: %s", userID)
return "", err
}

Expand Down Expand Up @@ -1607,11 +1608,28 @@ func (s *service) getIndividualSignatureCallbackURL(ctx context.Context, userID
log.WithFields(f).Debugf("found pull request ID: %s", pullRequestID)

// Get installation ID through a helper function
log.WithFields(f).Debugf("getting repository...")
installationId, err = s.getInstallationIDFromRepositoryID(ctx, repositoryID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("unable to get github organization for repository ID: %s", repositoryID)
return "", err
}

callbackURL := fmt.Sprintf("%s/v4/signed/individual/%d/%s/%s", s.ClaV4ApiURL, installationId, repositoryID, pullRequestID)
return callbackURL, nil
}

func (s *service) getInstallationIDFromRepositoryID(ctx context.Context, repositoryID string) (int64, error) {
var installationId int64
f := logrus.Fields{
"functionName": "sign.getInstallationIDFromRepositoryID",
"repositoryID": repositoryID,
}
// Get installation ID through a helper function
log.WithFields(f).Debugf("getting repository for ID=%s...", repositoryID)
githubRepository, err := s.repositoryService.GetRepositoryByExternalID(ctx, repositoryID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("unable to get installation ID for repository ID: %s", repositoryID)
return "", err
return 0, err
}

// Get github organization
Expand All @@ -1620,17 +1638,16 @@ func (s *service) getIndividualSignatureCallbackURL(ctx context.Context, userID

if err != nil {
log.WithFields(f).WithError(err).Warnf("unable to get github organization for repository ID: %s", repositoryID)
return "", err
return 0, err
}

installationId = githubOrg.OrganizationInstallationID
if installationId == 0 {
log.WithFields(f).WithError(err).Warnf("unable to get installation ID for repository ID: %s", repositoryID)
return "", err
log.WithFields(f).Warnf("unable to get installation ID for repository ID: %s", repositoryID)
return 0, err
}

callbackURL := fmt.Sprintf("%s/v4/signed/individual/%d/%s/%s", s.ClaV4ApiURL, installationId, repositoryID, pullRequestID)
return callbackURL, nil
return installationId, nil
}

//nolint:gocyclo
Expand Down Expand Up @@ -2769,3 +2786,117 @@ func claSignatoryEmailContent(params ClaSignatoryEmailParams) (string, string) {

return emailSubject, emailBody
}

func (s *service) getActiveSignatureReturnURL(ctx context.Context, userID string, metadata map[string]interface{}) (string, error) {

f := logrus.Fields{
"functionName": "sign.getActiveSignatureReturnURL",
}

var returnURL, rId string
var err, err2 error
var pullRequestID int
var repositoryID int64
var installationID int64

if found, ok := metadata["pull_request_id"]; ok && found != nil {
prId := fmt.Sprintf("%v", found)
pullRequestID, err2 = strconv.Atoi(prId)
if err2 != nil {
log.WithFields(f).WithError(err2).Warnf("unable to get pull request ID for user: %s", userID)
return "", err2
}
} else {
err2 = errors.New("missing pull_request_id in metadata")
log.WithFields(f).WithError(err2).Warnf("unable to get pull request ID for user: %s", userID)
return "", err2
}

if found, ok := metadata["repository_id"]; ok && found != nil {
rId = fmt.Sprintf("%v", found)
repositoryID, err2 = strconv.ParseInt(rId, 10, 64)
if err2 != nil {
log.WithFields(f).WithError(err2).Warnf("unable to get repository ID for user: %s", userID)
return "", err2
}
} else {
err2 = errors.New("missing repository_id in metadata")
log.WithFields(f).WithError(err2).Warnf("unable to get repository ID for user: %s", userID)
return "", err2
}

// Get installation ID through a helper function
installationID, err = s.getInstallationIDFromRepositoryID(ctx, rId)
if err != nil {
log.WithFields(f).WithError(err).Warnf("unable to get github organization for repository ID: %v", repositoryID)
return "", err
}

returnURL, err = github.GetReturnURL(ctx, installationID, repositoryID, pullRequestID)

if err != nil {
return "", err
}

return returnURL, nil
}

func (s *service) GetUserActiveSignature(ctx context.Context, userID string) (*models.UserActiveSignature, error) {
f := logrus.Fields{
"functionName": "sign.GetUserActiveSignature",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"userID": userID,
}
activeSignatureMetadata, err := s.storeRepository.GetActiveSignatureMetaData(ctx, userID)
if err != nil {
log.WithFields(f).WithError(err).Warnf("unable to get active signature meta data for user: %s", userID)
return nil, err
}
log.WithFields(f).Debugf("active signature metadata: %+v", activeSignatureMetadata)
if len(activeSignatureMetadata) == 0 {
return nil, nil
}
var (
mergeRequestId *string
isGitlab bool
returnURL string
pullRequestId string
repositoryId string
)
if mrId, ok := activeSignatureMetadata["merge_request_id"]; ok && mrId != nil {
mrStr := fmt.Sprintf("%v", mrId)
mergeRequestId = &mrStr
isGitlab = true
}
if isGitlab {
var ok bool
returnURL, ok = activeSignatureMetadata["return_url"].(string)
if !ok {
log.WithFields(f).Warnf("missing return_url in metadata while merge_request_id is present: %+v", activeSignatureMetadata)
}
} else {
returnURL, err = s.getActiveSignatureReturnURL(ctx, userID, activeSignatureMetadata)
if err != nil {
log.WithFields(f).WithError(err).Warnf("unable to get active signature return url for user: %s", userID)
return nil, err
}
}
projectId, ok := activeSignatureMetadata["project_id"].(string)
if !ok {
log.WithFields(f).Warnf("missing project_id in metadata: %+v", activeSignatureMetadata)
}
if val, ok := activeSignatureMetadata["pull_request_id"]; ok && val != nil {
pullRequestId = fmt.Sprintf("%v", val)
}
if val, ok := activeSignatureMetadata["repository_id"]; ok && val != nil {
repositoryId = fmt.Sprintf("%v", val)
}
return &models.UserActiveSignature{
MergeRequestID: mergeRequestId,
ProjectID: projectId,
PullRequestID: pullRequestId,
RepositoryID: repositoryId,
ReturnURL: strfmt.URI(returnURL),
UserID: userID,
}, nil
}
3 changes: 2 additions & 1 deletion cla-backend/cla/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ def request_company_ccla(
# return cla.controllers.user.request_company_admin_access(str(user_id), str(company_id))


# LG: This is ported to golang and no longer used in dev (still used in prod)
@hug.get("/user/{user_id}/active-signature", versions=2)
def get_user_active_signature(user_id: hug.types.uuid):
"""
Expand Down Expand Up @@ -773,7 +774,7 @@ def get_projects(auth_user: check_auth):
del project["project_external_id"]
return projects


# LG: This is ported to golang and no longer used in dev (still used in prod).
@hug.get("/project/{project_id}", versions=2)
def get_project(project_id: hug.types.uuid):
"""
Expand Down
3 changes: 3 additions & 0 deletions tests/py2go/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@
- Manually via `cURL`: `` curl -s -XGET http://127.0.0.1:5001/v4/project-compat/01af041c-fa69-4052-a23c-fb8c1d3bef24 | jq . ``.
- To manually see given project values if APIs differ (to dewbug): `` aws --region us-east-1 --profile lfproduct-dev dynamodb get-item --table-name cla-dev-projects --key '{"project_id": {"S": "4a855799-0aea-4e01-98b7-ef3da09df478"}}' | jq '.Item' ``.
- And `` aws --region us-east-1 --profile lfproduct-dev dynamodb query --table-name cla-dev-projects-cla-groups --index-name cla-group-id-index --key-condition-expression "cla_group_id = :project_id" --expression-attribute-values '{":project_id":{"S":"4a855799-0aea-4e01-98b7-ef3da09df478"}}' | jq '.Items' ``.
- `` DEBUG='' USER_UUID=b817eb57-045a-4fe0-8473-fbb416a01d70 PY_API_URL=https://api.lfcla.dev.platform.linuxfoundation.org go test -v -run '^TestUserActiveSignatureAPI$' ``.
- `` REPO_ID=466156917 PR_ID=3 DEBUG=1 go test -v -run '^TestUserActiveSignatureAPI$' ``.
- `` MAX_PARALLEL=2 DEBUG='' go test -v -run '^TestAllUserActiveSignatureAPI$' ``.
Loading
Loading