diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 386e4633a..d7dee8ce9 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -8,7 +8,6 @@ on: branches: - dev - permissions: id-token: write contents: read diff --git a/.gitignore b/.gitignore index 834c8f3bb..65b03c4ce 100755 --- a/.gitignore +++ b/.gitignore @@ -241,3 +241,7 @@ dist/* api-postman/* cla-backend/run-python-test-example-*.py + +# LG +out +*.secret diff --git a/aws_env.md b/aws_env.md new file mode 100644 index 000000000..4e53960f3 --- /dev/null +++ b/aws_env.md @@ -0,0 +1,130 @@ +# Setting up AWS environment + +You need to have MFA enabled for your AWS user, your `~/.aws/config` shoudl look like this: +``` +[profile lfproduct-dev] +role_arn = arn:aws:iam::395594542180:role/product-contractors-role +source_profile = lfproduct +region = us-east-1 +output = json + +[profile lfproduct-test] +role_arn = arn:aws:iam::726224182707:role/product-contractors-role +source_profile = lfproduct +region = us-east-1 +output = json + +[profile lfproduct-staging] +role_arn = arn:aws:iam::844390194980:role/product-contractors-role +source_profile = lfproduct +region = us-east-1 +output = json + +[profile lfproduct-prod] +role_arn = arn:aws:iam::716487311010:role/product-contractors-role +source_profile = lfproduct +region = us-east-1 +output = json + +[default] +region = us-east-1 +output = json +``` + +It defines 4 profiles to use: `dev`, `staging`, `test` and `prod`. + +You will be using one of them. + + +Your `~/.aws/credentials` file shoudl initially look like this (replace `redacted`): +``` +[lfproduct-long-term] +aws_secret_access_key = [access_key_redacted] +aws_access_key_id = [key_id_redacted] +aws_mfa_device = arn:aws:iam::[arn_number_redacted]:mfa/[your_aws_user_redacted] + +[default] +aws_access_key_id = [key_id_redacted] +aws_secret_access_key = [access_key_redacted] +``` + +Now every 36 hours or less you need to refresh your MFA key by calling: `aws-mfa --force --duration 129600 --profile lfproduct`. + +When called it adds or replaces the following section (`[lfproduct]` which is used as a source profile for `dev`, `test`, `staging` or `prod` in aws config) in `~/.aws/credentials`: +``` +[lfproduct] +assumed_role = False +aws_access_key_id = [key_id_redacted] +aws_secret_access_key = [secret_access_key_redacted] +aws_session_token = [session_token_redacted] +aws_security_token = [session_token_redacted] +expiration = 2024-11-28 16:54:59 [now + 36 hours] + +``` + + +Once you have all of this, you must set a correct set of environment variables to run either `python` or `golang` backends. + +To do so you need to get credentials for a specific profile `lfproduct-`: `dev`, `test`, `staging`, `prod`. To see full one-time set of credentials you can call: +- for `dev`: `` aws sts assume-role --role-arn arn:aws:iam::395594542180:role/product-contractors-role --profile lfproduct --role-session-name lfproduct-dev-session ``. +- for `prod`: `` aws sts assume-role --role-arn arn:aws:iam::716487311010:role/product-contractors-role --profile lfproduct --role-session-name lfproduct-prod-session ``. + +Note - just replace the iam::[number] depending on environment type (`[stage]`) and update `lfproduct-[stage]-name`. + +You can set up a script like `setenv.sh` which will set all required variables, example for `dev`: +``` +#!/bin/bash + +rm -rf /tmp/aws +cp -R /root/.aws /tmp/.aws + +data="$(aws sts assume-role --role-arn arn:aws:iam::395594542180:role/product-contractors-role --profile lfproduct --role-session-name lfproduct-dev-session)" +export AWS_ACCESS_KEY_ID="$(echo "${data}" | jq -r '.Credentials.AccessKeyId')" +export AWS_SECRET_ACCESS_KEY="$(echo "${data}" | jq -r '.Credentials.SecretAccessKey')" +export AWS_SESSION_TOKEN="$(echo "${data}" | jq -r '.Credentials.SessionToken')" +export AWS_SECURITY_TOKEN="$(echo "${data}" | jq -r '.Credentials.SessionToken')" + +export AWS_SDK_LOAD_CONFIG=true +export AWS_PROFILE='lfproduct-dev' +export AWS_REGION='us-east-1' +export AWS_DEFAULT_REGION='us-east-1' +export DYNAMODB_AWS_REGION='us-east-1' +export REGION='us-east-1' + +export PRODUCT_DOMAIN='dev.lfcla.com' +export ROOT_DOMAIN='lfcla.dev.platform.linuxfoundation.org' +export PORT='5000' +export STAGE='dev' +# export STAGE='local' +export GH_ORG_VALIDATION=false +export DISABLE_LOCAL_PERMISSION_CHECKS=true +export COMPANY_USER_VALIDATION=false +export CLA_SIGNATURE_FILES_BUCKET=cla-signature-files-dev +``` + +Call it via `` . ./setenv.sh `` or `` source setenv.sh `` to execute in the current shell. + +You can reset environment variables by exiting the shell session or calling the following `unsetenv.sh` in the current shell via: `` . ./unsetenv.sh `` or `` source unsetenv.sh ``: +``` +#!/bin/bash +rm -rf /tmp/.aws +unset AWS_PROFILE +unset AWS_REGION +unset AWS_ACCESS_KEY_ID +unset AWS_SECRET_ACCESS_KEY +unset PRODUCT_DOMAIN +unset ROOT_DOMAIN +unset PORT +unset STAGE +unset AWS_SESSION_TOKEN +unset AWS_SECURITY_TOKEN +unset GH_ORG_VALIDATION +unset DISABLE_LOCAL_PERMISSION_CHECKS +unset COMPANY_USER_VALIDATION +unset CLA_SIGNATURE_FILES_BUCKET +unset DYNAMODB_AWS_REGION +unset REGION +unset AWS_ROLE_ARN +unset AWS_TOKEN_SERIAL +unset AWS_SDK_LOAD_CONFIG +``` diff --git a/cla-backend-go/api_client/api_client.go b/cla-backend-go/api_client/api_client.go new file mode 100644 index 000000000..42c0d1999 --- /dev/null +++ b/cla-backend-go/api_client/api_client.go @@ -0,0 +1,27 @@ +// Copyright The Linux Foundation and each contributor to CommunityBridge. +// SPDX-License-Identifier: MIT + +package apiclient + +import ( + "context" + "net/http" +) + +type APIClient interface { + GetData(ctx context.Context, url string) (*http.Response, error) +} + +type RestAPIClient struct { + Client *http.Client +} + +// GetData makes a get request to the specified url + +func (c *RestAPIClient) GetData(ctx context.Context, url string) (*http.Response, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, err + } + return c.Client.Do(req) +} diff --git a/cla-backend-go/api_client/mocks/mock_client.go b/cla-backend-go/api_client/mocks/mock_client.go new file mode 100644 index 000000000..08dd5ceba --- /dev/null +++ b/cla-backend-go/api_client/mocks/mock_client.go @@ -0,0 +1,54 @@ +// Copyright The Linux Foundation and each contributor to CommunityBridge. +// SPDX-License-Identifier: MIT + +// Code generated by MockGen. DO NOT EDIT. +// Source: api_client/api_client.go + +// Package mock_apiclient is a generated GoMock package. +package mock_apiclient + +import ( + context "context" + http "net/http" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockAPIClient is a mock of APIClient interface. +type MockAPIClient struct { + ctrl *gomock.Controller + recorder *MockAPIClientMockRecorder +} + +// MockAPIClientMockRecorder is the mock recorder for MockAPIClient. +type MockAPIClientMockRecorder struct { + mock *MockAPIClient +} + +// NewMockAPIClient creates a new mock instance. +func NewMockAPIClient(ctrl *gomock.Controller) *MockAPIClient { + mock := &MockAPIClient{ctrl: ctrl} + mock.recorder = &MockAPIClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockAPIClient) EXPECT() *MockAPIClientMockRecorder { + return m.recorder +} + +// GetData mocks base method. +func (m *MockAPIClient) GetData(ctx context.Context, url string) (*http.Response, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetData", ctx, url) + ret0, _ := ret[0].(*http.Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetData indicates an expected call of GetData. +func (mr *MockAPIClientMockRecorder) GetData(ctx, url interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetData", reflect.TypeOf((*MockAPIClient)(nil).GetData), ctx, url) +} diff --git a/cla-backend-go/cla_manager/service.go b/cla-backend-go/cla_manager/service.go index 00961bae2..e13bbf276 100644 --- a/cla-backend-go/cla_manager/service.go +++ b/cla-backend-go/cla_manager/service.go @@ -39,7 +39,7 @@ type IService interface { PendingRequest(companyID, claGroupID, requestID string) (*models.ClaManagerRequest, error) DeleteRequest(requestID string) error - AddClaManager(ctx context.Context, authUser *auth.User, companyID string, claGroupID string, LFID string, projectSFName string) (*models.Signature, error) + AddClaManager(ctx context.Context, authUser *auth.User, companyID string, claGroupID string, LFID string, projectSFID string) (*models.Signature, error) RemoveClaManager(ctx context.Context, authUser *auth.User, companyID string, claGroupID string, LFID string, projectSFName string) (*models.Signature, error) } @@ -193,7 +193,7 @@ func (s service) DeleteRequest(requestID string) error { } // AddClaManager Adds LFID to Signature Access Control list -func (s service) AddClaManager(ctx context.Context, authUser *auth.User, companyID string, claGroupID string, LFID string, projectSFName string) (*models.Signature, error) { +func (s service) AddClaManager(ctx context.Context, authUser *auth.User, companyID string, claGroupID string, LFID string, projectSFID string) (*models.Signature, error) { f := logrus.Fields{ "functionName": "v1.cla_manager.AddClaManager", @@ -201,9 +201,9 @@ func (s service) AddClaManager(ctx context.Context, authUser *auth.User, company "companyID": companyID, "claGroupID": claGroupID, "LFID": LFID, - "projectName": projectSFName, + "projectSFID": projectSFID, } - + var projectSFName string userModel, userErr := s.usersService.GetUserByLFUserName(LFID) if userErr != nil || userModel == nil { return nil, userErr @@ -218,11 +218,6 @@ func (s service) AddClaManager(ctx context.Context, authUser *auth.User, company return nil, projectErr } - // if projectSFName is empty, we can set clagroup project name. - if projectSFName == "" { - projectSFName = claGroupModel.ProjectName - } - // Look up signature ACL to ensure the user can add cla manager signed := true @@ -258,8 +253,9 @@ func (s service) AddClaManager(ctx context.Context, authUser *auth.User, company RecipientAddress: manager.LfEmail.String(), CompanyName: companyModel.CompanyName, }, - Name: userModel.Username, - Email: userModel.LfEmail.String(), + Name: userModel.Username, + Email: userModel.LfEmail.String(), + ProjectSFID: projectSFID, }, claGroupModel) } // Notify the added user @@ -267,7 +263,7 @@ func (s service) AddClaManager(ctx context.Context, authUser *auth.User, company RecipientName: userModel.Username, RecipientAddress: userModel.LfEmail.String(), CompanyName: companyModel.CompanyName, - }, claGroupModel) + }, claGroupModel, projectSFID) // Send an event s.eventsService.LogEventWithContext(ctx, &events.LogEventArgs{ @@ -317,7 +313,7 @@ func (s service) getCompanySignature(ctx context.Context, companyID string, claG } // RemoveClaManager removes lfid from signature acl with given company and project -func (s service) RemoveClaManager(ctx context.Context, authUser *auth.User, companyID string, claGroupID string, LFID string, projectSFName string) (*models.Signature, error) { +func (s service) RemoveClaManager(ctx context.Context, authUser *auth.User, companyID string, claGroupID string, LFID string, projectSFID string) (*models.Signature, error) { f := logrus.Fields{ "functionName": "v1.cla_manager.RemoveClaManager", @@ -327,6 +323,7 @@ func (s service) RemoveClaManager(ctx context.Context, authUser *auth.User, comp "companyID": companyID, } + var projectSFName string userModel, userErr := s.usersService.GetUserByLFUserName(LFID) if userErr != nil || userModel == nil { return nil, userErr @@ -341,11 +338,6 @@ func (s service) RemoveClaManager(ctx context.Context, authUser *auth.User, comp return nil, projectErr } - // if projectSFName is empty, we can set clagroup project name. - if projectSFName == "" { - projectSFName = claGroupModel.ProjectName - } - signed := true approved := true sigModel, sigErr := s.sigService.GetProjectCompanySignature(ctx, companyID, claGroupID, &signed, &approved, nil, aws.Int64(5)) @@ -420,50 +412,56 @@ func (s service) RemoveClaManager(ctx context.Context, authUser *auth.User, comp type ProjectDetails struct { ProjectName string - ProjectSFID string + ProjectSFID []string } -func (s service) getProjectDetails(ctx context.Context, claGroupModel *models.ClaGroup) ProjectDetails { - f := logrus.Fields{ - "functionName": "v1.cla_manager.getProjectDetails", - utils.XREQUESTID: ctx.Value(utils.XREQUESTID), - "claGroupID": claGroupModel.ProjectID, - } +// func (s service) getProjectDetails(ctx context.Context, claGroupModel *models.ClaGroup) ProjectDetails { +// f := logrus.Fields{ +// "functionName": "v1.cla_manager.getProjectDetails", +// utils.XREQUESTID: ctx.Value(utils.XREQUESTID), +// "claGroupID": claGroupModel.ProjectID, +// } +// projectSFIDs := make([]string, 0) - projectDetails := ProjectDetails{ - ProjectName: claGroupModel.ProjectName, - ProjectSFID: claGroupModel.ProjectExternalID, - } - signedAtFoundation := false +// projectDetails := ProjectDetails{ +// ProjectName: claGroupModel.ProjectName, +// } +// signedAtFoundation := false +// var err error - pcg, err := s.projectClaRepository.GetCLAGroup(ctx, claGroupModel.ProjectID) - if err != nil { - log.WithFields(f).Warnf("unable to fetch project cla group by project id: %s, error: %+v", claGroupModel.ProjectID, err) - } +// pcg, pcgErr := s.projectClaRepository.GetCLAGroup(ctx, claGroupModel.ProjectID) +// if pcgErr != nil { +// log.WithFields(f).WithError(err).Debug("unable too get pcg record") +// } - // check if cla group is signed at foundation level - if pcg != nil && pcg.FoundationSFID != "" { - signedAtFoundation, err = s.projectClaRepository.IsExistingFoundationLevelCLAGroup(ctx, pcg.FoundationSFID) - if err != nil { - log.WithFields(f).Warnf("unable to fetch foundation level cla group by foundation id: %s, error: %+v", pcg.FoundationSFID, err) - } +// signedAtFoundation, err = s.projectClaRepository.SignedAtFoundation(ctx, claGroupModel.ProjectID) +// if err != nil { +// log.WithFields(f).WithError(err).Debug("unable to get status of cla signed at foundation") +// } - if signedAtFoundation { - log.WithFields(f).Debugf("cla group is signed at foundation level...") - projectDetails.ProjectName = pcg.FoundationName - projectDetails.ProjectSFID = pcg.FoundationSFID - } - } +// if signedAtFoundation && pcg != nil && err != nil { +// log.WithFields(f).Debug("cla group is signed at foundation level...") +// projectDetails.ProjectName = pcg.FoundationName +// projectSFIDs = append(projectSFIDs, pcg.FoundationSFID) - return projectDetails -} +// } else { +// log.WithFields(f).Debug("cla group is signed at project level ...") + +// } -func (s service) sendClaManagerAddedEmailToUser(emailSvc emails.EmailTemplateService, emailParams emails.CommonEmailParams, claGroupModel *models.ClaGroup) { - projectDetails := s.getProjectDetails(context.Background(), claGroupModel) - projectName := projectDetails.ProjectName - projectSFID := projectDetails.ProjectSFID +// projectDetails.ProjectSFID = projectSFIDs + +// return projectDetails +// } + +func (s service) sendClaManagerAddedEmailToUser(emailSvc emails.EmailTemplateService, emailParams emails.CommonEmailParams, claGroupModel *models.ClaGroup, projectSFID string) { + f := logrus.Fields{ + "functionName": "sendClaManagerAddedEmailToUser", + "projectSFID": projectSFID, + } + log.WithFields(f).Info("Sending email to user") // subject string, body string, recipients []string - subject := fmt.Sprintf("EasyCLA: Added as CLA Manager for Project :%s", projectName) + subject := fmt.Sprintf("EasyCLA: Added as CLA Manager for Project :%s", claGroupModel.ProjectName) recipients := []string{emailParams.RecipientAddress} body, err := emails.RenderClaManagerAddedEToUserTemplate(emailSvc, claGroupModel.Version, projectSFID, emails.ClaManagerAddedEToUserTemplateParams{ CommonEmailParams: emailParams, @@ -487,7 +485,7 @@ func sendClaManagerAddedEmailToCLAManagers(emailSvc emails.EmailTemplateService, // subject string, body string, recipients []string subject := fmt.Sprintf("EasyCLA: CLA Manager Added Notice for %s", projectName) recipients := []string{emailParams.RecipientAddress} - body, err := emails.RenderClaManagerAddedToCLAManagersTemplate(emailSvc, claGroupModel.Version, claGroupModel.ProjectExternalID, emailParams) + body, err := emails.RenderClaManagerAddedToCLAManagersTemplate(emailSvc, claGroupModel.Version, projectName, emailParams) if err != nil { log.Warnf("email template render : %s failed : %v", emails.ClaManagerAddedToCLAManagersTemplate, err) return @@ -553,10 +551,12 @@ func sendRemovedClaManagerEmailToRecipient(emailSvc emails.EmailTemplateService, body, err := emails.RenderRemovedCLAManagerTemplate( emailSvc, claGroupModel.Version, - claGroupModel.ProjectExternalID, emails.RemovedCLAManagerTemplateParams{ CommonEmailParams: emailParams, CLAManagers: emailCLAManagerParams, + CLAGroupTemplateParams: emails.CLAGroupTemplateParams{ + CLAGroupName: projectName, + }, }) if err != nil { @@ -573,14 +573,11 @@ func sendRemovedClaManagerEmailToRecipient(emailSvc emails.EmailTemplateService, } func (s service) sendClaManagerDeleteEmailToCLAManagers(emailSvc emails.EmailTemplateService, emailParams emails.ClaManagerDeletedToCLAManagersTemplateParams, claGroupModel *models.ClaGroup) { - projectDetails := s.getProjectDetails(context.Background(), claGroupModel) - projectName := projectDetails.ProjectName - projectSFID := projectDetails.ProjectSFID // subject string, body string, recipients []string - subject := fmt.Sprintf("EasyCLA: CLA Manager Removed Notice for %s", projectName) + subject := fmt.Sprintf("EasyCLA: CLA Manager Removed Notice for %s", claGroupModel.ProjectName) recipients := []string{emailParams.RecipientAddress} - body, err := emails.RenderClaManagerDeletedToCLAManagersTemplate(emailSvc, claGroupModel.Version, projectSFID, emailParams) + body, err := emails.RenderClaManagerDeletedToCLAManagersTemplate(emailSvc, claGroupModel.Version, claGroupModel.ProjectName) if err != nil { log.Warnf("email template render : %s failed : %v", emails.ClaManagerDeletedToCLAManagersTemplateName, err) diff --git a/cla-backend-go/cmd/dynamo_events_lambda/main.go b/cla-backend-go/cmd/dynamo_events_lambda/main.go index ee910d335..652f7e4e3 100644 --- a/cla-backend-go/cmd/dynamo_events_lambda/main.go +++ b/cla-backend-go/cmd/dynamo_events_lambda/main.go @@ -115,12 +115,7 @@ func init() { githubOrganizationsService := github_organizations.NewService(githubOrganizationsRepo, repositoriesRepo, projectClaGroupRepo) repositoriesService := repositories.NewService(repositoriesRepo, githubOrganizationsRepo, projectClaGroupRepo) - gerritService := gerrits.NewService(gerritRepo, &gerrits.LFGroup{ - LfBaseURL: configFile.LFGroup.ClientURL, - ClientID: configFile.LFGroup.ClientID, - ClientSecret: configFile.LFGroup.ClientSecret, - RefreshToken: configFile.LFGroup.RefreshToken, - }) + gerritService := gerrits.NewService(gerritRepo) // Services projectService := service.NewService(projectRepo, repositoriesRepo, gerritRepo, projectClaGroupRepo, usersRepo) diff --git a/cla-backend-go/cmd/gitlab_repository_check/handler/handler.go b/cla-backend-go/cmd/gitlab_repository_check/handler/handler.go index f4b261403..1097ffa52 100644 --- a/cla-backend-go/cmd/gitlab_repository_check/handler/handler.go +++ b/cla-backend-go/cmd/gitlab_repository_check/handler/handler.go @@ -137,12 +137,7 @@ func Handler(ctx context.Context) error { v1ProjectClaGroupRepo, }) - gerritService := gerrits.NewService(gerritRepo, &gerrits.LFGroup{ - LfBaseURL: configFile.LFGroup.ClientURL, - ClientID: configFile.LFGroup.ClientID, - ClientSecret: configFile.LFGroup.ClientSecret, - RefreshToken: configFile.LFGroup.RefreshToken, - }) + gerritService := gerrits.NewService(gerritRepo) approvalsTableName := "cla-" + stage + "-approvals" diff --git a/cla-backend-go/cmd/ldap_gerrit_check/main.go b/cla-backend-go/cmd/ldap_gerrit_check/main.go new file mode 100644 index 000000000..76fdca97c --- /dev/null +++ b/cla-backend-go/cmd/ldap_gerrit_check/main.go @@ -0,0 +1,173 @@ +// Copyright The Linux Foundation and each contributor to CommunityBridge. +// SPDX-License-Identifier: MIT + +package main + +import ( + // "context" + "encoding/csv" + "flag" + "fmt" + "os" + "path/filepath" + "sync" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/communitybridge/easycla/cla-backend-go/events" + "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models" + eventOps "github.com/communitybridge/easycla/cla-backend-go/gen/v1/restapi/operations/events" + log "github.com/communitybridge/easycla/cla-backend-go/logging" + // "github.com/communitybridge/easycla/cla-backend-go/users" +) + +var awsSession = session.Must(session.NewSession(&aws.Config{})) +var stage string + +func main() { + stage = os.Getenv("STAGE") + if stage == "" { + log.Fatal("stage not set") + } + log.Infof("STAGE set to %s\n", stage) + + var wg sync.WaitGroup + var mu sync.Mutex + + // Initialize the events repository + eventsRepo := events.NewRepository(awsSession, stage) + eventService := events.NewService(eventsRepo, nil) + + // Initialize the users repository + // usersRepo := users.NewRepository(awsSession, stage) + + inputFilename := flag.String("input-file", "", "Input with a given list of lf usernames") + claGroup := flag.String("cla-group-id", "", "The ID of the CLA group") + claGroupName := flag.String("cla-group-name", "", "The name of the CLA group") + flag.Parse() + + if *inputFilename == "" || *claGroup == "" { + log.Fatalf("Both input-file and cla-group are required") + } + + log.Debugf("Input file: %s", *inputFilename) + + file, err := os.Open(*inputFilename) + if err != nil { + log.Fatalf("Unable to read input file: %s", *inputFilename) + } + + defer func() { + if err = file.Close(); err != nil { + log.Fatalf("Error closing file: %v", err) + } + }() + + reader := csv.NewReader(file) + + records, err := reader.ReadAll() + if err != nil { + log.Fatalf("Unable to read file") + } + + log.Debugf("CLA Group Name: %s", *claGroup) + + type Report struct { + Username string + Events []*models.Event + } + + projectReport := make([]Report, 0) + + for i, record := range records { + if i == 0 { + continue + } + lfUsername := record[0] + log.Debugf("Processing record: %s", lfUsername) + report := Report{ + Username: lfUsername, + } + + // Increment the wait group + wg.Add(1) + + go func(lfusername string) { + defer wg.Done() + log.Debugf("Processing record: %s", lfusername) + searchParams := eventOps.SearchEventsParams{ + SearchTerm: &lfusername, + ProjectID: claGroup, + } + events, eventErr := eventService.SearchEvents(&searchParams) + if eventErr != nil { + log.Debugf("Error getting events: %v", eventErr) + report.Events = nil + } + + if len(events.Events) == 0 { + log.Warnf("No events found for user: %s", lfusername) + report.Events = nil + } else { + log.Debugf("Events found for user: %s", lfusername) + report.Events = events.Events + } + + mu.Lock() + projectReport = append(projectReport, report) + defer mu.Unlock() + + }(lfUsername) + } + + // Wait for all the go routines to finish + wg.Wait() + + // Create a csv file with the results + outputFilename := fmt.Sprintf("ldap-%s-%s.csv", *claGroupName, time.Now().Format("2006-01-02-15-04-05")) + outputFile, err := os.Create(filepath.Clean(outputFilename)) + + if err != nil { + log.Fatalf("Unable to create output file: %s", outputFilename) + } + + defer func() { + if err = outputFile.Close(); err != nil { + log.Fatalf("Error closing file: %v", err) + } + }() + + writer := csv.NewWriter(outputFile) + + err = writer.Write([]string{"Username", "Event ID", "Event Data", "Event Type", "Event Date"}) + if err != nil { + log.Fatalf("Error writing csv: %v", err) + } + + for _, report := range projectReport { + if report.Events == nil { + err = writer.Write([]string{report.Username, "No events found", "", "", ""}) + if err != nil { + log.Fatalf("Error writing csv: %v", err) + } + continue + } + for _, event := range report.Events { + err = writer.Write([]string{report.Username, event.EventID, event.EventData, event.EventType, event.EventTime}) + if err != nil { + log.Fatalf("Error writing csv: %v", err) + } + + } + } + + writer.Flush() + + if err := writer.Error(); err != nil { + log.Fatalf("Error writing csv: %v", err) + } + + log.Infof("Output written to: %s", outputFilename) + +} diff --git a/cla-backend-go/cmd/migrate_approval_list/main.go b/cla-backend-go/cmd/migrate_approval_list/main.go index 574b58356..63f6c387f 100644 --- a/cla-backend-go/cmd/migrate_approval_list/main.go +++ b/cla-backend-go/cmd/migrate_approval_list/main.go @@ -81,7 +81,7 @@ func init() { v1ProjectClaGroupRepo, }) ghOrgRepo = github_organizations.NewRepository(awsSession, stage) - gerritService = gerrits.NewService(gerritsRepo, nil) + gerritService = gerrits.NewService(gerritsRepo) signatureRepo = signatures.NewRepository(awsSession, stage, companyRepo, usersRepo, eventsService, &ghRepo, ghOrgRepo, gerritService, approvalRepo) log.Info("initialized repositories\n") diff --git a/cla-backend-go/cmd/server.go b/cla-backend-go/cmd/server.go index 1e67f9680..e2291e37d 100644 --- a/cla-backend-go/cmd/server.go +++ b/cla-backend-go/cmd/server.go @@ -273,13 +273,7 @@ func server(localMode bool) http.Handler { v1ProjectClaGroupRepo, }) - gerritService := gerrits.NewService(gerritRepo, &gerrits.LFGroup{ - LfBaseURL: configFile.LFGroup.ClientURL, - ClientID: configFile.LFGroup.ClientID, - ClientSecret: configFile.LFGroup.ClientSecret, - RefreshToken: configFile.LFGroup.RefreshToken, - EventsService: eventsService, - }) + gerritService := gerrits.NewService(gerritRepo) // Signature repository handler signaturesRepo := signatures.NewRepository(awsSession, stage, v1CompanyRepo, usersRepo, eventsService, gitV1Repository, githubOrganizationsRepo, gerritService, approvalsRepo) diff --git a/cla-backend-go/emails/cla_manager_templates.go b/cla-backend-go/emails/cla_manager_templates.go index 4c7b85e45..d8f5d2dbc 100644 --- a/cla-backend-go/emails/cla_manager_templates.go +++ b/cla-backend-go/emails/cla_manager_templates.go @@ -16,8 +16,8 @@ const ( // RemovedCLAManagerTemplate includes the email template for email when user is removed as CLA Manager RemovedCLAManagerTemplate = `

Hello {{.RecipientName}},

-

This is a notification email from EasyCLA regarding the project {{.GetProjectNameOrFoundation}} and CLA Group {{.CLAGroupName}}.

-

You have been removed as a CLA Manager from {{.CompanyName}} for the project {{.Project.ExternalProjectName}}.

+

This is a notification email from EasyCLA regarding the CLA Group {{.CLAGroupName}}.

+

You have been removed as a CLA Manager from {{.CompanyName}} for the CLA Group {{.CLAGroupName}}.

If you have further questions about this, please contact one of the existing managers from {{.CompanyName}}: