Skip to content

Commit 23f9fd5

Browse files
ninzdealako
authored andcommitted
Invite request api (#193)
* adjust MetaFields resq/res json * marshal json before using POST request for docraptor * Include ACL put object permissions * include tab ids * add full apache html, adjust dynamo wrappers * update anchor string to v2.0 * remove view docusign button * bucket file name convention to the new automated templates * add project ID and signature type to retrieve s3 files * use camel case for backend data * pdf urls camel case * adjust offsets for icla fields, include pdfURLs into save dynamo * add pdfs URLs to template create service * add s3 URL for backwards compatibility, get latest version of template by creation date * change camel case for project template frontend * remove console log * typo * correct function arguments for user.get_latest_signatures * wip added Delete whitelist api * Update CLA Check to include Github org whitelist * add github auth with auto redirect * load config properly * Added delete and add whitelist apis * added logic in add org whitelist method to handle duplicate entrys * added get api for cla whitelist * modified code to adhere to coding standards * added github auth check to whitelist * add selected field to github org response * CLA-776 / CLA-777 * quick fix * UI Fixes * Update UI * fix minor issues with cors and delete api * added api to add approved users to company acl * add company invites to dynamodb * send email to company admins for corporate console access * wip still needs end to end testing * add company invites to serverless yml * fix cors issues withCredentials * added api to add approved users to company acl * corrected typo in accesslist/handlers.go * add comments for company invites python model * add invite request endpoint and corporate link env var * added invite api * changed error message in GetPendingInviteRequests * fixed new line placement and restructured cla-backend-go * fixed typos * add dynamo userPermissions model and use authorizer * send invite request for cla managers * use query instead of scan to load user by lfusername * wip merging cla-764 with invite-list-api * wip merge builds * modified config/ssm.go * placed AddUserToCompanyAccessList and GetEmailFromLFID in user package * removed invile list folder * added endpoint handler for SendInviteRequest * use user name instead of lfusername for email * refactored logic to service layer * modified approval email template * spacing for email template * Initial duplicate company UI * fixed GetPendingComapny method * resolve conflict * use company invites table * resolve conflict * add backend endpoints to UI, pending invites to display on Company page * typo * pending invites endpoint, css on grid * refactored repo logic to repo layer * refactored repo logic from service layer to repo layer * added LFID to user struct * add inviteId to request params, add add accept/decline endpoints to UI * delete unused accesslist package * Update Pending Invites UI * add authorization headers on handlers * add deletePendingInvite endpoint * check duplicate ACLs before adding * adjust UI for backend CompanyInvites endpoints * adjust pending invites grid size * removed duplicate path * wip resolved PR changes * modified branch to resolve PR requests * resolved PR modification requests * add Pending Invites insert to dynamodb when sending email requests * request of access email alert when company already exists * remove pending invites from companies * adjust css * delete pending invite request once accepted, include inviteID as parameter * use deleteWithBody to pass inviteID * Fix for Modal header * add userLFID to the requestBody * delete pending invite once its accepted * show only signed signatures on View Signatures on PMC * display Sign out from corporate console * Remove second declaration of Repository interface in whitelist/service.go
1 parent 9e9b771 commit 23f9fd5

File tree

30 files changed

+1433
-357
lines changed

30 files changed

+1433
-357
lines changed

cla-backend-go/Gopkg.lock

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

cla-backend-go/auth/authorizer.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,18 @@ type UserPermissioner interface {
1818
GetUserAndProfilesByLFID(lfidUsername string) (user.CLAUser, error)
1919
GetUserProjectIDs(userID string) ([]string, error)
2020
GetClaManagerCorporateClaIDs(userID string) ([]string, error)
21+
GetUserCompanyIDs(userID string) ([]string, error)
2122
}
2223

2324
type Authorizer struct {
2425
auth0Validator Auth0Validator
2526
userPermissioner UserPermissioner
2627
}
2728

28-
func NewAuthorizer(auth0Validator Auth0Validator) Authorizer {
29+
func NewAuthorizer(auth0Validator Auth0Validator, userPermissioner UserPermissioner) Authorizer {
2930
return Authorizer{
30-
auth0Validator: auth0Validator,
31-
// userPermissioner: userPermissioner,
31+
auth0Validator: auth0Validator,
32+
userPermissioner: userPermissioner,
3233
}
3334
}
3435

@@ -71,6 +72,13 @@ func (a Authorizer) SecurityAuth(token string, scopes []string) (*user.CLAUser,
7172

7273
user.ProjectIDs = projectIDs
7374
case companyScope:
75+
//TODO: Get all companies for this user
76+
companies, err := a.userPermissioner.GetUserCompanyIDs(user.UserID)
77+
if err != nil {
78+
return nil, err
79+
}
80+
81+
user.CompanyIDs = companies
7482
case adminScope:
7583
}
7684
}

cla-backend-go/cmd/server.go

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ import (
77
"net/url"
88
"os"
99

10+
"github.com/LF-Engineering/cla-monorepo/cla-backend-go/auth"
11+
"github.com/LF-Engineering/cla-monorepo/cla-backend-go/company"
1012
"github.com/LF-Engineering/cla-monorepo/cla-backend-go/config"
1113
"github.com/LF-Engineering/cla-monorepo/cla-backend-go/docraptor"
1214
"github.com/LF-Engineering/cla-monorepo/cla-backend-go/gen/restapi"
1315
"github.com/LF-Engineering/cla-monorepo/cla-backend-go/gen/restapi/operations"
1416
"github.com/LF-Engineering/cla-monorepo/cla-backend-go/github"
1517
"github.com/LF-Engineering/cla-monorepo/cla-backend-go/health"
1618
"github.com/LF-Engineering/cla-monorepo/cla-backend-go/template"
19+
"github.com/LF-Engineering/cla-monorepo/cla-backend-go/user"
1720
"github.com/LF-Engineering/cla-monorepo/cla-backend-go/whitelist"
21+
1822
"github.com/aws/aws-sdk-go/aws"
1923
"github.com/aws/aws-sdk-go/aws/session"
2024
"github.com/aws/aws-sdk-go/service/dynamodb"
@@ -106,43 +110,48 @@ func server() http.Handler {
106110
logrus.Panic(err)
107111
}
108112

109-
// auth0Validator, err := auth.NewAuth0Validator(
110-
// configFile.Auth0.Domain,
111-
// configFile.Auth0.ClientID,
112-
// configFile.Auth0.UsernameClaim,
113-
// configFile.Auth0.Algorithm)
114-
// if err != nil {
115-
// logrus.Panic(err)
116-
// }
113+
auth0Validator, err := auth.NewAuth0Validator(
114+
configFile.Auth0.Domain,
115+
configFile.Auth0.ClientID,
116+
configFile.Auth0.UsernameClaim,
117+
configFile.Auth0.Algorithm)
118+
if err != nil {
119+
logrus.Panic(err)
120+
}
117121

118122
var (
119-
// userRepo = user.NewRepository(db)
120123
// projectRepo = project.NewRepository(db)
121124
// contractGroupRepo = contractgroup.NewRepository(db)
125+
userRepo = user.NewDynamoRepository(awsSession, viper.GetString("STAGE"), configFile.SenderEmailAddress)
122126
templateRepo = template.NewRepository(awsSession, viper.GetString("STAGE"))
123127
whitelistRepo = whitelist.NewRepository(awsSession, viper.GetString("STAGE"))
128+
companyRepo = company.NewRepository(awsSession, viper.GetString("STAGE"))
124129
)
125130

126131
var (
127132
healthService = health.New(GitHash, BuildStamp)
128133
// projectService = project.NewService(projectRepo)
129134
//contractGroupService = contractgroup.NewService(contractGroupRepo)
130135
// userService = user.NewService(userRepo)
136+
131137
templateService = template.NewService(viper.GetString("STAGE"), templateRepo, docraptorClient, awsSession)
132138
whitelistService = whitelist.NewService(whitelistRepo, http.DefaultClient)
133-
//authorizer = auth.NewAuthorizer(auth0Validator)
139+
companyService = company.NewService(companyRepo, awsSession, configFile.SenderEmailAddress, configFile.CorporateConsoleURL, userRepo)
140+
authorizer = auth.NewAuthorizer(auth0Validator, userRepo)
134141
)
135142

136143
sessionStore, err := dynastore.New(dynastore.Path("/"), dynastore.HTTPOnly(), dynastore.TableName(configFile.SessionStoreTableName), dynastore.DynamoDB(dynamodb.New(awsSession)))
137144
if err != nil {
138145
log.Fatalln(err)
139146
}
140-
141-
//api.OauthSecurityAuth = authorizer.SecurityAuth
147+
api.OauthSecurityAuth = authorizer.SecurityAuth
142148
health.Configure(api, healthService)
143149
template.Configure(api, templateService)
144150
github.Configure(api, configFile.Github.ClientID, configFile.Github.ClientSecret, sessionStore)
145151
whitelist.Configure(api, whitelistService, sessionStore)
152+
153+
company.Configure(api, companyService)
154+
146155
// project.Configure(api, projectService)
147156
// contractgroup.Configure(api, contractGroupService)
148157

cla-backend-go/company/handlers.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package company
2+
3+
import (
4+
"github.com/LF-Engineering/cla-monorepo/cla-backend-go/gen/models"
5+
"github.com/LF-Engineering/cla-monorepo/cla-backend-go/gen/restapi/operations"
6+
"github.com/LF-Engineering/cla-monorepo/cla-backend-go/user"
7+
8+
"github.com/go-openapi/runtime/middleware"
9+
)
10+
11+
func Configure(api *operations.ClaAPI, service service) {
12+
13+
api.AddUsertoCompanyAccessListHandler = operations.AddUsertoCompanyAccessListHandlerFunc(func(params operations.AddUsertoCompanyAccessListParams, claUser *user.CLAUser) middleware.Responder {
14+
err := service.AddUserToCompanyAccessList(params.CompanyID, params.User.InviteID, params.User.UserLFID)
15+
if err != nil {
16+
return operations.NewAddGithubOrganizationFromClaBadRequest()
17+
}
18+
19+
return operations.NewAddUsertoCompanyAccessListOK()
20+
})
21+
22+
api.GetPendingInviteRequestsHandler = operations.GetPendingInviteRequestsHandlerFunc(func(params operations.GetPendingInviteRequestsParams, claUser *user.CLAUser) middleware.Responder {
23+
result, err := service.GetPendingCompanyInviteRequests(params.CompanyID)
24+
if err != nil {
25+
return operations.NewGetPendingInviteRequestsBadRequest().WithPayload(errorResponse(err))
26+
}
27+
28+
return operations.NewGetPendingInviteRequestsOK().WithPayload(result)
29+
})
30+
31+
api.SendInviteRequestHandler = operations.SendInviteRequestHandlerFunc(func(params operations.SendInviteRequestParams, claUser *user.CLAUser) middleware.Responder {
32+
33+
err := service.SendRequestAccessEmail(params.CompanyID, claUser)
34+
if err != nil {
35+
return operations.NewSendInviteRequestBadRequest().WithPayload(errorResponse(err))
36+
}
37+
return operations.NewSendInviteRequestOK()
38+
})
39+
40+
api.DeletePendingInviteHandler = operations.DeletePendingInviteHandlerFunc(func(params operations.DeletePendingInviteParams, claUser *user.CLAUser) middleware.Responder {
41+
err := service.DeletePendingCompanyInviteRequest(params.User.InviteID)
42+
if err != nil {
43+
return operations.NewDeletePendingInviteBadRequest().WithPayload(errorResponse(err))
44+
}
45+
46+
return operations.NewDeletePendingInviteOK()
47+
})
48+
49+
}
50+
51+
type codedResponse interface {
52+
Code() string
53+
}
54+
55+
func errorResponse(err error) *models.ErrorResponse {
56+
code := ""
57+
if e, ok := err.(codedResponse); ok {
58+
code = e.Code()
59+
}
60+
61+
e := models.ErrorResponse{
62+
Code: code,
63+
Message: err.Error(),
64+
}
65+
66+
return &e
67+
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package company
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/aws/aws-sdk-go/aws"
7+
"github.com/aws/aws-sdk-go/aws/session"
8+
"github.com/aws/aws-sdk-go/service/dynamodb"
9+
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
10+
"github.com/gofrs/uuid"
11+
)
12+
13+
type Repository interface {
14+
GetPendingCompanyInviteRequests(companyID string) ([]CompanyInvite, error)
15+
GetCompany(CompanyID string) (Company, error)
16+
DeletePendingCompanyInviteRequest(InviteID string) error
17+
AddPendingCompanyInviteRequest(companyID string, userID string) error
18+
UpdateCompanyAccessList(companyID string, companyACL []string) error
19+
}
20+
21+
type repository struct {
22+
stage string
23+
dynamoDBClient *dynamodb.DynamoDB
24+
}
25+
26+
type Company struct {
27+
CompanyID string `dynamodbav:"company_id"`
28+
CompanyName string `dynamodbav:"company_name"`
29+
CompanyACL []string `dynamodbav:"company_acl"`
30+
}
31+
32+
type CompanyInvite struct {
33+
CompanyInviteID string `dynamodbav:"company_invite_id"`
34+
RequestedCompanyID string `dynamodbav:"requested_company_id"`
35+
UserID string `dynamodbav:"user_id"`
36+
}
37+
38+
func NewRepository(awsSession *session.Session, stage string) repository {
39+
return repository{
40+
stage: stage,
41+
dynamoDBClient: dynamodb.New(awsSession),
42+
}
43+
}
44+
45+
func (repo repository) GetCompany(CompanyID string) (Company, error) {
46+
tableName := fmt.Sprintf("cla-%s-companies", repo.stage)
47+
companyTableData, err := repo.dynamoDBClient.GetItem(&dynamodb.GetItemInput{
48+
TableName: aws.String(tableName),
49+
Key: map[string]*dynamodb.AttributeValue{
50+
"company_id": {
51+
S: aws.String(CompanyID),
52+
},
53+
},
54+
})
55+
if err != nil {
56+
fmt.Println(err.Error())
57+
return Company{}, err
58+
}
59+
60+
company := Company{}
61+
err = dynamodbattribute.UnmarshalMap(companyTableData.Item, &company)
62+
if err != nil {
63+
fmt.Println(err.Error())
64+
return Company{}, err
65+
}
66+
67+
return company, nil
68+
}
69+
70+
func (repo repository) GetPendingCompanyInviteRequests(companyID string) ([]CompanyInvite, error) {
71+
tableName := fmt.Sprintf("cla-%s-company-invites", repo.stage)
72+
input := &dynamodb.QueryInput{
73+
KeyConditions: map[string]*dynamodb.Condition{
74+
"requested_company_id": {
75+
ComparisonOperator: aws.String("EQ"),
76+
AttributeValueList: []*dynamodb.AttributeValue{
77+
{
78+
S: aws.String(companyID),
79+
},
80+
},
81+
},
82+
},
83+
TableName: aws.String(tableName),
84+
IndexName: aws.String("requested-company-index"),
85+
}
86+
companyInviteAV, err := repo.dynamoDBClient.Query(input)
87+
if err != nil {
88+
fmt.Println("Unable to retrieve data from Company-Invites table", err)
89+
return nil, err
90+
}
91+
92+
companyInvites := []CompanyInvite{}
93+
err = dynamodbattribute.UnmarshalListOfMaps(companyInviteAV.Items, &companyInvites)
94+
if err != nil {
95+
fmt.Println(err.Error())
96+
return nil, err
97+
}
98+
99+
return companyInvites, nil
100+
}
101+
102+
func (repo repository) DeletePendingCompanyInviteRequest(inviteID string) error {
103+
tableName := fmt.Sprintf("cla-%s-company-invites", repo.stage)
104+
input := &dynamodb.DeleteItemInput{
105+
Key: map[string]*dynamodb.AttributeValue{
106+
"company_invite_id": {
107+
S: aws.String(inviteID),
108+
},
109+
},
110+
TableName: aws.String(tableName),
111+
}
112+
113+
_, err := repo.dynamoDBClient.DeleteItem(input)
114+
if err != nil {
115+
fmt.Println("Unable to delete Company Invite Request", err)
116+
return err
117+
}
118+
119+
return nil
120+
}
121+
122+
func (repo repository) AddPendingCompanyInviteRequest(companyID string, userID string) error {
123+
companyInviteID, err := uuid.NewV4()
124+
if err != nil {
125+
fmt.Println("Unable to generate a UUID for a pending invite", err)
126+
return err
127+
}
128+
129+
input := &dynamodb.PutItemInput{
130+
Item: map[string]*dynamodb.AttributeValue{
131+
"company_invite_id": {
132+
S: aws.String(companyInviteID.String()),
133+
},
134+
"requested_company_id": {
135+
S: aws.String(companyID),
136+
},
137+
"user_id": {
138+
S: aws.String(userID),
139+
},
140+
},
141+
TableName: aws.String(fmt.Sprintf("cla-%s-company-invites", repo.stage)),
142+
}
143+
144+
_, err = repo.dynamoDBClient.PutItem(input)
145+
if err != nil {
146+
fmt.Println("Unable to create a new pending invite", err)
147+
}
148+
149+
return nil
150+
}
151+
152+
func (repo repository) UpdateCompanyAccessList(companyID string, companyACL []string) error {
153+
tableName := fmt.Sprintf("cla-%s-companies", repo.stage)
154+
input := &dynamodb.UpdateItemInput{
155+
ExpressionAttributeNames: map[string]*string{
156+
"#S": aws.String("company_acl"),
157+
},
158+
ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
159+
":s": {
160+
SS: aws.StringSlice(companyACL),
161+
},
162+
},
163+
TableName: aws.String(tableName),
164+
Key: map[string]*dynamodb.AttributeValue{
165+
"company_id": {
166+
S: aws.String(companyID),
167+
},
168+
},
169+
UpdateExpression: aws.String("SET #S = :s"),
170+
}
171+
172+
_, err := repo.dynamoDBClient.UpdateItem(input)
173+
if err != nil {
174+
fmt.Println("Error updating Company Access List:", err)
175+
return err
176+
}
177+
178+
return nil
179+
}

0 commit comments

Comments
 (0)