diff --git a/management/server/idp/auth0.go b/management/server/idp/auth0.go index 517e169d0..64ec88e9f 100644 --- a/management/server/idp/auth0.go +++ b/management/server/idp/auth0.go @@ -461,7 +461,7 @@ func (am *Auth0Manager) UpdateUserAppMetadata(userID string, appMetadata AppMeta return nil } -func buildCreateUserRequestPayload(email string, name string, accountID string) (string, error) { +func buildCreateUserRequestPayload(email, name, accountID, invitedByEmail string) (string, error) { invite := true req := &createUserRequest{ Email: email, @@ -469,6 +469,7 @@ func buildCreateUserRequestPayload(email string, name string, accountID string) AppMeta: AppMetadata{ WTAccountID: accountID, WTPendingInvite: &invite, + WTInvitedBy: invitedByEmail, }, Connection: "Username-Password-Authentication", 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 -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 { return nil, err } diff --git a/management/server/idp/auth0_test.go b/management/server/idp/auth0_test.go index fecee936b..0814b4b69 100644 --- a/management/server/idp/auth0_test.go +++ b/management/server/idp/auth0_test.go @@ -343,7 +343,7 @@ func TestAuth0_UpdateUserAppMetadata(t *testing.T) { updateUserAppMetadataTestCase2 := updateUserAppMetadataTest{ name: "Bad Status Code", 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, statusCode: 400, helper: JsonParser{}, @@ -366,7 +366,7 @@ func TestAuth0_UpdateUserAppMetadata(t *testing.T) { updateUserAppMetadataTestCase4 := updateUserAppMetadataTest{ name: "Good request", 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, statusCode: 200, helper: JsonParser{}, @@ -378,7 +378,7 @@ func TestAuth0_UpdateUserAppMetadata(t *testing.T) { updateUserAppMetadataTestCase5 := updateUserAppMetadataTest{ name: "Update Pending Invite", 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{ WTAccountID: "ok", WTPendingInvite: &invite, diff --git a/management/server/idp/authentik.go b/management/server/idp/authentik.go index 396d390e2..586348fee 100644 --- a/management/server/idp/authentik.go +++ b/management/server/idp/authentik.go @@ -362,7 +362,7 @@ func (am *AuthentikManager) GetAllAccounts() (map[string][]*UserData, error) { } // 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() if err != nil { return nil, err diff --git a/management/server/idp/azure.go b/management/server/idp/azure.go index b70e87be1..7cff7d8fc 100644 --- a/management/server/idp/azure.go +++ b/management/server/idp/azure.go @@ -236,7 +236,7 @@ func (ac *AzureCredentials) Authenticate() (JWTToken, error) { } // 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) if err != nil { return nil, err diff --git a/management/server/idp/google_workspace.go b/management/server/idp/google_workspace.go index 9a5d73f75..efe457fdd 100644 --- a/management/server/idp/google_workspace.go +++ b/management/server/idp/google_workspace.go @@ -185,7 +185,7 @@ func (gm *GoogleWorkspaceManager) GetAllAccounts() (map[string][]*UserData, erro } // 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 metadata := AppMetadata{ WTAccountID: accountID, diff --git a/management/server/idp/idp.go b/management/server/idp/idp.go index a4d7c9bdf..48afd5c32 100644 --- a/management/server/idp/idp.go +++ b/management/server/idp/idp.go @@ -15,7 +15,7 @@ type Manager interface { GetUserDataByID(userId string, appMetadata AppMetadata) (*UserData, error) GetAccount(accountId 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) InviteUserByID(userID string) error } @@ -72,6 +72,7 @@ type AppMetadata struct { // maps to wt_account_id when json.marshal WTAccountID string `json:"wt_account_id,omitempty"` WTPendingInvite *bool `json:"wt_pending_invite"` + WTInvitedBy string `json:"wt_invited_by_email"` } // JWTToken a JWT object that holds information of a token diff --git a/management/server/idp/keycloak.go b/management/server/idp/keycloak.go index d44396571..12ed87389 100644 --- a/management/server/idp/keycloak.go +++ b/management/server/idp/keycloak.go @@ -230,7 +230,7 @@ func (kc *KeycloakCredentials) Authenticate() (JWTToken, error) { } // 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() if err != nil { return nil, err diff --git a/management/server/idp/okta.go b/management/server/idp/okta.go index 9d00bc6ee..c6b5055d4 100644 --- a/management/server/idp/okta.go +++ b/management/server/idp/okta.go @@ -103,7 +103,7 @@ func (oc *OktaCredentials) Authenticate() (JWTToken, error) { } // 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 ( sendEmail = true activate = true diff --git a/management/server/idp/zitadel.go b/management/server/idp/zitadel.go index 4de5659be..fce2c7b37 100644 --- a/management/server/idp/zitadel.go +++ b/management/server/idp/zitadel.go @@ -234,7 +234,7 @@ func (zc *ZitadelCredentials) Authenticate() (JWTToken, error) { } // 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) if err != nil { return nil, err diff --git a/management/server/user.go b/management/server/user.go index 34a879328..0b61dd60a 100644 --- a/management/server/user.go +++ b/management/server/user.go @@ -215,6 +215,12 @@ func (am *DefaultAccountManager) inviteNewUser(accountID, userID string, invite 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 user, err := am.lookupUserInCacheByEmail(invite.Email, accountID) 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") } - 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 { return nil, err }