Specify invited by email when inviting a user (#1087)

This commit is contained in:
Misha Bragin 2023-08-16 23:05:22 +02:00 committed by GitHub
parent 4572c6c1f8
commit 8e3bcd57a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 22 additions and 14 deletions

View File

@ -461,7 +461,7 @@ func (am *Auth0Manager) UpdateUserAppMetadata(userID string, appMetadata AppMeta
return nil return nil
} }
func buildCreateUserRequestPayload(email string, name string, accountID string) (string, error) { func buildCreateUserRequestPayload(email, name, accountID, invitedByEmail string) (string, error) {
invite := true invite := true
req := &createUserRequest{ req := &createUserRequest{
Email: email, Email: email,
@ -469,6 +469,7 @@ func buildCreateUserRequestPayload(email string, name string, accountID string)
AppMeta: AppMetadata{ AppMeta: AppMetadata{
WTAccountID: accountID, WTAccountID: accountID,
WTPendingInvite: &invite, WTPendingInvite: &invite,
WTInvitedBy: invitedByEmail,
}, },
Connection: "Username-Password-Authentication", Connection: "Username-Password-Authentication",
Password: GeneratePassword(8, 1, 1, 1), Password: GeneratePassword(8, 1, 1, 1),
@ -634,9 +635,9 @@ func (am *Auth0Manager) GetUserByEmail(email string) ([]*UserData, error) {
} }
// CreateUser creates a new user in Auth0 Idp and sends an invite // CreateUser creates a new user in Auth0 Idp and sends an invite
func (am *Auth0Manager) CreateUser(email string, name string, accountID string) (*UserData, error) { func (am *Auth0Manager) CreateUser(email, name, accountID, invitedByEmail string) (*UserData, error) {
payloadString, err := buildCreateUserRequestPayload(email, name, accountID) payloadString, err := buildCreateUserRequestPayload(email, name, accountID, invitedByEmail)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -343,7 +343,7 @@ func TestAuth0_UpdateUserAppMetadata(t *testing.T) {
updateUserAppMetadataTestCase2 := updateUserAppMetadataTest{ updateUserAppMetadataTestCase2 := updateUserAppMetadataTest{
name: "Bad Status Code", name: "Bad Status Code",
inputReqBody: fmt.Sprintf("{\"access_token\":\"%s\",\"scope\":\"read:users\",\"expires_in\":%d,\"token_type\":\"Bearer\"}", token, exp), inputReqBody: fmt.Sprintf("{\"access_token\":\"%s\",\"scope\":\"read:users\",\"expires_in\":%d,\"token_type\":\"Bearer\"}", token, exp),
expectedReqBody: fmt.Sprintf("{\"app_metadata\":{\"wt_account_id\":\"%s\",\"wt_pending_invite\":null}}", appMetadata.WTAccountID), expectedReqBody: fmt.Sprintf("{\"app_metadata\":{\"wt_account_id\":\"%s\",\"wt_pending_invite\":null,\"wt_invited_by_email\":\"\"}}", appMetadata.WTAccountID),
appMetadata: appMetadata, appMetadata: appMetadata,
statusCode: 400, statusCode: 400,
helper: JsonParser{}, helper: JsonParser{},
@ -366,7 +366,7 @@ func TestAuth0_UpdateUserAppMetadata(t *testing.T) {
updateUserAppMetadataTestCase4 := updateUserAppMetadataTest{ updateUserAppMetadataTestCase4 := updateUserAppMetadataTest{
name: "Good request", name: "Good request",
inputReqBody: fmt.Sprintf("{\"access_token\":\"%s\",\"scope\":\"read:users\",\"expires_in\":%d,\"token_type\":\"Bearer\"}", token, exp), inputReqBody: fmt.Sprintf("{\"access_token\":\"%s\",\"scope\":\"read:users\",\"expires_in\":%d,\"token_type\":\"Bearer\"}", token, exp),
expectedReqBody: fmt.Sprintf("{\"app_metadata\":{\"wt_account_id\":\"%s\",\"wt_pending_invite\":null}}", appMetadata.WTAccountID), expectedReqBody: fmt.Sprintf("{\"app_metadata\":{\"wt_account_id\":\"%s\",\"wt_pending_invite\":null,\"wt_invited_by_email\":\"\"}}", appMetadata.WTAccountID),
appMetadata: appMetadata, appMetadata: appMetadata,
statusCode: 200, statusCode: 200,
helper: JsonParser{}, helper: JsonParser{},
@ -378,7 +378,7 @@ func TestAuth0_UpdateUserAppMetadata(t *testing.T) {
updateUserAppMetadataTestCase5 := updateUserAppMetadataTest{ updateUserAppMetadataTestCase5 := updateUserAppMetadataTest{
name: "Update Pending Invite", name: "Update Pending Invite",
inputReqBody: fmt.Sprintf("{\"access_token\":\"%s\",\"scope\":\"read:users\",\"expires_in\":%d,\"token_type\":\"Bearer\"}", token, exp), inputReqBody: fmt.Sprintf("{\"access_token\":\"%s\",\"scope\":\"read:users\",\"expires_in\":%d,\"token_type\":\"Bearer\"}", token, exp),
expectedReqBody: fmt.Sprintf("{\"app_metadata\":{\"wt_account_id\":\"%s\",\"wt_pending_invite\":true}}", appMetadata.WTAccountID), expectedReqBody: fmt.Sprintf("{\"app_metadata\":{\"wt_account_id\":\"%s\",\"wt_pending_invite\":true,\"wt_invited_by_email\":\"\"}}", appMetadata.WTAccountID),
appMetadata: AppMetadata{ appMetadata: AppMetadata{
WTAccountID: "ok", WTAccountID: "ok",
WTPendingInvite: &invite, WTPendingInvite: &invite,

View File

@ -362,7 +362,7 @@ func (am *AuthentikManager) GetAllAccounts() (map[string][]*UserData, error) {
} }
// CreateUser creates a new user in authentik Idp and sends an invitation. // CreateUser creates a new user in authentik Idp and sends an invitation.
func (am *AuthentikManager) CreateUser(email string, name string, accountID string) (*UserData, error) { func (am *AuthentikManager) CreateUser(email, name, accountID, invitedByEmail string) (*UserData, error) {
ctx, err := am.authenticationContext() ctx, err := am.authenticationContext()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -236,7 +236,7 @@ func (ac *AzureCredentials) Authenticate() (JWTToken, error) {
} }
// CreateUser creates a new user in azure AD Idp. // CreateUser creates a new user in azure AD Idp.
func (am *AzureManager) CreateUser(email string, name string, accountID string) (*UserData, error) { func (am *AzureManager) CreateUser(email, name, accountID, invitedByEmail string) (*UserData, error) {
payload, err := buildAzureCreateUserRequestPayload(email, name, accountID, am.ClientID) payload, err := buildAzureCreateUserRequestPayload(email, name, accountID, am.ClientID)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -185,7 +185,7 @@ func (gm *GoogleWorkspaceManager) GetAllAccounts() (map[string][]*UserData, erro
} }
// CreateUser creates a new user in Google Workspace and sends an invitation. // CreateUser creates a new user in Google Workspace and sends an invitation.
func (gm *GoogleWorkspaceManager) CreateUser(email string, name string, accountID string) (*UserData, error) { func (gm *GoogleWorkspaceManager) CreateUser(email, name, accountID, invitedByEmail string) (*UserData, error) {
invite := true invite := true
metadata := AppMetadata{ metadata := AppMetadata{
WTAccountID: accountID, WTAccountID: accountID,

View File

@ -15,7 +15,7 @@ type Manager interface {
GetUserDataByID(userId string, appMetadata AppMetadata) (*UserData, error) GetUserDataByID(userId string, appMetadata AppMetadata) (*UserData, error)
GetAccount(accountId string) ([]*UserData, error) GetAccount(accountId string) ([]*UserData, error)
GetAllAccounts() (map[string][]*UserData, error) GetAllAccounts() (map[string][]*UserData, error)
CreateUser(email string, name string, accountID string) (*UserData, error) CreateUser(email, name, accountID, invitedByEmail string) (*UserData, error)
GetUserByEmail(email string) ([]*UserData, error) GetUserByEmail(email string) ([]*UserData, error)
InviteUserByID(userID string) error InviteUserByID(userID string) error
} }
@ -72,6 +72,7 @@ type AppMetadata struct {
// maps to wt_account_id when json.marshal // maps to wt_account_id when json.marshal
WTAccountID string `json:"wt_account_id,omitempty"` WTAccountID string `json:"wt_account_id,omitempty"`
WTPendingInvite *bool `json:"wt_pending_invite"` WTPendingInvite *bool `json:"wt_pending_invite"`
WTInvitedBy string `json:"wt_invited_by_email"`
} }
// JWTToken a JWT object that holds information of a token // JWTToken a JWT object that holds information of a token

View File

@ -230,7 +230,7 @@ func (kc *KeycloakCredentials) Authenticate() (JWTToken, error) {
} }
// CreateUser creates a new user in keycloak Idp and sends an invite. // CreateUser creates a new user in keycloak Idp and sends an invite.
func (km *KeycloakManager) CreateUser(email string, name string, accountID string) (*UserData, error) { func (km *KeycloakManager) CreateUser(email, name, accountID, invitedByEmail string) (*UserData, error) {
jwtToken, err := km.credentials.Authenticate() jwtToken, err := km.credentials.Authenticate()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -103,7 +103,7 @@ func (oc *OktaCredentials) Authenticate() (JWTToken, error) {
} }
// CreateUser creates a new user in okta Idp and sends an invitation. // CreateUser creates a new user in okta Idp and sends an invitation.
func (om *OktaManager) CreateUser(email string, name string, accountID string) (*UserData, error) { func (om *OktaManager) CreateUser(email, name, accountID, invitedByEmail string) (*UserData, error) {
var ( var (
sendEmail = true sendEmail = true
activate = true activate = true

View File

@ -234,7 +234,7 @@ func (zc *ZitadelCredentials) Authenticate() (JWTToken, error) {
} }
// CreateUser creates a new user in zitadel Idp and sends an invite. // CreateUser creates a new user in zitadel Idp and sends an invite.
func (zm *ZitadelManager) CreateUser(email string, name string, accountID string) (*UserData, error) { func (zm *ZitadelManager) CreateUser(email, name, accountID, invitedByEmail string) (*UserData, error) {
payload, err := buildZitadelCreateUserRequestPayload(email, name) payload, err := buildZitadelCreateUserRequestPayload(email, name)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -215,6 +215,12 @@ func (am *DefaultAccountManager) inviteNewUser(accountID, userID string, invite
return nil, status.Errorf(status.NotFound, "account %s doesn't exist", accountID) return nil, status.Errorf(status.NotFound, "account %s doesn't exist", accountID)
} }
// initiator is the one who is inviting the new user
initiatorUser, err := am.lookupUserInCache(userID, account)
if err != nil {
return nil, status.Errorf(status.NotFound, "user %s doesn't exist in IdP", userID)
}
// check if the user is already registered with this email => reject // check if the user is already registered with this email => reject
user, err := am.lookupUserInCacheByEmail(invite.Email, accountID) user, err := am.lookupUserInCacheByEmail(invite.Email, accountID)
if err != nil { if err != nil {
@ -234,7 +240,7 @@ func (am *DefaultAccountManager) inviteNewUser(accountID, userID string, invite
return nil, status.Errorf(status.UserAlreadyExists, "can't invite a user with an existing NetBird account") return nil, status.Errorf(status.UserAlreadyExists, "can't invite a user with an existing NetBird account")
} }
idpUser, err := am.idpManager.CreateUser(invite.Email, invite.Name, accountID) idpUser, err := am.idpManager.CreateUser(invite.Email, invite.Name, accountID, initiatorUser.Email)
if err != nil { if err != nil {
return nil, err return nil, err
} }