diff --git a/management/server/account.go b/management/server/account.go index aab198342..efdfc6de6 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -126,6 +126,7 @@ type AccountManager interface { SavePostureChecks(accountID, userID string, postureChecks *posture.Checks) error DeletePostureChecks(accountID, postureChecksID, userID string) error ListPostureChecks(accountID, userID string) ([]*posture.Checks, error) + GetIdpManager() idp.Manager } type DefaultAccountManager struct { @@ -900,6 +901,10 @@ func (am *DefaultAccountManager) GetExternalCacheManager() ExternalCacheManager return am.externalCacheManager } +func (am *DefaultAccountManager) GetIdpManager() idp.Manager { + return am.idpManager +} + // UpdateAccountSettings updates Account settings. // Only users with role UserRoleAdmin can update the account. // User that performs the update has to belong to the account. diff --git a/management/server/idp/auth0.go b/management/server/idp/auth0.go index 745136f62..34a5c0de5 100644 --- a/management/server/idp/auth0.go +++ b/management/server/idp/auth0.go @@ -114,6 +114,22 @@ type auth0Profile struct { LastLogin string `json:"last_login"` } +// Connections represents a single Auth0 connection +// https://auth0.com/docs/api/management/v2/connections/get-connections +type Connection struct { + Id string `json:"id"` + Name string `json:"name"` + DisplayName string `json:"display_name"` + IsDomainConnection bool `json:"is_domain_connection"` + Realms []string `json:"realms"` + Metadata map[string]string `json:"metadata"` + Options ConnectionOptions `json:"options"` +} + +type ConnectionOptions struct { + DomainAliases []string `json:"domain_aliases"` +} + // NewAuth0Manager creates a new instance of the Auth0Manager func NewAuth0Manager(config Auth0ClientConfig, appMetrics telemetry.AppMetrics) (*Auth0Manager, error) { httpTransport := http.DefaultTransport.(*http.Transport).Clone() @@ -581,13 +597,13 @@ func (am *Auth0Manager) GetAllAccounts() (map[string][]*UserData, error) { body, err := io.ReadAll(jobResp.Body) if err != nil { - log.Debugf("Coudln't read export job response; %v", err) + log.Debugf("Couldn't read export job response; %v", err) return nil, err } err = am.helper.Unmarshal(body, &exportJobResp) if err != nil { - log.Debugf("Coudln't unmarshal export job response; %v", err) + log.Debugf("Couldn't unmarshal export job response; %v", err) return nil, err } @@ -635,7 +651,7 @@ func (am *Auth0Manager) GetUserByEmail(email string) ([]*UserData, error) { err = am.helper.Unmarshal(body, &userResp) if err != nil { - log.Debugf("Coudln't unmarshal export job response; %v", err) + log.Debugf("Couldn't unmarshal export job response; %v", err) return nil, err } @@ -684,13 +700,13 @@ func (am *Auth0Manager) CreateUser(email, name, accountID, invitedByEmail string body, err := io.ReadAll(resp.Body) if err != nil { - log.Debugf("Coudln't read export job response; %v", err) + log.Debugf("Couldn't read export job response; %v", err) return nil, err } err = am.helper.Unmarshal(body, &createResp) if err != nil { - log.Debugf("Coudln't unmarshal export job response; %v", err) + log.Debugf("Couldn't unmarshal export job response; %v", err) return nil, err } @@ -777,6 +793,56 @@ func (am *Auth0Manager) DeleteUser(userID string) error { return nil } +// GetAllConnections returns detailed list of all connections filtered by given params. +// Note this method is not part of the IDP Manager interface as this is Auth0 specific. +func (am *Auth0Manager) GetAllConnections(strategy []string) ([]Connection, error) { + var connections []Connection + + q := make(url.Values) + q.Set("strategy", strings.Join(strategy, ",")) + + req, err := am.createRequest(http.MethodGet, "/api/v2/connections?"+q.Encode(), nil) + if err != nil { + return connections, err + } + + resp, err := am.httpClient.Do(req) + if err != nil { + log.Debugf("execute get connections request: %v", err) + if am.appMetrics != nil { + am.appMetrics.IDPMetrics().CountRequestError() + } + return connections, err + } + + defer func() { + err = resp.Body.Close() + if err != nil { + log.Errorf("close get connections request body: %v", err) + } + }() + if resp.StatusCode != 200 { + if am.appMetrics != nil { + am.appMetrics.IDPMetrics().CountRequestStatusError() + } + return connections, fmt.Errorf("unable to get connections, statusCode %d", resp.StatusCode) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Debugf("Couldn't read get connections response; %v", err) + return connections, err + } + + err = am.helper.Unmarshal(body, &connections) + if err != nil { + log.Debugf("Couldn't unmarshal get connection response; %v", err) + return connections, err + } + + return connections, err +} + // checkExportJobStatus checks the status of the job created at CreateExportUsersJob. // If the status is "completed", then return the downloadLink func (am *Auth0Manager) checkExportJobStatus(jobID string) (bool, string, error) { diff --git a/management/server/mock_server/account_mock.go b/management/server/mock_server/account_mock.go index 7d4161d3b..848bad2f5 100644 --- a/management/server/mock_server/account_mock.go +++ b/management/server/mock_server/account_mock.go @@ -11,6 +11,7 @@ import ( nbdns "github.com/netbirdio/netbird/dns" "github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server/activity" + "github.com/netbirdio/netbird/management/server/idp" "github.com/netbirdio/netbird/management/server/jwtclaims" nbpeer "github.com/netbirdio/netbird/management/server/peer" "github.com/netbirdio/netbird/management/server/posture" @@ -93,6 +94,7 @@ type MockAccountManager struct { DeletePostureChecksFunc func(accountID, postureChecksID, userID string) error ListPostureChecksFunc func(accountID, userID string) ([]*posture.Checks, error) GetUsageFunc func(ctx context.Context, accountID string, start, end time.Time) (*server.AccountUsageStats, error) + GetIdpManagerFunc func() idp.Manager } // GetUsersFromAccount mock implementation of GetUsersFromAccount from server.AccountManager interface @@ -705,10 +707,18 @@ func (am *MockAccountManager) ListPostureChecks(accountID, userID string) ([]*po return nil, status.Errorf(codes.Unimplemented, "method ListPostureChecks is not implemented") } -// GetUsage mocks GetCurrentUsage of the AccountManager interface +// GetUsage mocks GetUsage of the AccountManager interface func (am *MockAccountManager) GetUsage(ctx context.Context, accountID string, start time.Time, end time.Time) (*server.AccountUsageStats, error) { if am.GetUsageFunc != nil { return am.GetUsageFunc(ctx, accountID, start, end) } return nil, status.Errorf(codes.Unimplemented, "method GetUsage is not implemented") } + +// GetIdpManager mocks GetIdpManager of the AccountManager interface +func (am *MockAccountManager) GetIdpManager() idp.Manager { + if am.GetIdpManagerFunc != nil { + return am.GetIdpManagerFunc() + } + return nil +}