diff --git a/management/server/file_store.go b/management/server/file_store.go index 2f76a40c7..50eeca596 100644 --- a/management/server/file_store.go +++ b/management/server/file_store.go @@ -17,10 +17,9 @@ import ( "github.com/netbirdio/netbird/management/server/status" "github.com/netbirdio/netbird/management/server/telemetry" "github.com/netbirdio/netbird/route" + "github.com/netbirdio/netbird/util" "github.com/rs/xid" log "github.com/sirupsen/logrus" - - "github.com/netbirdio/netbird/util" ) // storeFileName Store file name. Stored in the datadir @@ -1015,7 +1014,7 @@ func (s *FileStore) SavePolicy(_ context.Context, _ LockingStrength, _ *Policy) return status.Errorf(status.Internal, "SavePolicy is not implemented") } -func (s *FileStore) DeletePolicy(_ context.Context, _ LockingStrength, _ string) error { +func (s *FileStore) DeletePolicy(_ context.Context, _ LockingStrength, _, _ string) error { return status.Errorf(status.Internal, "DeletePolicy is not implemented") } @@ -1031,7 +1030,7 @@ func (s *FileStore) SavePostureChecks(_ context.Context, _ LockingStrength, _ *p return status.Errorf(status.Internal, "SavePostureChecks is not implemented") } -func (s *FileStore) DeletePostureChecks(_ context.Context, _ LockingStrength, _ string) error { +func (s *FileStore) DeletePostureChecks(_ context.Context, _ LockingStrength, _, _ string) error { return status.Errorf(status.Internal, "DeletePostureChecks is not implemented") } @@ -1043,6 +1042,13 @@ func (s *FileStore) GetRouteByID(_ context.Context, _ LockingStrength, _ string, return nil, status.Errorf(status.Internal, "GetRouteByID is not implemented") } +func (s *FileStore) SaveRoute(_ context.Context, _ LockingStrength, _ *route.Route) error { + return status.Errorf(status.Internal, "SaveRoute is not implemented") +} +func (s *FileStore) DeleteRoute(_ context.Context, _ LockingStrength, _, _ string) error { + return status.Errorf(status.Internal, "DeleteRoute is not implemented") +} + func (s *FileStore) GetAccountSetupKeys(_ context.Context, _ LockingStrength, _ string) ([]*SetupKey, error) { return nil, status.Errorf(status.Internal, "GetAccountSetupKeys is not implemented") } @@ -1055,7 +1061,7 @@ func (s *FileStore) SaveSetupKey(_ context.Context, _ LockingStrength, _ *SetupK return status.Errorf(status.Internal, "GetSetupKeyByID is not implemented") } -func (s *FileStore) DeleteSetupKey(_ context.Context, _ LockingStrength, _ string) error { +func (s *FileStore) DeleteSetupKey(_ context.Context, _ LockingStrength, _, _ string) error { return status.Errorf(status.Internal, "DeleteSetupKey is not implemented") } @@ -1066,3 +1072,43 @@ func (s *FileStore) GetAccountNameServerGroups(_ context.Context, _ LockingStren func (s *FileStore) GetNameServerGroupByID(_ context.Context, _ LockingStrength, _ string, _ string) (*dns.NameServerGroup, error) { return nil, status.Errorf(status.Internal, "GetNameServerGroupByID is not implemented") } + +func (s *FileStore) SaveNameServerGroup(_ context.Context, _ LockingStrength, _ *dns.NameServerGroup) error { + return status.Errorf(status.Internal, "SaveNameServerGroup is not implemented") +} + +func (s *FileStore) DeleteNameServerGroup(_ context.Context, _ LockingStrength, _, _ string) error { + return status.Errorf(status.Internal, "DeleteNameServerGroup is not implemented") +} + +func (s *FileStore) GetAccountPeers(_ context.Context, _ LockingStrength, _ string) ([]*nbpeer.Peer, error) { + return nil, status.Errorf(status.Internal, "GetAccountPeers is not implemented") +} + +func (s *FileStore) GetAccountPeersWithExpiration(_ context.Context, _ LockingStrength, _ string) ([]*nbpeer.Peer, error) { + return nil, status.Errorf(status.Internal, "GetAccountPeersWithExpiration is not implemented") +} + +func (s *FileStore) GetPeerByID(_ context.Context, _ LockingStrength, _ string, _ string) (*nbpeer.Peer, error) { + return nil, status.Errorf(status.Internal, "GetPeerByID is not implemented") +} + +func (s *FileStore) GetPATByID(_ context.Context, _ LockingStrength, _ string, _ string) (*PersonalAccessToken, error) { + return nil, status.Errorf(status.Internal, "GetPATByID is not implemented") +} + +func (s *FileStore) SavePAT(_ context.Context, _ LockingStrength, _ *PersonalAccessToken) error { + return status.Errorf(status.Internal, "SavePAT is not implemented") +} + +func (s *FileStore) DeletePAT(_ context.Context, _ LockingStrength, _, _ string) error { + return status.Errorf(status.Internal, "DeletePAT is not implemented") +} + +func (s *FileStore) SaveDNSSettings(_ context.Context, _ LockingStrength, _ string, _ *DNSSettings) error { + return status.Errorf(status.Internal, "SaveDNSSettings is not implemented") +} + +func (s *FileStore) SaveAccountSettings(_ context.Context, _ LockingStrength, _ string, _ *Settings) error { + return status.Errorf(status.Internal, "SaveAccountSettings is not implemented") +} diff --git a/management/server/peer.go b/management/server/peer.go index c652ade3f..f962ea414 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -144,7 +144,8 @@ func (am *DefaultAccountManager) MarkPeerConnected(ctx context.Context, peerPubK } if peer.AddedWithSSOLogin() && peer.LoginExpirationEnabled && account.Settings.PeerLoginExpirationEnabled { - am.checkAndSchedulePeerLoginExpiration(ctx, account) + //TODO: use refactored method + //am.checkAndSchedulePeerLoginExpiration(ctx, account) } if oldStatus.LoginExpired { @@ -215,7 +216,8 @@ func (am *DefaultAccountManager) UpdatePeer(ctx context.Context, accountID, user am.StoreEvent(ctx, userID, peer.IP.String(), accountID, event, peer.EventMeta(am.GetDNSDomain())) if peer.AddedWithSSOLogin() && peer.LoginExpirationEnabled && account.Settings.PeerLoginExpirationEnabled { - am.checkAndSchedulePeerLoginExpiration(ctx, account) + //TODO: use refactored method + //am.checkAndSchedulePeerLoginExpiration(ctx, account) } } @@ -1001,6 +1003,72 @@ func (am *DefaultAccountManager) updateAccountPeers(ctx context.Context, account wg.Wait() } +// getNextPeerExpiration returns the minimum duration in which the next peer of the account will expire if it was found. +// If there is no peer that expires this function returns false and a duration of 0. +// This function only considers peers that haven't been expired yet and that are connected. +func (am *DefaultAccountManager) getNextPeerExpiration(ctx context.Context, accountID string) (time.Duration, bool) { + settings, err := am.Store.GetAccountSettings(ctx, LockingStrengthShare, accountID) + if err != nil { + log.WithContext(ctx).Errorf("failed to get account settings: %v", err) + return 0, false + } + + peersWithExpiry, err := am.Store.GetAccountPeersWithExpiration(ctx, LockingStrengthShare, accountID) + if err != nil { + log.WithContext(ctx).Errorf("failed to get peers with expiration: %v", err) + return 0, false + } + + if len(peersWithExpiry) == 0 { + return 0, false + } + var nextExpiry *time.Duration + for _, peer := range peersWithExpiry { + // consider only connected peers because others will require login on connecting to the management server + if peer.Status.LoginExpired || !peer.Status.Connected { + continue + } + _, duration := peer.LoginExpired(settings.PeerLoginExpiration) + if nextExpiry == nil || duration < *nextExpiry { + // if expiration is below 1s return 1s duration + // this avoids issues with ticker that can't be set to < 0 + if duration < time.Second { + return time.Second, true + } + nextExpiry = &duration + } + } + + if nextExpiry == nil { + return 0, false + } + + return *nextExpiry, true +} + +// getExpiredPeers returns peers that have been expired. +func (am *DefaultAccountManager) getExpiredPeers(ctx context.Context, accountID string) ([]*nbpeer.Peer, error) { + settings, err := am.Store.GetAccountSettings(ctx, LockingStrengthShare, accountID) + if err != nil { + return nil, err + } + + peersWithExpiry, err := am.Store.GetAccountPeersWithExpiration(ctx, LockingStrengthShare, accountID) + if err != nil { + return nil, err + } + + var peers []*nbpeer.Peer + for _, peer := range peersWithExpiry { + expired, _ := peer.LoginExpired(settings.PeerLoginExpiration) + if expired { + peers = append(peers, peer) + } + } + + return peers, nil +} + func ConvertSliceToMap(existingLabels []string) map[string]struct{} { labelMap := make(map[string]struct{}, len(existingLabels)) for _, label := range existingLabels { diff --git a/management/server/sql_store.go b/management/server/sql_store.go index 6fa568aaa..3bef8d410 100644 --- a/management/server/sql_store.go +++ b/management/server/sql_store.go @@ -782,6 +782,21 @@ func (s *SqlStore) GetAccountSettings(ctx context.Context, lockStrength LockingS return accountSettings.Settings, nil } +// SaveAccountSettings stores the account settings in DB. +func (s *SqlStore) SaveAccountSettings(ctx context.Context, lockStrength LockingStrength, accountID string, settings *Settings) error { + result := s.db.WithContext(ctx).Debug().Clauses(clause.Locking{Strength: string(lockStrength)}).Model(&Account{}). + Select("*").Where(idQueryCondition, accountID).Updates(&AccountSettings{Settings: settings}) + if result.Error != nil { + return status.Errorf(status.Internal, "failed to save account settings to store: %v", result.Error) + } + + if result.RowsAffected == 0 { + return status.Errorf(status.NotFound, "account not found") + } + + return nil +} + // SaveUserLastLogin stores the last login time for a user in DB. func (s *SqlStore) SaveUserLastLogin(ctx context.Context, accountID, userID string, lastLogin time.Time) error { var user User @@ -1055,6 +1070,19 @@ func (s *SqlStore) GetAccountDNSSettings(ctx context.Context, lockStrength Locki return &accountDNSSettings.DNSSettings, nil } +// SaveDNSSettings saves the DNS settings to the store. +func (s *SqlStore) SaveDNSSettings(ctx context.Context, lockStrength LockingStrength, accountID string, settings *DNSSettings) error { + result := s.db.WithContext(ctx).Clauses(clause.Locking{Strength: string(lockStrength)}).Model(&Account{}). + Where(idQueryCondition, accountID).Updates(&AccountDNSSettings{DNSSettings: *settings}) + if result.Error != nil { + return status.Errorf(status.Internal, "failed to save dns settings to store: %v", result.Error) + } + if result.RowsAffected == 0 { + return status.Errorf(status.NotFound, "account not found") + } + return nil +} + // AccountExists checks whether an account exists by the given ID. func (s *SqlStore) AccountExists(ctx context.Context, lockStrength LockingStrength, id string) (bool, error) { var accountID string @@ -1120,13 +1148,13 @@ func (s *SqlStore) GetPolicyByID(ctx context.Context, lockStrength LockingStreng // SavePolicy saves a policy to the database. func (s *SqlStore) SavePolicy(ctx context.Context, lockStrength LockingStrength, policy *Policy) error { return s.db.WithContext(ctx).Session(&gorm.Session{FullSaveAssociations: true}). - Clauses(clause.Locking{Strength: string(lockStrength)}).Save(&policy).Error + Clauses(clause.Locking{Strength: string(lockStrength)}).Save(policy).Error } // DeletePolicy deletes a policy from the database. -func (s *SqlStore) DeletePolicy(ctx context.Context, lockStrength LockingStrength, policyID string) error { +func (s *SqlStore) DeletePolicy(ctx context.Context, lockStrength LockingStrength, policyID, accountID string) error { return s.db.WithContext(ctx).Clauses(clause.Locking{Strength: string(lockStrength)}). - Delete(&Policy{}, idQueryCondition, policyID).Error + Delete(&Policy{}, accountAndIDQueryCondition, accountID, policyID).Error } // GetAccountPostureChecks retrieves posture checks for an account. @@ -1141,23 +1169,21 @@ func (s *SqlStore) GetPostureChecksByID(ctx context.Context, lockStrength Lockin // SavePostureChecks saves a posture checks to the database. func (s *SqlStore) SavePostureChecks(ctx context.Context, lockStrength LockingStrength, postureCheck *posture.Checks) error { - result := s.db.WithContext(ctx).Session(&gorm.Session{FullSaveAssociations: true}). - Clauses(clause.Locking{Strength: string(lockStrength)}).Save(&postureCheck) - + result := s.db.WithContext(ctx).Clauses(clause.Locking{Strength: string(lockStrength)}).Save(postureCheck) if result.Error != nil { if errors.Is(result.Error, gorm.ErrDuplicatedKey) { return status.Errorf(status.InvalidArgument, "name should be unique") } - return result.Error + return status.Errorf(status.Internal, "failed to save posture checks to store: %v", result.Error) } return nil } // DeletePostureChecks deletes a posture checks from the database. -func (s *SqlStore) DeletePostureChecks(ctx context.Context, lockStrength LockingStrength, postureChecksID string) error { +func (s *SqlStore) DeletePostureChecks(ctx context.Context, lockStrength LockingStrength, postureChecksID, accountID string) error { return s.db.WithContext(ctx).Clauses(clause.Locking{Strength: string(lockStrength)}). - Delete(&posture.Checks{}, idQueryCondition, postureChecksID).Error + Delete(&posture.Checks{}, accountAndIDQueryCondition, accountID, postureChecksID).Error } // GetAccountRoutes retrieves network routes for an account. @@ -1170,6 +1196,17 @@ func (s *SqlStore) GetRouteByID(ctx context.Context, lockStrength LockingStrengt return getRecordByID[route.Route](s.db.WithContext(ctx), lockStrength, routeID, accountID) } +// SaveRoute saves a route to the database. +func (s *SqlStore) SaveRoute(ctx context.Context, lockStrength LockingStrength, route *route.Route) error { + return s.db.WithContext(ctx).Clauses(clause.Locking{Strength: string(lockStrength)}).Save(route).Error +} + +// DeleteRoute deletes a route from the database. +func (s *SqlStore) DeleteRoute(ctx context.Context, lockStrength LockingStrength, routeID, accountID string) error { + return s.db.WithContext(ctx).Clauses(clause.Locking{Strength: string(lockStrength)}). + Delete(&route.Route{}, accountAndIDQueryCondition, accountID, routeID).Error +} + // GetAccountSetupKeys retrieves setup keys for an account. func (s *SqlStore) GetAccountSetupKeys(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*SetupKey, error) { return getRecords[*SetupKey](s.db.WithContext(ctx), lockStrength, accountID) @@ -1187,9 +1224,9 @@ func (s *SqlStore) SaveSetupKey(ctx context.Context, lockStrength LockingStrengt } // DeleteSetupKey deletes a setup key from the database. -func (s *SqlStore) DeleteSetupKey(ctx context.Context, lockStrength LockingStrength, setupKeyID string) error { +func (s *SqlStore) DeleteSetupKey(ctx context.Context, lockStrength LockingStrength, setupKeyID, accountID string) error { return s.db.WithContext(ctx).Clauses(clause.Locking{Strength: string(lockStrength)}). - Delete(&posture.Checks{}, idQueryCondition, setupKeyID).Error + Delete(&SetupKey{}, accountAndIDQueryCondition, accountID, setupKeyID).Error } // GetAccountNameServerGroups retrieves name server groups for an account. @@ -1202,6 +1239,58 @@ func (s *SqlStore) GetNameServerGroupByID(ctx context.Context, lockStrength Lock return getRecordByID[nbdns.NameServerGroup](s.db.WithContext(ctx), lockStrength, nsGroupID, accountID) } +// SaveNameServerGroup saves a name server group to the database. +func (s *SqlStore) SaveNameServerGroup(ctx context.Context, lockStrength LockingStrength, nameServerGroup *nbdns.NameServerGroup) error { + return s.db.WithContext(ctx).Clauses(clause.Locking{Strength: string(lockStrength)}).Save(nameServerGroup).Error +} + +// DeleteNameServerGroup deletes a name server group from the database. +func (s *SqlStore) DeleteNameServerGroup(ctx context.Context, lockStrength LockingStrength, nameServerGroupID, accountID string) error { + return deleteRecordByID[nbdns.NameServerGroup](s.db.WithContext(ctx), lockStrength, nameServerGroupID, accountID) +} + +// GetPATByID retrieves a personal access token by its ID and user ID. +func (s *SqlStore) GetPATByID(ctx context.Context, lockStrength LockingStrength, patID string, userID string) (*PersonalAccessToken, error) { + var pat PersonalAccessToken + result := s.db.WithContext(ctx).Clauses(clause.Locking{Strength: string(lockStrength)}). + First(&pat, "user_id = ? and id = ?", userID, patID) + if err := result.Error; err != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return nil, status.Errorf(status.NotFound, "PAT not found") + } + return nil, status.Errorf(status.Internal, "failed to get PAT from store") + } + + return &pat, nil +} + +// SavePAT saves a personal access token to the database. +func (s *SqlStore) SavePAT(ctx context.Context, lockStrength LockingStrength, pat *PersonalAccessToken) error { + return s.db.WithContext(ctx).Clauses(clause.Locking{Strength: string(lockStrength)}).Save(pat).Error +} + +// DeletePAT deletes a personal access token from the database. +func (s *SqlStore) DeletePAT(ctx context.Context, lockStrength LockingStrength, patID, userID string) error { + return s.db.WithContext(ctx).Clauses(clause.Locking{Strength: string(lockStrength)}). + Delete(&PersonalAccessToken{}, "user_id = ? and id = ?", userID, patID).Error +} + +// GetAccountPeers retrieves peers for an account. +func (s *SqlStore) GetAccountPeers(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*nbpeer.Peer, error) { + return getRecords[*nbpeer.Peer](s.db.WithContext(ctx), lockStrength, accountID) +} + +// GetAccountPeersWithExpiration retrieves a list of peers that have Peer.LoginExpirationEnabled set to true and that were added by a user. +func (s *SqlStore) GetAccountPeersWithExpiration(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*nbpeer.Peer, error) { + db := s.db.WithContext(ctx).Where("login_expiration_enabled = ? AND user_id IS NOT NULL AND user_id != ''", true) + return getRecords[*nbpeer.Peer](db, lockStrength, accountID) +} + +// GetPeerByID retrieves a peer by its ID and account ID. +func (s *SqlStore) GetPeerByID(ctx context.Context, lockStrength LockingStrength, peerID string, accountID string) (*nbpeer.Peer, error) { + return getRecordByID[nbpeer.Peer](s.db.WithContext(ctx), lockStrength, peerID, accountID) +} + // getRecords retrieves records from the database based on the account ID. func getRecords[T any](db *gorm.DB, lockStrength LockingStrength, accountID string) ([]T, error) { var record []T @@ -1234,3 +1323,23 @@ func getRecordByID[T any](db *gorm.DB, lockStrength LockingStrength, recordID, a } return &record, nil } + +// deleteRecordByID deletes a record by its ID and account ID from the database. +func deleteRecordByID[T any](db *gorm.DB, lockStrength LockingStrength, recordID, accountID string) error { + var record T + + parts := strings.Split(fmt.Sprintf("%T", record), ".") + recordType := parts[len(parts)-1] + + result := db.Clauses(clause.Locking{Strength: string(lockStrength)}). + Delete(&record, accountAndIDQueryCondition, accountID, recordID) + if err := result.Error; err != nil { + return status.Errorf(status.Internal, "failed to delete %s from store: %v", recordType, err) + } + + if result.RowsAffected == 0 { + return status.Errorf(status.NotFound, "%s not found", recordType) + } + + return nil +} diff --git a/management/server/store.go b/management/server/store.go index 26ca56474..a3fdd012e 100644 --- a/management/server/store.go +++ b/management/server/store.go @@ -51,11 +51,15 @@ type Store interface { GetAccountBySetupKey(ctx context.Context, setupKey string) (*Account, error) // todo use key hash later GetAccountByPrivateDomain(ctx context.Context, domain string) (*Account, error) GetAccountIDByPrivateDomain(ctx context.Context, lockStrength LockingStrength, domain string) (string, error) - GetAccountSettings(ctx context.Context, lockStrength LockingStrength, accountID string) (*Settings, error) - GetAccountDNSSettings(ctx context.Context, lockStrength LockingStrength, accountID string) (*DNSSettings, error) SaveAccount(ctx context.Context, account *Account) error DeleteAccount(ctx context.Context, account *Account) error + GetAccountDNSSettings(ctx context.Context, lockStrength LockingStrength, accountID string) (*DNSSettings, error) + SaveDNSSettings(ctx context.Context, lockStrength LockingStrength, accountID string, settings *DNSSettings) error + + GetAccountSettings(ctx context.Context, lockStrength LockingStrength, accountID string) (*Settings, error) + SaveAccountSettings(ctx context.Context, lockStrength LockingStrength, accountID string, settings *Settings) error + GetUserByTokenID(ctx context.Context, tokenID string) (*User, error) GetUserByUserID(ctx context.Context, lockStrength LockingStrength, userID string) (*User, error) SaveUsers(accountID string, users map[string]*User) error @@ -64,6 +68,10 @@ type Store interface { DeleteHashedPAT2TokenIDIndex(hashedToken string) error DeleteTokenID2UserIDIndex(tokenID string) error + GetPATByID(ctx context.Context, lockStrength LockingStrength, patID string, userID string) (*PersonalAccessToken, error) + SavePAT(ctx context.Context, strength LockingStrength, pat *PersonalAccessToken) error + DeletePAT(ctx context.Context, strength LockingStrength, patID string, userID string) error + GetAccountGroups(ctx context.Context, accountID string) ([]*nbgroup.Group, error) GetGroupByID(ctx context.Context, lockStrength LockingStrength, groupID, accountID string) (*nbgroup.Group, error) GetGroupByName(ctx context.Context, lockStrength LockingStrength, groupName, accountID string) (*nbgroup.Group, error) @@ -72,19 +80,22 @@ type Store interface { GetAccountPolicies(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*Policy, error) GetPolicyByID(ctx context.Context, lockStrength LockingStrength, policyID string, accountID string) (*Policy, error) SavePolicy(ctx context.Context, lockStrength LockingStrength, policy *Policy) error - DeletePolicy(ctx context.Context, lockStrength LockingStrength, postureCheckID string) error + DeletePolicy(ctx context.Context, lockStrength LockingStrength, postureCheckID, accountID string) error GetPostureCheckByChecksDefinition(accountID string, checks *posture.ChecksDefinition) (*posture.Checks, error) GetAccountPostureChecks(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*posture.Checks, error) GetPostureChecksByID(ctx context.Context, lockStrength LockingStrength, postureCheckID string, accountID string) (*posture.Checks, error) SavePostureChecks(ctx context.Context, lockStrength LockingStrength, postureCheck *posture.Checks) error - DeletePostureChecks(ctx context.Context, lockStrength LockingStrength, postureChecksID string) error + DeletePostureChecks(ctx context.Context, lockStrength LockingStrength, postureChecksID, accountID string) error GetPeerLabelsInAccount(ctx context.Context, lockStrength LockingStrength, accountId string) ([]string, error) AddPeerToAllGroup(ctx context.Context, accountID string, peerID string) error AddPeerToGroup(ctx context.Context, accountId string, peerId string, groupID string) error AddPeerToAccount(ctx context.Context, peer *nbpeer.Peer) error GetPeerByPeerPubKey(ctx context.Context, lockStrength LockingStrength, peerKey string) (*nbpeer.Peer, error) + GetAccountPeers(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*nbpeer.Peer, error) + GetAccountPeersWithExpiration(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*nbpeer.Peer, error) + GetPeerByID(ctx context.Context, lockStrength LockingStrength, peerID string, accountID string) (*nbpeer.Peer, error) SavePeer(ctx context.Context, accountID string, peer *nbpeer.Peer) error SavePeerStatus(accountID, peerID string, status nbpeer.PeerStatus) error SavePeerLocation(accountID string, peer *nbpeer.Peer) error @@ -94,13 +105,17 @@ type Store interface { GetAccountSetupKeys(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*SetupKey, error) GetSetupKeyByID(ctx context.Context, lockStrength LockingStrength, setupKeyID string, accountID string) (*SetupKey, error) SaveSetupKey(ctx context.Context, lockStrength LockingStrength, setupKey *SetupKey) error - DeleteSetupKey(ctx context.Context, lockStrength LockingStrength, setupKeyID string) error + DeleteSetupKey(ctx context.Context, lockStrength LockingStrength, setupKeyID, accountID string) error GetAccountRoutes(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*route.Route, error) GetRouteByID(ctx context.Context, lockStrength LockingStrength, routeID string, accountID string) (*route.Route, error) + SaveRoute(ctx context.Context, lockStrength LockingStrength, route *route.Route) error + DeleteRoute(ctx context.Context, lockStrength LockingStrength, routeID, accountID string) error GetAccountNameServerGroups(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*dns.NameServerGroup, error) GetNameServerGroupByID(ctx context.Context, lockStrength LockingStrength, nameServerGroupID string, accountID string) (*dns.NameServerGroup, error) + SaveNameServerGroup(ctx context.Context, lockStrength LockingStrength, nameServerGroup *dns.NameServerGroup) error + DeleteNameServerGroup(ctx context.Context, lockStrength LockingStrength, nameServerGroupID, accountID string) error GetTakenIPs(ctx context.Context, lockStrength LockingStrength, accountId string) ([]net.IP, error) IncrementNetworkSerial(ctx context.Context, lockStrength LockingStrength, accountId string) error