diff --git a/management/server/account.go b/management/server/account.go index b376f6f5e..023818c67 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -1188,6 +1188,18 @@ func (am *DefaultAccountManager) GetAccountMeta(ctx context.Context, accountID s return am.Store.GetAccountMeta(ctx, store.LockingStrengthShare, accountID) } +func (am *DefaultAccountManager) GetAccountOnboarding(ctx context.Context, accountID string, userID string) (*types.AccountOnboarding, error) { + allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Accounts, operations.Read) + if err != nil { + return nil, status.NewPermissionValidationError(err) + } + if !allowed { + return nil, status.NewPermissionDeniedError() + } + + return am.Store.GetAccountOnboarding(ctx, store.LockingStrengthShare, accountID) +} + func (am *DefaultAccountManager) GetAccountIDFromUserAuth(ctx context.Context, userAuth nbcontext.UserAuth) (string, string, error) { if userAuth.UserId == "" { return "", "", errors.New(emptyUserID) diff --git a/management/server/account/manager.go b/management/server/account/manager.go index de5031c03..f298bce10 100644 --- a/management/server/account/manager.go +++ b/management/server/account/manager.go @@ -39,6 +39,7 @@ type Manager interface { GetSetupKey(ctx context.Context, accountID, userID, keyID string) (*types.SetupKey, error) GetAccountByID(ctx context.Context, accountID string, userID string) (*types.Account, error) GetAccountMeta(ctx context.Context, accountID string, userID string) (*types.AccountMeta, error) + GetAccountOnboarding(ctx context.Context, accountID string, userID string) (*types.AccountOnboarding, error) AccountExists(ctx context.Context, accountID string) (bool, error) GetAccountIDByUserID(ctx context.Context, userID, domain string) (string, error) GetAccountIDFromUserAuth(ctx context.Context, userAuth nbcontext.UserAuth) (string, string, error) diff --git a/management/server/http/api/openapi.yml b/management/server/http/api/openapi.yml index 1c5ca9b04..f8c2b9854 100644 --- a/management/server/http/api/openapi.yml +++ b/management/server/http/api/openapi.yml @@ -60,6 +60,8 @@ components: description: Account creator type: string example: google-oauth2|277474792786460067937 + onboarding: + $ref: '#/components/schemas/AccountOnboarding' required: - id - settings @@ -67,6 +69,21 @@ components: - domain_category - created_at - created_by + - onboarding + AccountOnboarding: + type: object + properties: + signup_form_pending: + description: Indicates whether the account signup form is pending + type: boolean + example: true + onboarding_flow_pending: + description: Indicates whether the account onboarding flow is pending + type: boolean + example: false + required: + - signup_form_pending + - onboarding_flow_pending AccountSettings: type: object properties: @@ -153,6 +170,8 @@ components: properties: settings: $ref: '#/components/schemas/AccountSettings' + onboarding: + $ref: '#/components/schemas/AccountOnboarding' required: - settings User: diff --git a/management/server/http/api/types.gen.go b/management/server/http/api/types.gen.go index d27fd2a57..a9f17aab4 100644 --- a/management/server/http/api/types.gen.go +++ b/management/server/http/api/types.gen.go @@ -250,8 +250,9 @@ type Account struct { DomainCategory string `json:"domain_category"` // Id Account ID - Id string `json:"id"` - Settings AccountSettings `json:"settings"` + Id string `json:"id"` + Onboarding AccountOnboarding `json:"onboarding"` + Settings AccountSettings `json:"settings"` } // AccountExtraSettings defines model for AccountExtraSettings. @@ -266,9 +267,19 @@ type AccountExtraSettings struct { PeerApprovalEnabled bool `json:"peer_approval_enabled"` } +// AccountOnboarding defines model for AccountOnboarding. +type AccountOnboarding struct { + // OnboardingFlowPending Indicates whether the account onboarding flow is pending + OnboardingFlowPending bool `json:"onboarding_flow_pending"` + + // SignupFormPending Indicates whether the account signup form is pending + SignupFormPending bool `json:"signup_form_pending"` +} + // AccountRequest defines model for AccountRequest. type AccountRequest struct { - Settings AccountSettings `json:"settings"` + Onboarding *AccountOnboarding `json:"onboarding,omitempty"` + Settings AccountSettings `json:"settings"` } // AccountSettings defines model for AccountSettings. diff --git a/management/server/http/handlers/accounts/accounts_handler.go b/management/server/http/handlers/accounts/accounts_handler.go index dfc782b3f..84728c48d 100644 --- a/management/server/http/handlers/accounts/accounts_handler.go +++ b/management/server/http/handlers/accounts/accounts_handler.go @@ -59,7 +59,13 @@ func (h *handler) getAllAccounts(w http.ResponseWriter, r *http.Request) { return } - resp := toAccountResponse(accountID, settings, meta) + onboarding, err := h.accountManager.GetAccountOnboarding(r.Context(), accountID, userID) + if err != nil { + util.WriteError(r.Context(), err, w) + return + } + + resp := toAccountResponse(accountID, settings, meta, onboarding) util.WriteJSONObject(r.Context(), w, []*api.Account{resp}) } @@ -126,7 +132,12 @@ func (h *handler) updateAccount(w http.ResponseWriter, r *http.Request) { settings.LazyConnectionEnabled = *req.Settings.LazyConnectionEnabled } - updatedSettings, err := h.accountManager.UpdateAccountSettings(r.Context(), accountID, userID, settings) + onboarding := &types.AccountOnboarding{ + OnboardingFlowPending: req.Onboarding.OnboardingFlowPending, + SignupFormPending: req.Onboarding.SignupFormPending, + } + + updatedSettings, err := h.accountManager.UpdateAccountSettings(r.Context(), accountID, userID, settings, onboarding) if err != nil { util.WriteError(r.Context(), err, w) return @@ -138,7 +149,13 @@ func (h *handler) updateAccount(w http.ResponseWriter, r *http.Request) { return } - resp := toAccountResponse(accountID, updatedSettings, meta) + onboarding, err := h.accountManager.GetAccountOnboarding(r.Context(), accountID, userID) + if err != nil { + util.WriteError(r.Context(), err, w) + return + } + + resp := toAccountResponse(accountID, updatedSettings, meta, onboarding) util.WriteJSONObject(r.Context(), w, &resp) } @@ -167,7 +184,7 @@ func (h *handler) deleteAccount(w http.ResponseWriter, r *http.Request) { util.WriteJSONObject(r.Context(), w, util.EmptyObject{}) } -func toAccountResponse(accountID string, settings *types.Settings, meta *types.AccountMeta) *api.Account { +func toAccountResponse(accountID string, settings *types.Settings, meta *types.AccountMeta, onboarding *types.AccountOnboarding) *api.Account { jwtAllowGroups := settings.JWTAllowGroups if jwtAllowGroups == nil { jwtAllowGroups = []string{} @@ -188,6 +205,11 @@ func toAccountResponse(accountID string, settings *types.Settings, meta *types.A DnsDomain: &settings.DNSDomain, } + apiOnboarding := api.AccountOnboarding{ + OnboardingFlowPending: onboarding.OnboardingFlowPending, + SignupFormPending: onboarding.SignupFormPending, + } + if settings.Extra != nil { apiSettings.Extra = &api.AccountExtraSettings{ PeerApprovalEnabled: settings.Extra.PeerApprovalEnabled, @@ -203,5 +225,6 @@ func toAccountResponse(accountID string, settings *types.Settings, meta *types.A CreatedBy: meta.CreatedBy, Domain: meta.Domain, DomainCategory: meta.DomainCategory, + Onboarding: apiOnboarding, } } diff --git a/management/server/mock_server/account_mock.go b/management/server/mock_server/account_mock.go index 3caa6744a..723f64b69 100644 --- a/management/server/mock_server/account_mock.go +++ b/management/server/mock_server/account_mock.go @@ -117,6 +117,7 @@ type MockAccountManager struct { GetOwnerInfoFunc func(ctx context.Context, accountID string) (*types.UserInfo, error) GetCurrentUserInfoFunc func(ctx context.Context, userAuth nbcontext.UserAuth) (*users.UserInfoWithPermissions, error) GetAccountMetaFunc func(ctx context.Context, accountID, userID string) (*types.AccountMeta, error) + GetAccountOnboardingFunc func(ctx context.Context, accountID, userID string) (*types.AccountOnboarding, error) GetOrCreateAccountByPrivateDomainFunc func(ctx context.Context, initiatorId, domain string) (*types.Account, bool, error) } @@ -814,6 +815,14 @@ func (am *MockAccountManager) GetAccountMeta(ctx context.Context, accountID stri return nil, status.Errorf(codes.Unimplemented, "method GetAccountMeta is not implemented") } +// GetAccountOnboarding mocks GetAccountOnboarding of the AccountManager interface +func (am *MockAccountManager) GetAccountOnboarding(ctx context.Context, accountID string, userID string) (*types.AccountOnboarding, error) { + if am.GetAccountOnboardingFunc != nil { + return am.GetAccountOnboardingFunc(ctx, accountID, userID) + } + return nil, status.Errorf(codes.Unimplemented, "method GetAccountOnboarding is not implemented") +} + // GetUserByID mocks GetUserByID of the AccountManager interface func (am *MockAccountManager) GetUserByID(ctx context.Context, id string) (*types.User, error) { if am.GetUserByIDFunc != nil { diff --git a/management/server/types/account.go b/management/server/types/account.go index 090ba76e4..ac05ee363 100644 --- a/management/server/types/account.go +++ b/management/server/types/account.go @@ -87,6 +87,7 @@ type Account struct { Networks []*networkTypes.Network `gorm:"foreignKey:AccountID;references:id"` NetworkRouters []*routerTypes.NetworkRouter `gorm:"foreignKey:AccountID;references:id"` NetworkResources []*resourceTypes.NetworkResource `gorm:"foreignKey:AccountID;references:id"` + Onboarding AccountOnboarding } // Subclass used in gorm to only load network and not whole account @@ -104,6 +105,14 @@ type AccountSettings struct { Settings *Settings `gorm:"embedded;embeddedPrefix:settings_"` } +type AccountOnboarding struct { + AccountID string `gorm:"primaryKey"` + OnboardingFlowPending bool + SignupFormPending bool + CreatedAt time.Time + UpdatedAt time.Time +} + // GetRoutesToSync returns the enabled routes for the peer ID and the routes // from the ACL peers that have distribution groups associated with the peer ID. // Please mind, that the returned route.Route objects will contain Peer.Key instead of Peer.ID. @@ -866,6 +875,7 @@ func (a *Account) Copy() *Account { Networks: nets, NetworkRouters: networkRouters, NetworkResources: networkResources, + Onboarding: a.Onboarding, } }