Skip to content

Commit

Permalink
[#4357,#4348] Bug/LF Users
Browse files Browse the repository at this point in the history
- Handled exception case for users that dont exist
- Updated response with ccla,icla,ccla_requires_icla fields

Signed-off-by: Harold Wanyama <hwanyama@contractor.linuxfoundation.org>
nickmango committed Jun 18, 2024
1 parent e23761e commit 4876d47
Showing 9 changed files with 1,673 additions and 18 deletions.
346 changes: 346 additions & 0 deletions cla-backend-go/company/mocks/mock_service.go

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions cla-backend-go/company/service.go
Original file line number Diff line number Diff line change
@@ -62,10 +62,10 @@ type IService interface { // nolint
// calls org service
SearchOrganizationByName(ctx context.Context, orgName string, websiteName string, includeSigningEntityName bool, filter string) (*models.OrgList, error)

sendRequestAccessEmail(ctx context.Context, companyModel *models.Company, requesterName, requesterEmail, recipientName, recipientAddress string)
sendRequestApprovedEmailToRecipient(ctx context.Context, companyModel *models.Company, recipientName, recipientAddress string)
sendRequestRejectedEmailToRecipient(ctx context.Context, companyModel *models.Company, recipientName, recipientAddress string)
getPreferredNameAndEmail(ctx context.Context, lfid string) (string, string, error)
// sendRequestAccessEmail(ctx context.Context, companyModel *models.Company, requesterName, requesterEmail, recipientName, recipientAddress string)
// sendRequestApprovedEmailToRecipient(ctx context.Context, companyModel *models.Company, recipientName, recipientAddress string)
// sendRequestRejectedEmailToRecipient(ctx context.Context, companyModel *models.Company, recipientName, recipientAddress string)
// getPreferredNameAndEmail(ctx context.Context, lfid string) (string, string, error)
}

// NewService creates a new company service object
249 changes: 249 additions & 0 deletions cla-backend-go/project/mocks/mock_service.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions cla-backend-go/signatures/service.go
Original file line number Diff line number Diff line change
@@ -72,10 +72,10 @@ type SignatureService interface {
GetClaGroupCCLASignatures(ctx context.Context, claGroupID string, approved, signed *bool) (*models.Signatures, error)
GetClaGroupCorporateContributors(ctx context.Context, claGroupID string, companyID *string, pageSize *int64, nextKey *string, searchTerm *string) (*models.CorporateContributorList, error)

createOrGetEmployeeModels(ctx context.Context, claGroupModel *models.ClaGroup, companyModel *models.Company, corporateSignatureModel *models.Signature) ([]*models.User, error)
// createOrGetEmployeeModels(ctx context.Context, claGroupModel *models.ClaGroup, companyModel *models.Company, corporateSignatureModel *models.Signature) ([]*models.User, error)
CreateOrUpdateEmployeeSignature(ctx context.Context, claGroupModel *models.ClaGroup, companyModel *models.Company, corporateSignatureModel *models.Signature) ([]*models.User, error)
UpdateEnvelopeDetails(ctx context.Context, signatureID, envelopeID string, signURL *string) (*models.Signature, error)
handleGitHubStatusUpdate(ctx context.Context, employeeUserModel *models.User) error
// handleGitHubStatusUpdate(ctx context.Context, employeeUserModel *models.User) error
ProcessEmployeeSignature(ctx context.Context, companyModel *models.Company, claGroupModel *models.ClaGroup, user *models.User) (*bool, error)
}

17 changes: 17 additions & 0 deletions cla-backend-go/swagger/cla.v2.yaml
Original file line number Diff line number Diff line change
@@ -6176,5 +6176,22 @@ definitions:
companyID:
type: string
description: The ID of the company associated with the User (optional)
CCLARequiresICLA:
type: boolean
description: Flag ensuring user signs ICLA and is acknowledged the company
x-omitempty: false
companyAffiliation:
type: boolean
description: User is affiliated with a company (use case when user has no companyID attribute)
x-omitempty: false
ICLA:
type: boolean
description: User has an ICLA signature
x-omitempty: false
CCLA:
type: boolean
description: Flag to indicate if user has been company acknowledged and approved
x-omitempty: false



247 changes: 247 additions & 0 deletions cla-backend-go/v2/signatures/mock_users/mock_service.go
534 changes: 534 additions & 0 deletions cla-backend-go/v2/signatures/mock_v1_signatures/mock_service.go

Large diffs are not rendered by default.

85 changes: 73 additions & 12 deletions cla-backend-go/v2/signatures/service.go
Original file line number Diff line number Diff line change
@@ -444,31 +444,92 @@ func (s *Service) IsUserAuthorized(ctx context.Context, lfid, claGroupId string)
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
}

hasSigned := false

response := models.LfidAuthorizedResponse{
ClaGroupID: claGroupId,
Lfid: lfid,
ClaGroupID: claGroupId,
Lfid: lfid,
Authorized: false,
ICLA: false,
CCLA: false,
CCLARequiresICLA: false,
}

