mirror of
https://github.com/netbirdio/netbird.git
synced 2025-06-20 17:58:02 +02:00
[management] Add account meta (#3724)
This commit is contained in:
parent
986eb8c1e0
commit
c69df13515
@ -1057,6 +1057,19 @@ func (am *DefaultAccountManager) GetAccountByID(ctx context.Context, accountID s
|
||||
return am.Store.GetAccount(ctx, accountID)
|
||||
}
|
||||
|
||||
// GetAccountMeta returns the account metadata associated with this account ID.
|
||||
func (am *DefaultAccountManager) GetAccountMeta(ctx context.Context, accountID string, userID string) (*types.AccountMeta, 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.GetAccountMeta(ctx, store.LockingStrengthShare, accountID)
|
||||
}
|
||||
|
||||
func (am *DefaultAccountManager) GetAccountIDFromUserAuth(ctx context.Context, userAuth nbcontext.UserAuth) (string, string, error) {
|
||||
if userAuth.UserId == "" {
|
||||
return "", "", errors.New(emptyUserID)
|
||||
|
@ -37,6 +37,7 @@ type Manager interface {
|
||||
SaveOrAddUsers(ctx context.Context, accountID, initiatorUserID string, updates []*types.User, addIfNotExists bool) ([]*types.UserInfo, error)
|
||||
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)
|
||||
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)
|
||||
|
@ -43,9 +43,30 @@ components:
|
||||
example: ch8i4ug6lnn4g9hqv7l0
|
||||
settings:
|
||||
$ref: '#/components/schemas/AccountSettings'
|
||||
domain:
|
||||
description: Account domain
|
||||
type: string
|
||||
example: netbird.io
|
||||
domain_category:
|
||||
description: Account domain category
|
||||
type: string
|
||||
example: private
|
||||
created_at:
|
||||
description: Account creation date (UTC)
|
||||
type: string
|
||||
format: date-time
|
||||
example: "2023-05-05T09:00:35.477782Z"
|
||||
created_by:
|
||||
description: Account creator
|
||||
type: string
|
||||
example: google-oauth2|277474792786460067937
|
||||
required:
|
||||
- id
|
||||
- settings
|
||||
- domain
|
||||
- domain_category
|
||||
- created_at
|
||||
- created_by
|
||||
AccountSettings:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -223,6 +223,18 @@ type AccessiblePeer struct {
|
||||
|
||||
// Account defines model for Account.
|
||||
type Account struct {
|
||||
// CreatedAt Account creation date (UTC)
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
||||
// CreatedBy Account creator
|
||||
CreatedBy string `json:"created_by"`
|
||||
|
||||
// Domain Account domain
|
||||
Domain string `json:"domain"`
|
||||
|
||||
// DomainCategory Account domain category
|
||||
DomainCategory string `json:"domain_category"`
|
||||
|
||||
// Id Account ID
|
||||
Id string `json:"id"`
|
||||
Settings AccountSettings `json:"settings"`
|
||||
|
@ -47,13 +47,19 @@ func (h *handler) getAllAccounts(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
accountID, userID := userAuth.AccountId, userAuth.UserId
|
||||
|
||||
meta, err := h.accountManager.GetAccountMeta(r.Context(), accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
settings, err := h.settingsManager.GetSettings(r.Context(), accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
resp := toAccountResponse(accountID, settings)
|
||||
resp := toAccountResponse(accountID, settings, meta)
|
||||
util.WriteJSONObject(r.Context(), w, []*api.Account{resp})
|
||||
}
|
||||
|
||||
@ -120,7 +126,13 @@ func (h *handler) updateAccount(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
resp := toAccountResponse(updatedAccount.Id, updatedAccount.Settings)
|
||||
meta, err := h.accountManager.GetAccountMeta(r.Context(), accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
resp := toAccountResponse(updatedAccount.Id, updatedAccount.Settings, meta)
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, &resp)
|
||||
}
|
||||
@ -149,7 +161,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) *api.Account {
|
||||
func toAccountResponse(accountID string, settings *types.Settings, meta *types.AccountMeta) *api.Account {
|
||||
jwtAllowGroups := settings.JWTAllowGroups
|
||||
if jwtAllowGroups == nil {
|
||||
jwtAllowGroups = []string{}
|
||||
@ -179,5 +191,9 @@ func toAccountResponse(accountID string, settings *types.Settings) *api.Account
|
||||
return &api.Account{
|
||||
Id: accountID,
|
||||
Settings: apiSettings,
|
||||
CreatedAt: meta.CreatedAt,
|
||||
CreatedBy: meta.CreatedBy,
|
||||
Domain: meta.Domain,
|
||||
DomainCategory: meta.DomainCategory,
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,12 @@ func initAccountsTestData(t *testing.T, account *types.Account) *handler {
|
||||
accCopy.UpdateSettings(newSettings)
|
||||
return accCopy, nil
|
||||
},
|
||||
GetAccountByIDFunc: func(ctx context.Context, accountID string, userID string) (*types.Account, error) {
|
||||
return account.Copy(), nil
|
||||
},
|
||||
GetAccountMetaFunc: func(ctx context.Context, accountID string, userID string) (*types.AccountMeta, error) {
|
||||
return account.GetMeta(), nil
|
||||
},
|
||||
},
|
||||
settingsManager: settingsMockManager,
|
||||
}
|
||||
|
@ -116,6 +116,7 @@ type MockAccountManager struct {
|
||||
UpdateToPrimaryAccountFunc func(ctx context.Context, accountId string) (*types.Account, error)
|
||||
GetOwnerInfoFunc func(ctx context.Context, accountID string) (*types.UserInfo, error)
|
||||
GetCurrentUserInfoFunc func(ctx context.Context, accountID, userID string) (*types.UserInfo, error)
|
||||
GetAccountMetaFunc func(ctx context.Context, accountID, userID string) (*types.AccountMeta, error)
|
||||
}
|
||||
|
||||
func (am *MockAccountManager) UpdateAccountPeers(ctx context.Context, accountID string) {
|
||||
@ -803,6 +804,14 @@ func (am *MockAccountManager) GetAccountByID(ctx context.Context, accountID stri
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetAccountByID is not implemented")
|
||||
}
|
||||
|
||||
// GetAccountByID mocks GetAccountByID of the AccountManager interface
|
||||
func (am *MockAccountManager) GetAccountMeta(ctx context.Context, accountID string, userID string) (*types.AccountMeta, error) {
|
||||
if am.GetAccountMetaFunc != nil {
|
||||
return am.GetAccountMetaFunc(ctx, accountID, userID)
|
||||
}
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetAccountMeta 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 {
|
||||
|
@ -658,6 +658,21 @@ func (s *SqlStore) GetAllAccounts(ctx context.Context) (all []*types.Account) {
|
||||
return all
|
||||
}
|
||||
|
||||
func (s *SqlStore) GetAccountMeta(ctx context.Context, lockStrength LockingStrength, accountID string) (*types.AccountMeta, error) {
|
||||
var accountMeta types.AccountMeta
|
||||
result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Model(&types.Account{}).
|
||||
First(&accountMeta, idQueryCondition, accountID)
|
||||
if result.Error != nil {
|
||||
log.WithContext(ctx).Errorf("error when getting account meta %s from the store: %s", accountID, result.Error)
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, status.NewAccountNotFoundError(accountID)
|
||||
}
|
||||
return nil, status.NewGetAccountFromStoreError(result.Error)
|
||||
}
|
||||
|
||||
return &accountMeta, nil
|
||||
}
|
||||
|
||||
func (s *SqlStore) GetAccount(ctx context.Context, accountID string) (*types.Account, error) {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
|
@ -3247,3 +3247,19 @@ func TestSqlStore_SaveGroups_LargeBatch(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 8003, len(accountGroups))
|
||||
}
|
||||
|
||||
func TestSqlStore_GetAccountMeta(t *testing.T) {
|
||||
store, cleanup, err := NewTestStoreFromSQL(context.Background(), "../testdata/extended-store.sql", t.TempDir())
|
||||
t.Cleanup(cleanup)
|
||||
require.NoError(t, err)
|
||||
|
||||
accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||
accountMeta, err := store.GetAccountMeta(context.Background(), LockingStrengthShare, accountID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, accountMeta)
|
||||
require.Equal(t, accountID, accountMeta.AccountID)
|
||||
require.Equal(t, "edafee4e-63fb-11ec-90d6-0242ac120003", accountMeta.CreatedBy)
|
||||
require.Equal(t, "test.com", accountMeta.Domain)
|
||||
require.Equal(t, "private", accountMeta.DomainCategory)
|
||||
require.Equal(t, time.Date(2024, time.October, 2, 14, 1, 38, 210000000, time.UTC), accountMeta.CreatedAt.UTC())
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ type Store interface {
|
||||
GetAccountsCounter(ctx context.Context) (int64, error)
|
||||
GetAllAccounts(ctx context.Context) []*types.Account
|
||||
GetAccount(ctx context.Context, accountID string) (*types.Account, error)
|
||||
GetAccountMeta(ctx context.Context, lockStrength LockingStrength, accountID string) (*types.AccountMeta, error)
|
||||
AccountExists(ctx context.Context, lockStrength LockingStrength, id string) (bool, error)
|
||||
GetAccountDomainAndCategory(ctx context.Context, lockStrength LockingStrength, accountID string) (string, string, error)
|
||||
GetAccountByUser(ctx context.Context, userID string) (*types.Account, error)
|
||||
|
@ -25,7 +25,7 @@ CREATE INDEX `idx_routes_account_id` ON `routes`(`account_id`);
|
||||
CREATE INDEX `idx_name_server_groups_account_id` ON `name_server_groups`(`account_id`);
|
||||
CREATE INDEX `idx_posture_checks_account_id` ON `posture_checks`(`account_id`);
|
||||
|
||||
INSERT INTO accounts VALUES('bf1c8084-ba50-4ce7-9439-34653001fc3b','','2024-10-02 16:01:38.210014+02:00','test.com','private',1,'af1c8024-ha40-4ce2-9418-34653101fc3c','{"IP":"100.64.0.0","Mask":"//8AAA=="}','',0,'[]',0,86400000000000,0,0,0,'',NULL,NULL,NULL);
|
||||
INSERT INTO accounts VALUES('bf1c8084-ba50-4ce7-9439-34653001fc3b','edafee4e-63fb-11ec-90d6-0242ac120003','2024-10-02 16:01:38.210000+02:00','test.com','private',1,'af1c8024-ha40-4ce2-9418-34653101fc3c','{"IP":"100.64.0.0","Mask":"//8AAA=="}','',0,'[]',0,86400000000000,0,0,0,'',NULL,NULL,NULL);
|
||||
INSERT INTO setup_keys VALUES('A2C8E62B-38F5-4553-B31E-DD66C696CEBB','bf1c8084-ba50-4ce7-9439-34653001fc3b','A2C8E62B-38F5-4553-B31E-DD66C696CEBB','Default key','reusable','2021-08-19 20:46:20.005936822+02:00','2321-09-18 20:46:20.005936822+02:00','2021-08-19 20:46:20.005936822+02:00',0,0,NULL,'["cfefqs706sqkneg59g2g"]',0,0);
|
||||
INSERT INTO setup_keys VALUES('A2C8E62B-38F5-4553-B31E-DD66C696CEBC','bf1c8084-ba50-4ce7-9439-34653001fc3b','A2C8E62B-38F5-4553-B31E-DD66C696CEBC','Faulty key with non existing group','reusable','2021-08-19 20:46:20.005936822+02:00','2321-09-18 20:46:20.005936822+02:00','2021-08-19 20:46:20.005936822+02:00',0,0,NULL,'["abcd"]',0,0);
|
||||
INSERT INTO users VALUES('edafee4e-63fb-11ec-90d6-0242ac120003','bf1c8084-ba50-4ce7-9439-34653001fc3b','admin',0,0,'','["cfefqs706sqkneg59g3g"]',0,NULL,'2024-10-02 16:01:38.210678+02:00','api',0,'');
|
||||
|
@ -40,6 +40,17 @@ const (
|
||||
|
||||
type LookupMap map[string]struct{}
|
||||
|
||||
// AccountMeta is a struct that contains a stripped down version of the Account object.
|
||||
// It doesn't carry any peers, groups, policies, or routes, etc. Just some metadata (e.g. ID, created by, created at, etc).
|
||||
type AccountMeta struct {
|
||||
// AccountId is the unique identifier of the account
|
||||
AccountID string `gorm:"column:id"`
|
||||
CreatedAt time.Time
|
||||
CreatedBy string
|
||||
Domain string
|
||||
DomainCategory string
|
||||
}
|
||||
|
||||
// Account represents a unique account of the system
|
||||
type Account struct {
|
||||
// we have to name column to aid as it collides with Network.Id when work with associations
|
||||
@ -855,6 +866,16 @@ func (a *Account) Copy() *Account {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Account) GetMeta() *AccountMeta {
|
||||
return &AccountMeta{
|
||||
AccountID: a.Id,
|
||||
CreatedBy: a.CreatedBy,
|
||||
CreatedAt: a.CreatedAt,
|
||||
Domain: a.Domain,
|
||||
DomainCategory: a.DomainCategory,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Account) GetGroupAll() (*Group, error) {
|
||||
for _, g := range a.Groups {
|
||||
if g.Name == "All" {
|
||||
|
Loading…
x
Reference in New Issue
Block a user