// fetch cla group
log.WithFields(f).Debug("fetching cla group")
claGroup, err := s.v1ProjectService.GetCLAGroupByID(ctx, claGroupId)
if err != nil {
log.WithFields(f).WithError(err).Debug("unable to fetch cla group")
return nil, err
}

if claGroup == nil {
log.WithFields(f).Debug("cla group not found")
return &response, nil
}
response.CCLARequiresICLA = claGroup.ProjectCCLARequiresICLA

// fetch cla user
log.WithFields(f).Debug("fetching user by lfid")
user, err := s.usersService.GetUserByLFUserName(lfid)

if user.CompanyID != "" {
log.WithFields(f).Debugf("User is associated with company: %s", user.CompanyID)
response.CompanyID = user.CompanyID
}

if err != nil {
log.WithFields(f).WithError(err).Debug("unable to fetch lfusername")
return nil, err
}

hasSigned, _, err := s.v1SignatureService.HasUserSigned(ctx, user, claGroupId)
if err != nil {
log.WithFields(f).WithError(err).Debug("Unable to check authorized status of the user")
return nil, err
if user == nil {
log.WithFields(f).Debug("user not found")
return &response, nil
}

// check if user has signed ICLA
log.WithFields(f).Debug("checking if user has signed ICLA")
approved, signed := true, true
icla, iclaErr := s.v1SignatureService.GetIndividualSignature(ctx, claGroupId, user.UserID, &approved, &signed)
if iclaErr != nil {
log.WithFields(f).WithError(iclaErr).Debug("unable to get individual signature")
}

if icla != nil {
log.WithFields(f).Debug("user has signed ICLA")
response.ICLA = true
hasSigned = true
}

// fetch company
if user.CompanyID == "" {
log.WithFields(f).Debug("user company id not found")
response.CompanyAffiliation = false
} else {
log.WithFields(f).Debug("fetching company")
companyModel, err := s.v1CompanyService.GetCompany(ctx, user.CompanyID)
if err != nil {
log.WithFields(f).WithError(err).Debug("unable to fetch company")
return nil, err
}
if companyModel == nil {
log.WithFields(f).Debug("company not found")
response.CompanyAffiliation = false
} else {
log.WithFields(f).Debug("company found")
response.CompanyAffiliation = true
// process ecla
ecla, err := s.v1SignatureService.ProcessEmployeeSignature(ctx, companyModel, claGroup, user)
if err != nil {
log.WithFields(f).WithError(err).Debug("unable to process ecla")
return nil, err
}
if ecla != nil && *ecla {
log.WithFields(f).Debug("user has signed ECLA")
hasSigned = true
response.CCLA = true
} else {
log.WithFields(f).Debug("user has not acknowledged with the company ")
}
}
}

response.Authorized = *hasSigned
response.Authorized = hasSigned
return &response, nil
}
201 changes: 201 additions & 0 deletions cla-backend-go/v2/signatures/service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// Copyright The Linux Foundation and each contributor to CommunityBridge.
// SPDX-License-Identifier: MIT

package signatures

import (
"context"
"errors"
"fmt"
"testing"

v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models"
// mock_signatures "github.com/communitybridge/easycla/cla-backend-go/v2/signatures/mock_v1_signatures"
mock_company "github.com/communitybridge/easycla/cla-backend-go/company/mocks"
ini "github.com/communitybridge/easycla/cla-backend-go/init"
mock_project "github.com/communitybridge/easycla/cla-backend-go/project/mocks"
mock_users "github.com/communitybridge/easycla/cla-backend-go/v2/signatures/mock_users"
mock_v1_signatures "github.com/communitybridge/easycla/cla-backend-go/v2/signatures/mock_v1_signatures"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)

func TestService_IsUserAuthorized_User_Not_Found(t *testing.T) {
// TestIsUserAuthorized test case
lfid := "foolfid"
projectID := "project-1234"

ctrl := gomock.NewController(t)
defer ctrl.Finish()

awsSession, err := ini.GetAWSSession()
if err != nil {
assert.Fail(t, "unable to create AWS session")
}

mockUserService := mock_users.NewMockService(ctrl)
mockUserService.EXPECT().GetUserByLFUserName(lfid).Return(nil, nil)

service := NewService(awsSession, "", nil, nil, nil, nil, nil, mockUserService, nil)

result, err := service.IsUserAuthorized(context.Background(), lfid, projectID)

assert.Nil(t, err)
assert.False(t, result.Authorized)
}

func TestService_IsUserAuthorized_CCLA_Requires_ICLA(t *testing.T) {
type testCase struct {
lfid string
projectID string
userID string
companyID string
claGroupRequiresICLA bool
getIndividualSignatureResult *v1Models.Signature
getIndividualSignatureError error
processEmployeeSignatureResult *bool
processEmployeeSignatureError error
expectedAuthorized bool
expectedCCLARequiresICLA bool
expectedICLA bool
expectedCCLA bool
expectedCompanyAffiliation bool
}

cases := []testCase{
{
lfid: "foobar_1",
projectID: "project-123",
userID: "user-123",
companyID: "company-123",
claGroupRequiresICLA: true,
getIndividualSignatureResult: &v1Models.Signature{
SignatureID: "signature-123",
},
getIndividualSignatureError: nil,
processEmployeeSignatureResult: func() *bool { b := true; return &b }(),
processEmployeeSignatureError: nil,
expectedAuthorized: true,
expectedCCLARequiresICLA: true,
expectedICLA: true,
expectedCCLA: true,
expectedCompanyAffiliation: true,
},
{
lfid: "foobar_2",
projectID: "project-123",
userID: "user-123",
companyID: "company-123",
claGroupRequiresICLA: false,
getIndividualSignatureResult: &v1Models.Signature{
SignatureID: "signature-123",
},
getIndividualSignatureError: nil,
processEmployeeSignatureResult: func() *bool { b := true; return &b }(),
processEmployeeSignatureError: nil,
expectedAuthorized: true,
expectedCCLARequiresICLA: false,
expectedICLA: true,
expectedCCLA: true,
expectedCompanyAffiliation: true,
},
{
lfid: "foobar_3",
projectID: "project-123",
userID: "user-123",
companyID: "company-123",
claGroupRequiresICLA: true,
getIndividualSignatureResult: &v1Models.Signature{
SignatureID: "signature-123",
},
getIndividualSignatureError: nil,
processEmployeeSignatureResult: nil,
processEmployeeSignatureError: nil,
expectedAuthorized: true,
expectedCCLARequiresICLA: true,
expectedICLA: true,
expectedCCLA: false,
expectedCompanyAffiliation: true,
},
{
lfid: "foobar_4",
projectID: "project-123",
userID: "user-123",
companyID: "company-123",
claGroupRequiresICLA: true,
getIndividualSignatureResult: nil,
getIndividualSignatureError: errors.New("some error"),
processEmployeeSignatureResult: func() *bool { b := true; return &b }(),
processEmployeeSignatureError: nil,
expectedAuthorized: true,
expectedCCLARequiresICLA: true,
expectedICLA: false,
expectedCCLA: true,
expectedCompanyAffiliation: true,
},
{
lfid: "foobar_5",
projectID: "project-123",
userID: "user-123",
companyID: "company-123",
claGroupRequiresICLA: true,
getIndividualSignatureResult: nil,
getIndividualSignatureError: errors.New("some error"),
processEmployeeSignatureResult: func() *bool { b := false; return &b }(),
processEmployeeSignatureError: nil,
expectedAuthorized: false,
expectedCCLARequiresICLA: true,
expectedICLA: false,
expectedCCLA: false,
expectedCompanyAffiliation: true,
},
}

for _, tc := range cases {
t.Run(fmt.Sprintf("LFID=%s ProjectID=%s", tc.lfid, tc.projectID), func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

awsSession, err := ini.GetAWSSession()
if err != nil {
assert.Fail(t, "unable to create AWS session")
}

mockUserService := mock_users.NewMockService(ctrl)
mockUserService.EXPECT().GetUserByLFUserName(tc.lfid).Return(&v1Models.User{
UserID: tc.userID,
CompanyID: tc.companyID,
}, nil)

mockProjectService := mock_project.NewMockService(ctrl)
mockProjectService.EXPECT().GetCLAGroupByID(context.Background(), tc.projectID).Return(&v1Models.ClaGroup{
ProjectID: tc.projectID,
ProjectCCLARequiresICLA: tc.claGroupRequiresICLA,
}, nil)

mockSignatureService := mock_v1_signatures.NewMockSignatureService(ctrl)

approved := true
signed := true
mockSignatureService.EXPECT().GetIndividualSignature(context.Background(), tc.projectID, tc.userID, &approved, &signed).Return(tc.getIndividualSignatureResult, tc.getIndividualSignatureError)

mockSignatureService.EXPECT().ProcessEmployeeSignature(context.Background(), gomock.Any(), gomock.Any(), gomock.Any()).Return(tc.processEmployeeSignatureResult, tc.processEmployeeSignatureError)

mockCompanyService := mock_company.NewMockIService(ctrl)
mockCompanyService.EXPECT().GetCompany(context.Background(), tc.companyID).Return(&v1Models.Company{
CompanyID: tc.companyID,
}, nil)

service := NewService(awsSession, "", mockProjectService, mockCompanyService, mockSignatureService, nil, nil, mockUserService, nil)

result, err := service.IsUserAuthorized(context.Background(), tc.lfid, tc.projectID)

assert.Nil(t, err)
assert.Equal(t, tc.expectedAuthorized, result.Authorized)
assert.Equal(t, tc.expectedCCLARequiresICLA, result.CCLARequiresICLA)
assert.Equal(t, tc.expectedICLA, result.ICLA)
assert.Equal(t, tc.expectedCCLA, result.CCLA)
assert.Equal(t, tc.expectedCompanyAffiliation, result.CompanyAffiliation)
})
}
}

0 comments on commit 4876d47

Please sign in to comment.