From 0b22a565afa38d2dfc10110aafa65d584f878e3a Mon Sep 17 00:00:00 2001 From: ctrl-zzz Date: Tue, 11 Jun 2024 20:45:40 +0200 Subject: [PATCH 01/13] feat: init inactivity expiration --- management/server/account.go | 153 ++++++++++++++++++++++++---- management/server/activity/codes.go | 7 ++ management/server/file_store.go | 6 +- management/server/peer.go | 56 +++++++--- management/server/peer/peer.go | 46 +++++---- 5 files changed, 214 insertions(+), 54 deletions(-) diff --git a/management/server/account.go b/management/server/account.go index 23781c915ab..d0bd67f9f36 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -44,12 +44,13 @@ import ( ) const ( - PublicCategory = "public" - PrivateCategory = "private" - UnknownCategory = "unknown" - CacheExpirationMax = 7 * 24 * 3600 * time.Second // 7 days - CacheExpirationMin = 3 * 24 * 3600 * time.Second // 3 days - DefaultPeerLoginExpiration = 24 * time.Hour + PublicCategory = "public" + PrivateCategory = "private" + UnknownCategory = "unknown" + CacheExpirationMax = 7 * 24 * 3600 * time.Second // 7 days + CacheExpirationMin = 3 * 24 * 3600 * time.Second // 3 days + DefaultPeerLoginExpiration = 24 * time.Hour + DefaultPeerInactivityExpiration = 20 * time.Second ) type userLoggedInOnce bool @@ -170,8 +171,9 @@ type DefaultAccountManager struct { // singleAccountModeDomain is a domain to use in singleAccountMode setup singleAccountModeDomain string // dnsDomain is used for peer resolution. This is appended to the peer's name - dnsDomain string - peerLoginExpiry Scheduler + dnsDomain string + peerLoginExpiry Scheduler + peerInactivityExpiry Scheduler // userDeleteFromIDPEnabled allows to delete user from IDP when user is deleted from account userDeleteFromIDPEnabled bool @@ -190,6 +192,10 @@ type Settings struct { // Applies to all peers that have Peer.LoginExpirationEnabled set to true. PeerLoginExpiration time.Duration + PeerInactivityExpirationEnabled bool + + PeerInactivityExpiration time.Duration + // RegularUsersViewBlocked allows to block regular users from viewing even their own peers and some UI elements RegularUsersViewBlocked bool @@ -213,13 +219,15 @@ type Settings struct { // Copy copies the Settings struct func (s *Settings) Copy() *Settings { settings := &Settings{ - PeerLoginExpirationEnabled: s.PeerLoginExpirationEnabled, - PeerLoginExpiration: s.PeerLoginExpiration, - JWTGroupsEnabled: s.JWTGroupsEnabled, - JWTGroupsClaimName: s.JWTGroupsClaimName, - GroupsPropagationEnabled: s.GroupsPropagationEnabled, - JWTAllowGroups: s.JWTAllowGroups, - RegularUsersViewBlocked: s.RegularUsersViewBlocked, + PeerLoginExpirationEnabled: s.PeerLoginExpirationEnabled, + PeerLoginExpiration: s.PeerLoginExpiration, + PeerInactivityExpirationEnabled: s.PeerInactivityExpirationEnabled, + PeerInactivityExpiration: s.PeerInactivityExpiration, + JWTGroupsEnabled: s.JWTGroupsEnabled, + JWTGroupsClaimName: s.JWTGroupsClaimName, + GroupsPropagationEnabled: s.GroupsPropagationEnabled, + JWTAllowGroups: s.JWTAllowGroups, + RegularUsersViewBlocked: s.RegularUsersViewBlocked, } if s.Extra != nil { settings.Extra = s.Extra.Copy() @@ -583,6 +591,57 @@ func (a *Account) GetPeersWithExpiration() []*nbpeer.Peer { return peers } +func (a *Account) GetInactivePeers() []*nbpeer.Peer { + var peers []*nbpeer.Peer + for _, inactivePeer := range a.GetPeersWithInactivity() { + inactive, _ := inactivePeer.SessionExpired(a.Settings.PeerInactivityExpiration) + if inactive { + peers = append(peers, inactivePeer) + } + } + return peers +} + +func (a *Account) GetNextInactivePeerExpiration() (time.Duration, bool) { + peersWithExpiry := a.GetPeersWithInactivity() + 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 { + continue + } + _, duration := peer.SessionExpired(a.Settings.PeerInactivityExpiration) + 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 +} + +// GetPeersWithInactivityPeers returns a list of peers that have Peer.InactivityExpirationEnabled set to true and that were added by a user +func (a *Account) GetPeersWithInactivity() []*nbpeer.Peer { + peers := make([]*nbpeer.Peer, 0) + for _, peer := range a.Peers { + if peer.InactivityExpirationEnabled && peer.AddedWithSSOLogin() { + peers = append(peers, peer) + } + } + return peers +} + // GetPeers returns a list of all Account peers func (a *Account) GetPeers() []*nbpeer.Peer { var peers []*nbpeer.Peer @@ -966,6 +1025,7 @@ func BuildManager( dnsDomain: dnsDomain, eventStore: eventStore, peerLoginExpiry: NewDefaultScheduler(), + peerInactivityExpiry: NewDefaultScheduler(), userDeleteFromIDPEnabled: userDeleteFromIDPEnabled, integratedPeerValidator: integratedPeerValidator, metrics: metrics, @@ -1094,6 +1154,22 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco am.checkAndSchedulePeerLoginExpiration(ctx, account) } + if oldSettings.PeerInactivityExpirationEnabled != newSettings.PeerInactivityExpirationEnabled { + event := activity.AccountPeerInactivityExpirationEnabled + if !newSettings.PeerInactivityExpirationEnabled { + event = activity.AccountPeerInactivityExpirationDisabled + am.peerInactivityExpiry.Cancel(ctx, []string{accountID}) + } else { + am.checkAndSchedulePeerInactivityExpiration(ctx, account) + } + am.StoreEvent(ctx, userID, accountID, accountID, event, nil) + } + + if oldSettings.PeerInactivityExpiration != newSettings.PeerInactivityExpiration { + am.StoreEvent(ctx, userID, accountID, accountID, activity.AccountPeerInactivityExpirationDurationUpdated, nil) + am.checkAndSchedulePeerInactivityExpiration(ctx, account) + } + updatedAccount := account.UpdateSettings(newSettings) err = am.Store.SaveAccount(ctx, account) @@ -1139,6 +1215,43 @@ func (am *DefaultAccountManager) checkAndSchedulePeerLoginExpiration(ctx context } } +// peerInactivityExpirationJob marks login expired for all inactive peers and returns the minimum duration in which the next peer of the account will expire by inactivity if found +func (am *DefaultAccountManager) peerInactivityExpirationJob(ctx context.Context, accountID string) func() (time.Duration, bool) { + return func() (time.Duration, bool) { + unlock := am.Store.AcquireAccountWriteLock(ctx, accountID) + defer unlock() + + account, err := am.Store.GetAccount(ctx, accountID) + if err != nil { + log.Errorf("failed getting account %s expiring peers", account.Id) + return account.GetNextInactivePeerExpiration() + } + + expiredPeers := account.GetInactivePeers() + var peerIDs []string + for _, peer := range expiredPeers { + peerIDs = append(peerIDs, peer.ID) + } + + log.Debugf("discovered %d peers to expire for account %s", len(peerIDs), account.Id) + + if err := am.expireAndUpdatePeers(ctx, account, expiredPeers); err != nil { + log.Errorf("failed updating account peers while expiring peers for account %s", account.Id) + return account.GetNextInactivePeerExpiration() + } + + return account.GetNextInactivePeerExpiration() + } +} + +// checkAndSchedulePeerInactivityExpiration periodically checks for inactive peers to end their sessions +func (am *DefaultAccountManager) checkAndSchedulePeerInactivityExpiration(ctx context.Context, account *Account) { + am.peerInactivityExpiry.Cancel(ctx, []string{account.Id}) + if nextRun, ok := account.GetNextInactivePeerExpiration(); ok { + go am.peerInactivityExpiry.Schedule(ctx, nextRun, account.Id, am.peerInactivityExpirationJob(ctx, account.Id)) + } +} + // newAccount creates a new Account with a generated ID and generated default setup keys. // If ID is already in use (due to collision) we try one more time before returning error func (am *DefaultAccountManager) newAccount(ctx context.Context, userID, domain string) (*Account, error) { @@ -2166,10 +2279,12 @@ func newAccountWithId(ctx context.Context, accountID, userID, domain string) *Ac NameServerGroups: nameServersGroups, DNSSettings: dnsSettings, Settings: &Settings{ - PeerLoginExpirationEnabled: true, - PeerLoginExpiration: DefaultPeerLoginExpiration, - GroupsPropagationEnabled: true, - RegularUsersViewBlocked: true, + PeerLoginExpirationEnabled: true, + PeerLoginExpiration: DefaultPeerLoginExpiration, + PeerInactivityExpirationEnabled: true, + PeerInactivityExpiration: DefaultPeerInactivityExpiration, + GroupsPropagationEnabled: true, + RegularUsersViewBlocked: true, }, } diff --git a/management/server/activity/codes.go b/management/server/activity/codes.go index 4ee57f1817c..6e518c889e0 100644 --- a/management/server/activity/codes.go +++ b/management/server/activity/codes.go @@ -139,6 +139,13 @@ const ( PostureCheckUpdated Activity = 61 // PostureCheckDeleted indicates that the user deleted a posture check PostureCheckDeleted Activity = 62 + + PeerInactivityExpirationEnabled Activity = 63 + PeerInactivityExpirationDisabled Activity = 64 + + AccountPeerInactivityExpirationEnabled Activity = 65 + AccountPeerInactivityExpirationDisabled Activity = 66 + AccountPeerInactivityExpirationDurationUpdated Activity = 67 ) var activityMap = map[Activity]Code{ diff --git a/management/server/file_store.go b/management/server/file_store.go index 1927568ef18..ac5ea178e7f 100644 --- a/management/server/file_store.go +++ b/management/server/file_store.go @@ -122,8 +122,10 @@ func restore(ctx context.Context, file string) (*FileStore, error) { for accountID, account := range store.Accounts { if account.Settings == nil { account.Settings = &Settings{ - PeerLoginExpirationEnabled: false, - PeerLoginExpiration: DefaultPeerLoginExpiration, + PeerLoginExpirationEnabled: false, + PeerInactivityExpirationEnabled: false, + PeerLoginExpiration: DefaultPeerLoginExpiration, + PeerInactivityExpiration: DefaultPeerInactivityExpiration, } } diff --git a/management/server/peer.go b/management/server/peer.go index c7d757bb479..3148fe848c9 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -146,6 +146,10 @@ func (am *DefaultAccountManager) MarkPeerConnected(ctx context.Context, peerPubK am.checkAndSchedulePeerLoginExpiration(ctx, account) } + if peer.AddedWithSSOLogin() && peer.InactivityExpirationEnabled && account.Settings.PeerInactivityExpirationEnabled { + am.checkAndSchedulePeerInactivityExpiration(ctx, account) + } + if oldStatus.LoginExpired { // we need to update other peers because when peer login expires all other peers are notified to disconnect from // the expired one. Here we notify them that connection is now allowed again. @@ -218,6 +222,25 @@ func (am *DefaultAccountManager) UpdatePeer(ctx context.Context, accountID, user } } + if peer.InactivityExpirationEnabled != update.InactivityExpirationEnabled { + + if !peer.AddedWithSSOLogin() { + return nil, status.Errorf(status.PreconditionFailed, "this peer hasn't been added with the SSO login, therefore the login expiration can't be updated") + } + + peer.InactivityExpirationEnabled = update.InactivityExpirationEnabled + + event := activity.PeerInactivityExpirationEnabled + if !update.InactivityExpirationEnabled { + event = activity.PeerInactivityExpirationDisabled + } + am.StoreEvent(ctx, userID, peer.IP.String(), accountID, event, peer.EventMeta(am.GetDNSDomain())) + + if peer.AddedWithSSOLogin() && peer.InactivityExpirationEnabled && account.Settings.PeerInactivityExpirationEnabled { + am.checkAndSchedulePeerInactivityExpiration(ctx, account) + } + } + account.UpdatePeer(peer) err = am.Store.SaveAccount(ctx, account) @@ -443,22 +466,23 @@ func (am *DefaultAccountManager) AddPeer(ctx context.Context, setupKey, userID s registrationTime := time.Now().UTC() newPeer := &nbpeer.Peer{ - ID: xid.New().String(), - Key: peer.Key, - SetupKey: upperKey, - IP: nextIp, - Meta: peer.Meta, - Name: peer.Meta.Hostname, - DNSLabel: newLabel, - UserID: userID, - Status: &nbpeer.PeerStatus{Connected: false, LastSeen: registrationTime}, - SSHEnabled: false, - SSHKey: peer.SSHKey, - LastLogin: registrationTime, - CreatedAt: registrationTime, - LoginExpirationEnabled: addedByUser, - Ephemeral: ephemeral, - Location: peer.Location, + ID: xid.New().String(), + Key: peer.Key, + SetupKey: upperKey, + IP: nextIp, + Meta: peer.Meta, + Name: peer.Meta.Hostname, + DNSLabel: newLabel, + UserID: userID, + Status: &nbpeer.PeerStatus{Connected: false, LastSeen: registrationTime}, + SSHEnabled: false, + SSHKey: peer.SSHKey, + LastLogin: registrationTime, + CreatedAt: registrationTime, + LoginExpirationEnabled: addedByUser, + InactivityExpirationEnabled: addedByUser, + Ephemeral: ephemeral, + Location: peer.Location, } if am.geo != nil && newPeer.Location.ConnectionIP != nil { diff --git a/management/server/peer/peer.go b/management/server/peer/peer.go index 3d9ba18e9e5..5c5b28d503c 100644 --- a/management/server/peer/peer.go +++ b/management/server/peer/peer.go @@ -38,6 +38,7 @@ type Peer struct { // LoginExpirationEnabled indicates whether peer's login expiration is enabled and once expired the peer has to re-login. // Works with LastLogin LoginExpirationEnabled bool + InactivityExpirationEnabled bool // LastLogin the time when peer performed last login operation LastLogin time.Time // CreatedAt records the time the peer was created @@ -170,23 +171,24 @@ func (p *Peer) Copy() *Peer { peerStatus = p.Status.Copy() } return &Peer{ - ID: p.ID, - AccountID: p.AccountID, - Key: p.Key, - SetupKey: p.SetupKey, - IP: p.IP, - Meta: p.Meta, - Name: p.Name, - DNSLabel: p.DNSLabel, - Status: peerStatus, - UserID: p.UserID, - SSHKey: p.SSHKey, - SSHEnabled: p.SSHEnabled, - LoginExpirationEnabled: p.LoginExpirationEnabled, - LastLogin: p.LastLogin, - CreatedAt: p.CreatedAt, - Ephemeral: p.Ephemeral, - Location: p.Location, + ID: p.ID, + AccountID: p.AccountID, + Key: p.Key, + SetupKey: p.SetupKey, + IP: p.IP, + Meta: p.Meta, + Name: p.Name, + DNSLabel: p.DNSLabel, + Status: peerStatus, + UserID: p.UserID, + SSHKey: p.SSHKey, + SSHEnabled: p.SSHEnabled, + LoginExpirationEnabled: p.LoginExpirationEnabled, + InactivityExpirationEnabled: p.InactivityExpirationEnabled, + LastLogin: p.LastLogin, + CreatedAt: p.CreatedAt, + Ephemeral: p.Ephemeral, + Location: p.Location, } } @@ -219,6 +221,16 @@ func (p *Peer) MarkLoginExpired(expired bool) { p.Status = newStatus } +func (p *Peer) SessionExpired(expiresIn time.Duration) (bool, time.Duration) { + if !p.AddedWithSSOLogin() || !p.InactivityExpirationEnabled || p.Status.Connected { + return false, 0 + } + expiresAt := p.Status.LastSeen.Add(expiresIn) + now := time.Now() + timeLeft := expiresAt.Sub(now) + return timeLeft <= 0, timeLeft +} + // LoginExpired indicates whether the peer's login has expired or not. // If Peer.LastLogin plus the expiresIn duration has happened already; then login has expired. // Return true if a login has expired, false otherwise, and time left to expiration (negative when expired). From efa79824cde1649a5aa7fc2d68f6c1b1b6391f29 Mon Sep 17 00:00:00 2001 From: ctrl-zzz Date: Tue, 11 Jun 2024 20:48:27 +0200 Subject: [PATCH 02/13] feat: inactivity mode api integration --- management/server/activity/codes.go | 78 +++++++-------- management/server/file_store.go | 8 +- management/server/http/accounts_handler.go | 8 +- management/server/http/api/openapi.yml | 11 +++ management/server/http/api/types.gen.go | 15 ++- management/server/http/peers_handler.go | 105 +++++++++++---------- management/server/peer.go | 2 +- 7 files changed, 129 insertions(+), 98 deletions(-) diff --git a/management/server/activity/codes.go b/management/server/activity/codes.go index 6e518c889e0..c6325118468 100644 --- a/management/server/activity/codes.go +++ b/management/server/activity/codes.go @@ -149,44 +149,46 @@ const ( ) var activityMap = map[Activity]Code{ - PeerAddedByUser: {"Peer added", "user.peer.add"}, - PeerAddedWithSetupKey: {"Peer added", "setupkey.peer.add"}, - UserJoined: {"User joined", "user.join"}, - UserInvited: {"User invited", "user.invite"}, - AccountCreated: {"Account created", "account.create"}, - PeerRemovedByUser: {"Peer deleted", "user.peer.delete"}, - RuleAdded: {"Rule added", "rule.add"}, - RuleUpdated: {"Rule updated", "rule.update"}, - RuleRemoved: {"Rule deleted", "rule.delete"}, - PolicyAdded: {"Policy added", "policy.add"}, - PolicyUpdated: {"Policy updated", "policy.update"}, - PolicyRemoved: {"Policy deleted", "policy.delete"}, - SetupKeyCreated: {"Setup key created", "setupkey.add"}, - SetupKeyUpdated: {"Setup key updated", "setupkey.update"}, - SetupKeyRevoked: {"Setup key revoked", "setupkey.revoke"}, - SetupKeyOverused: {"Setup key overused", "setupkey.overuse"}, - GroupCreated: {"Group created", "group.add"}, - GroupUpdated: {"Group updated", "group.update"}, - GroupAddedToPeer: {"Group added to peer", "peer.group.add"}, - GroupRemovedFromPeer: {"Group removed from peer", "peer.group.delete"}, - GroupAddedToUser: {"Group added to user", "user.group.add"}, - GroupRemovedFromUser: {"Group removed from user", "user.group.delete"}, - UserRoleUpdated: {"User role updated", "user.role.update"}, - GroupAddedToSetupKey: {"Group added to setup key", "setupkey.group.add"}, - GroupRemovedFromSetupKey: {"Group removed from user setup key", "setupkey.group.delete"}, - GroupAddedToDisabledManagementGroups: {"Group added to disabled management DNS setting", "dns.setting.disabled.management.group.add"}, - GroupRemovedFromDisabledManagementGroups: {"Group removed from disabled management DNS setting", "dns.setting.disabled.management.group.delete"}, - RouteCreated: {"Route created", "route.add"}, - RouteRemoved: {"Route deleted", "route.delete"}, - RouteUpdated: {"Route updated", "route.update"}, - PeerSSHEnabled: {"Peer SSH server enabled", "peer.ssh.enable"}, - PeerSSHDisabled: {"Peer SSH server disabled", "peer.ssh.disable"}, - PeerRenamed: {"Peer renamed", "peer.rename"}, - PeerLoginExpirationEnabled: {"Peer login expiration enabled", "peer.login.expiration.enable"}, - PeerLoginExpirationDisabled: {"Peer login expiration disabled", "peer.login.expiration.disable"}, - NameserverGroupCreated: {"Nameserver group created", "nameserver.group.add"}, - NameserverGroupDeleted: {"Nameserver group deleted", "nameserver.group.delete"}, - NameserverGroupUpdated: {"Nameserver group updated", "nameserver.group.update"}, + PeerAddedByUser: {"Peer added", "user.peer.add"}, + PeerAddedWithSetupKey: {"Peer added", "setupkey.peer.add"}, + UserJoined: {"User joined", "user.join"}, + UserInvited: {"User invited", "user.invite"}, + AccountCreated: {"Account created", "account.create"}, + PeerRemovedByUser: {"Peer deleted", "user.peer.delete"}, + RuleAdded: {"Rule added", "rule.add"}, + RuleUpdated: {"Rule updated", "rule.update"}, + RuleRemoved: {"Rule deleted", "rule.delete"}, + PolicyAdded: {"Policy added", "policy.add"}, + PolicyUpdated: {"Policy updated", "policy.update"}, + PolicyRemoved: {"Policy deleted", "policy.delete"}, + SetupKeyCreated: {"Setup key created", "setupkey.add"}, + SetupKeyUpdated: {"Setup key updated", "setupkey.update"}, + SetupKeyRevoked: {"Setup key revoked", "setupkey.revoke"}, + SetupKeyOverused: {"Setup key overused", "setupkey.overuse"}, + GroupCreated: {"Group created", "group.add"}, + GroupUpdated: {"Group updated", "group.update"}, + GroupAddedToPeer: {"Group added to peer", "peer.group.add"}, + GroupRemovedFromPeer: {"Group removed from peer", "peer.group.delete"}, + GroupAddedToUser: {"Group added to user", "user.group.add"}, + GroupRemovedFromUser: {"Group removed from user", "user.group.delete"}, + UserRoleUpdated: {"User role updated", "user.role.update"}, + GroupAddedToSetupKey: {"Group added to setup key", "setupkey.group.add"}, + GroupRemovedFromSetupKey: {"Group removed from user setup key", "setupkey.group.delete"}, + GroupAddedToDisabledManagementGroups: {"Group added to disabled management DNS setting", "dns.setting.disabled.management.group.add"}, + GroupRemovedFromDisabledManagementGroups: {"Group removed from disabled management DNS setting", "dns.setting.disabled.management.group.delete"}, + RouteCreated: {"Route created", "route.add"}, + RouteRemoved: {"Route deleted", "route.delete"}, + RouteUpdated: {"Route updated", "route.update"}, + PeerSSHEnabled: {"Peer SSH server enabled", "peer.ssh.enable"}, + PeerSSHDisabled: {"Peer SSH server disabled", "peer.ssh.disable"}, + PeerRenamed: {"Peer renamed", "peer.rename"}, + PeerLoginExpirationEnabled: {"Peer login expiration enabled", "peer.login.expiration.enable"}, + PeerLoginExpirationDisabled: {"Peer login expiration disabled", "peer.login.expiration.disable"}, + PeerInactivityExpirationEnabled: {"Peer inactivity expiration enabled", "peer.inactivity.expiration.enable"}, + PeerInactivityExpirationDisabled: {"Peer inactivity expiration disabled", "peer.inactivity.expiration.disable"}, + NameserverGroupCreated: {"Nameserver group created", "nameserver.group.add"}, + NameserverGroupDeleted: {"Nameserver group deleted", "nameserver.group.delete"}, + NameserverGroupUpdated: {"Nameserver group updated", "nameserver.group.update"}, AccountPeerLoginExpirationDurationUpdated: {"Account peer login expiration duration updated", "account.setting.peer.login.expiration.update"}, AccountPeerLoginExpirationEnabled: {"Account peer login expiration enabled", "account.setting.peer.login.expiration.enable"}, AccountPeerLoginExpirationDisabled: {"Account peer login expiration disabled", "account.setting.peer.login.expiration.disable"}, diff --git a/management/server/file_store.go b/management/server/file_store.go index ac5ea178e7f..6bb6ccb9f92 100644 --- a/management/server/file_store.go +++ b/management/server/file_store.go @@ -122,10 +122,10 @@ func restore(ctx context.Context, file string) (*FileStore, error) { for accountID, account := range store.Accounts { if account.Settings == nil { account.Settings = &Settings{ - PeerLoginExpirationEnabled: false, - PeerInactivityExpirationEnabled: false, - PeerLoginExpiration: DefaultPeerLoginExpiration, - PeerInactivityExpiration: DefaultPeerInactivityExpiration, + PeerLoginExpirationEnabled: false, + PeerInactivityExpirationEnabled: false, + PeerLoginExpiration: DefaultPeerLoginExpiration, + PeerInactivityExpiration: DefaultPeerInactivityExpiration, } } diff --git a/management/server/http/accounts_handler.go b/management/server/http/accounts_handler.go index ffa5b9a287c..dc40b670f4d 100644 --- a/management/server/http/accounts_handler.go +++ b/management/server/http/accounts_handler.go @@ -74,9 +74,11 @@ func (h *AccountsHandler) UpdateAccount(w http.ResponseWriter, r *http.Request) } settings := &server.Settings{ - PeerLoginExpirationEnabled: req.Settings.PeerLoginExpirationEnabled, - PeerLoginExpiration: time.Duration(float64(time.Second.Nanoseconds()) * float64(req.Settings.PeerLoginExpiration)), - RegularUsersViewBlocked: req.Settings.RegularUsersViewBlocked, + PeerLoginExpirationEnabled: req.Settings.PeerLoginExpirationEnabled, + PeerLoginExpiration: time.Duration(float64(time.Second.Nanoseconds()) * float64(req.Settings.PeerLoginExpiration)), + PeerInactivityExpirationEnabled: req.Settings.PeerInactivityExpirationEnabled, + PeerInactivityExpiration: time.Duration(float64(time.Second.Nanoseconds()) * float64(req.Settings.PeerInactivityExpiration)), + RegularUsersViewBlocked: req.Settings.RegularUsersViewBlocked, } if req.Settings.Extra != nil { diff --git a/management/server/http/api/openapi.yml b/management/server/http/api/openapi.yml index 45887dc2ece..b6f4244ad8a 100644 --- a/management/server/http/api/openapi.yml +++ b/management/server/http/api/openapi.yml @@ -54,6 +54,14 @@ components: description: Period of time after which peer login expires (seconds). type: integer example: 43200 + peer_inactivity_expiration_enabled: + description: Enables or disables peer inactivity expiration globally. After peer's session has expired the user has to log in (authenticate). Applies only to peers that were added by a user (interactive SSO login). + type: boolean + example: true + peer_inactivity_expiration: + description: Period of time of inactivity after which peer session expires (seconds). + type: integer + example: 43200 regular_users_view_blocked: description: Allows blocking regular users from viewing parts of the system. type: boolean @@ -243,6 +251,9 @@ components: login_expiration_enabled: type: boolean example: false + inactivity_expiration_enabled: + type: boolean + example: false approval_required: description: (Cloud only) Indicates whether peer needs approval type: boolean diff --git a/management/server/http/api/types.gen.go b/management/server/http/api/types.gen.go index 77a6c643d7a..b75a0680784 100644 --- a/management/server/http/api/types.gen.go +++ b/management/server/http/api/types.gen.go @@ -1,6 +1,6 @@ // Package api provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/deepmap/oapi-codegen version v1.11.1-0.20220912230023-4a1477f6a8ba DO NOT EDIT. +// Code generated by unknown module path version unknown version DO NOT EDIT. package api import ( @@ -202,6 +202,12 @@ type AccountSettings struct { // JwtGroupsEnabled Allows extract groups from JWT claim and add it to account groups. JwtGroupsEnabled *bool `json:"jwt_groups_enabled,omitempty"` + // PeerInactivityExpiration Period of time of inactivity after which peer session expires (seconds). + PeerInactivityExpiration int `json:"peer_inactivity_expiration"` + + // PeerInactivityExpirationEnabled Enables or disables peer inactivity expiration globally. After peer's session has expired the user has to log in (authenticate). Applies only to peers that were added by a user (interactive SSO login). + PeerInactivityExpirationEnabled bool `json:"peer_inactivity_expiration_enabled"` + // PeerLoginExpiration Period of time after which peer login expires (seconds). PeerLoginExpiration int `json:"peer_login_expiration"` @@ -538,6 +544,8 @@ type Peer struct { // LoginExpirationEnabled Indicates whether peer login expiration has been enabled or not LoginExpirationEnabled bool `json:"login_expiration_enabled"` + InactivityExpirationEnabled bool `json:"inactivity_expiration_enabled"` + // LoginExpired Indicates whether peer's login expired or not LoginExpired bool `json:"login_expired"` @@ -610,6 +618,8 @@ type PeerBase struct { // LoginExpirationEnabled Indicates whether peer login expiration has been enabled or not LoginExpirationEnabled bool `json:"login_expiration_enabled"` + InactivityExpirationEnabled bool `json:"inactivity_expiration_enabled"` + // LoginExpired Indicates whether peer's login expired or not LoginExpired bool `json:"login_expired"` @@ -685,6 +695,8 @@ type PeerBatch struct { // LoginExpirationEnabled Indicates whether peer login expiration has been enabled or not LoginExpirationEnabled bool `json:"login_expiration_enabled"` + InactivityExpirationEnabled bool `json:"inactivity_expiration_enabled"` + // LoginExpired Indicates whether peer's login expired or not LoginExpired bool `json:"login_expired"` @@ -736,6 +748,7 @@ type PeerRequest struct { // ApprovalRequired (Cloud only) Indicates whether peer needs approval ApprovalRequired *bool `json:"approval_required,omitempty"` LoginExpirationEnabled bool `json:"login_expiration_enabled"` + InactivityExpirationEnabled bool `json:"inactivity_expiration_enabled"` Name string `json:"name"` SshEnabled bool `json:"ssh_enabled"` } diff --git a/management/server/http/peers_handler.go b/management/server/http/peers_handler.go index 913d424d1b4..02c816852ac 100644 --- a/management/server/http/peers_handler.go +++ b/management/server/http/peers_handler.go @@ -88,10 +88,11 @@ func (h *PeersHandler) updatePeer(ctx context.Context, account *server.Account, } update := &nbpeer.Peer{ - ID: peerID, - SSHEnabled: req.SshEnabled, - Name: req.Name, - LoginExpirationEnabled: req.LoginExpirationEnabled, + ID: peerID, + SSHEnabled: req.SshEnabled, + Name: req.Name, + LoginExpirationEnabled: req.LoginExpirationEnabled, + InactivityExpirationEnabled: req.InactivityExpirationEnabled, } if req.ApprovalRequired != nil { @@ -277,30 +278,31 @@ func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsD } return &api.Peer{ - Id: peer.ID, - Name: peer.Name, - Ip: peer.IP.String(), - ConnectionIp: peer.Location.ConnectionIP.String(), - Connected: peer.Status.Connected, - LastSeen: peer.Status.LastSeen, - Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion), - KernelVersion: peer.Meta.KernelVersion, - GeonameId: int(peer.Location.GeoNameID), - Version: peer.Meta.WtVersion, - Groups: groupsInfo, - SshEnabled: peer.SSHEnabled, - Hostname: peer.Meta.Hostname, - UserId: peer.UserID, - UiVersion: peer.Meta.UIVersion, - DnsLabel: fqdn(peer, dnsDomain), - LoginExpirationEnabled: peer.LoginExpirationEnabled, - LastLogin: peer.LastLogin, - LoginExpired: peer.Status.LoginExpired, - AccessiblePeers: accessiblePeer, - ApprovalRequired: !approved, - CountryCode: peer.Location.CountryCode, - CityName: peer.Location.CityName, - SerialNumber: peer.Meta.SystemSerialNumber, + Id: peer.ID, + Name: peer.Name, + Ip: peer.IP.String(), + ConnectionIp: peer.Location.ConnectionIP.String(), + Connected: peer.Status.Connected, + LastSeen: peer.Status.LastSeen, + Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion), + KernelVersion: peer.Meta.KernelVersion, + GeonameId: int(peer.Location.GeoNameID), + Version: peer.Meta.WtVersion, + Groups: groupsInfo, + SshEnabled: peer.SSHEnabled, + Hostname: peer.Meta.Hostname, + UserId: peer.UserID, + UiVersion: peer.Meta.UIVersion, + DnsLabel: fqdn(peer, dnsDomain), + LoginExpirationEnabled: peer.LoginExpirationEnabled, + InactivityExpirationEnabled: peer.InactivityExpirationEnabled, + LastLogin: peer.LastLogin, + LoginExpired: peer.Status.LoginExpired, + AccessiblePeers: accessiblePeer, + ApprovalRequired: !approved, + CountryCode: peer.Location.CountryCode, + CityName: peer.Location.CityName, + SerialNumber: peer.Meta.SystemSerialNumber, } } @@ -311,29 +313,30 @@ func toPeerListItemResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dn } return &api.PeerBatch{ - Id: peer.ID, - Name: peer.Name, - Ip: peer.IP.String(), - ConnectionIp: peer.Location.ConnectionIP.String(), - Connected: peer.Status.Connected, - LastSeen: peer.Status.LastSeen, - Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion), - KernelVersion: peer.Meta.KernelVersion, - GeonameId: int(peer.Location.GeoNameID), - Version: peer.Meta.WtVersion, - Groups: groupsInfo, - SshEnabled: peer.SSHEnabled, - Hostname: peer.Meta.Hostname, - UserId: peer.UserID, - UiVersion: peer.Meta.UIVersion, - DnsLabel: fqdn(peer, dnsDomain), - LoginExpirationEnabled: peer.LoginExpirationEnabled, - LastLogin: peer.LastLogin, - LoginExpired: peer.Status.LoginExpired, - AccessiblePeersCount: accessiblePeersCount, - CountryCode: peer.Location.CountryCode, - CityName: peer.Location.CityName, - SerialNumber: peer.Meta.SystemSerialNumber, + Id: peer.ID, + Name: peer.Name, + Ip: peer.IP.String(), + ConnectionIp: peer.Location.ConnectionIP.String(), + Connected: peer.Status.Connected, + LastSeen: peer.Status.LastSeen, + Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion), + KernelVersion: peer.Meta.KernelVersion, + GeonameId: int(peer.Location.GeoNameID), + Version: peer.Meta.WtVersion, + Groups: groupsInfo, + SshEnabled: peer.SSHEnabled, + Hostname: peer.Meta.Hostname, + UserId: peer.UserID, + UiVersion: peer.Meta.UIVersion, + DnsLabel: fqdn(peer, dnsDomain), + LoginExpirationEnabled: peer.LoginExpirationEnabled, + InactivityExpirationEnabled: peer.InactivityExpirationEnabled, + LastLogin: peer.LastLogin, + LoginExpired: peer.Status.LoginExpired, + AccessiblePeersCount: accessiblePeersCount, + CountryCode: peer.Location.CountryCode, + CityName: peer.Location.CityName, + SerialNumber: peer.Meta.SystemSerialNumber, } } diff --git a/management/server/peer.go b/management/server/peer.go index 3148fe848c9..f930cfe79ed 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -159,7 +159,7 @@ func (am *DefaultAccountManager) MarkPeerConnected(ctx context.Context, peerPubK return nil } -// UpdatePeer updates peer. Only Peer.Name, Peer.SSHEnabled, and Peer.LoginExpirationEnabled can be updated. +// UpdatePeer updates peer. Only Peer.Name, Peer.SSHEnabled, Peer.LoginExpirationEnabled and Peer.InactivityExpirationEnabled can be updated. func (am *DefaultAccountManager) UpdatePeer(ctx context.Context, accountID, userID string, update *nbpeer.Peer) (*nbpeer.Peer, error) { unlock := am.Store.AcquireWriteLockByUID(ctx, accountID) defer unlock() From 4a14c37d44a603fc462dbfdff1240e9a1d9b7564 Mon Sep 17 00:00:00 2001 From: ctrl-zzz Date: Tue, 11 Jun 2024 20:49:12 +0200 Subject: [PATCH 03/13] docs: update comment --- management/server/account.go | 11 +++++++++-- management/server/http/api/types.gen.go | 13 ++++++++----- management/server/peer/peer.go | 8 +++++++- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/management/server/account.go b/management/server/account.go index d0bd67f9f36..d1467de9244 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -50,7 +50,7 @@ const ( CacheExpirationMax = 7 * 24 * 3600 * time.Second // 7 days CacheExpirationMin = 3 * 24 * 3600 * time.Second // 3 days DefaultPeerLoginExpiration = 24 * time.Hour - DefaultPeerInactivityExpiration = 20 * time.Second + DefaultPeerInactivityExpiration = 1 * time.Second ) type userLoggedInOnce bool @@ -192,8 +192,11 @@ type Settings struct { // Applies to all peers that have Peer.LoginExpirationEnabled set to true. PeerLoginExpiration time.Duration + // PeerInactivityExpirationEnabled globally enables or disables peer inactivity expiration PeerInactivityExpirationEnabled bool + // PeerInactivityExpiration is a setting that indicates when peer inactivity expires. + // Applies to all peers that have Peer.PeerInactivityExpirationEnabled set to true. PeerInactivityExpiration time.Duration // RegularUsersViewBlocked allows to block regular users from viewing even their own peers and some UI elements @@ -591,6 +594,7 @@ func (a *Account) GetPeersWithExpiration() []*nbpeer.Peer { return peers } +// GetExpiredPeers returns peers that have been expired by inactivity func (a *Account) GetInactivePeers() []*nbpeer.Peer { var peers []*nbpeer.Peer for _, inactivePeer := range a.GetPeersWithInactivity() { @@ -602,6 +606,9 @@ func (a *Account) GetInactivePeers() []*nbpeer.Peer { return peers } +// GetNextInactivePeerExpiration 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 (a *Account) GetNextInactivePeerExpiration() (time.Duration, bool) { peersWithExpiry := a.GetPeersWithInactivity() if len(peersWithExpiry) == 0 { @@ -610,7 +617,7 @@ func (a *Account) GetNextInactivePeerExpiration() (time.Duration, bool) { 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 { + if peer.Status.LoginExpired || peer.Status.Connected { continue } _, duration := peer.SessionExpired(a.Settings.PeerInactivityExpiration) diff --git a/management/server/http/api/types.gen.go b/management/server/http/api/types.gen.go index b75a0680784..03807d1e20b 100644 --- a/management/server/http/api/types.gen.go +++ b/management/server/http/api/types.gen.go @@ -544,6 +544,7 @@ type Peer struct { // LoginExpirationEnabled Indicates whether peer login expiration has been enabled or not LoginExpirationEnabled bool `json:"login_expiration_enabled"` + // InactivityExpirationEnabled Indicates whether peer inactivity expiration has been enabled or not InactivityExpirationEnabled bool `json:"inactivity_expiration_enabled"` // LoginExpired Indicates whether peer's login expired or not @@ -618,6 +619,7 @@ type PeerBase struct { // LoginExpirationEnabled Indicates whether peer login expiration has been enabled or not LoginExpirationEnabled bool `json:"login_expiration_enabled"` + // InactivityExpirationEnabled Indicates whether peer inactivity expiration has been enabled or not InactivityExpirationEnabled bool `json:"inactivity_expiration_enabled"` // LoginExpired Indicates whether peer's login expired or not @@ -695,6 +697,7 @@ type PeerBatch struct { // LoginExpirationEnabled Indicates whether peer login expiration has been enabled or not LoginExpirationEnabled bool `json:"login_expiration_enabled"` + // InactivityExpirationEnabled Indicates whether peer inactivity expiration has been enabled or not InactivityExpirationEnabled bool `json:"inactivity_expiration_enabled"` // LoginExpired Indicates whether peer's login expired or not @@ -746,11 +749,11 @@ type PeerNetworkRangeCheckAction string // PeerRequest defines model for PeerRequest. type PeerRequest struct { // ApprovalRequired (Cloud only) Indicates whether peer needs approval - ApprovalRequired *bool `json:"approval_required,omitempty"` - LoginExpirationEnabled bool `json:"login_expiration_enabled"` - InactivityExpirationEnabled bool `json:"inactivity_expiration_enabled"` - Name string `json:"name"` - SshEnabled bool `json:"ssh_enabled"` + ApprovalRequired *bool `json:"approval_required,omitempty"` + LoginExpirationEnabled bool `json:"login_expiration_enabled"` + InactivityExpirationEnabled bool `json:"inactivity_expiration_enabled"` + Name string `json:"name"` + SshEnabled bool `json:"ssh_enabled"` } // PersonalAccessToken defines model for PersonalAccessToken. diff --git a/management/server/peer/peer.go b/management/server/peer/peer.go index 5c5b28d503c..a6b8364924e 100644 --- a/management/server/peer/peer.go +++ b/management/server/peer/peer.go @@ -37,7 +37,7 @@ type Peer struct { SSHEnabled bool // LoginExpirationEnabled indicates whether peer's login expiration is enabled and once expired the peer has to re-login. // Works with LastLogin - LoginExpirationEnabled bool + LoginExpirationEnabled bool InactivityExpirationEnabled bool // LastLogin the time when peer performed last login operation LastLogin time.Time @@ -221,6 +221,12 @@ func (p *Peer) MarkLoginExpired(expired bool) { p.Status = newStatus } +// SessionExpired indicates whether the peer's session has expired or not. +// If Peer.LastLogin plus the expiresIn duration has happened already; then login has expired. +// Return true if a session has expired, false otherwise, and time left to expiration (negative when expired). +// Session expiration can be disabled/enabled on a Peer level via Peer.LoginExpirationEnabled property. +// Session expiration can also be disabled/enabled globally on the Account level via Settings.PeerLoginExpirationEnabled. +// Only peers added by interactive SSO login can be expired. func (p *Peer) SessionExpired(expiresIn time.Duration) (bool, time.Duration) { if !p.AddedWithSSOLogin() || !p.InactivityExpirationEnabled || p.Status.Connected { return false, 0 From 3d1ce1f013a1594953568ad1a22e9dd4bed20c07 Mon Sep 17 00:00:00 2001 From: ctrl-zzz Date: Mon, 17 Jun 2024 15:40:19 +0200 Subject: [PATCH 04/13] test: add tests for inactivity functionalities --- management/server/account_test.go | 315 ++++++++++++++++++++++++++++++ management/server/peer_test.go | 62 ++++++ 2 files changed, 377 insertions(+) diff --git a/management/server/account_test.go b/management/server/account_test.go index 03b5fa83efd..d141a5276dd 100644 --- a/management/server/account_test.go +++ b/management/server/account_test.go @@ -1928,6 +1928,90 @@ func TestAccount_GetExpiredPeers(t *testing.T) { } } +func TestAccount_GetInactivePeers(t *testing.T) { + type test struct { + name string + peers map[string]*nbpeer.Peer + expectedPeers map[string]struct{} + } + testCases := []test{ + { + name: "Peers with inactivity expiration disabled, no expired peers", + peers: map[string]*nbpeer.Peer{ + "peer-1": { + InactivityExpirationEnabled: false, + }, + "peer-2": { + InactivityExpirationEnabled: false, + }, + }, + expectedPeers: map[string]struct{}{}, + }, + { + name: "Two peers expired", + peers: map[string]*nbpeer.Peer{ + "peer-1": { + ID: "peer-1", + InactivityExpirationEnabled: true, + Status: &nbpeer.PeerStatus{ + LastSeen: time.Now().UTC().Add(-45 * time.Second), + Connected: false, + LoginExpired: false, + }, + LastLogin: time.Now().UTC().Add(-30 * time.Minute), + UserID: userID, + }, + "peer-2": { + ID: "peer-2", + InactivityExpirationEnabled: true, + Status: &nbpeer.PeerStatus{ + LastSeen: time.Now().UTC().Add(-45 * time.Second), + Connected: false, + LoginExpired: false, + }, + LastLogin: time.Now().UTC().Add(-2 * time.Hour), + UserID: userID, + }, + "peer-3": { + ID: "peer-3", + InactivityExpirationEnabled: true, + Status: &nbpeer.PeerStatus{ + LastSeen: time.Now().UTC(), + Connected: true, + LoginExpired: false, + }, + LastLogin: time.Now().UTC().Add(-1 * time.Hour), + UserID: userID, + }, + }, + expectedPeers: map[string]struct{}{ + "peer-1": {}, + "peer-2": {}, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + account := &Account{ + Peers: testCase.peers, + Settings: &Settings{ + PeerInactivityExpirationEnabled: true, + PeerInactivityExpiration: time.Second, + }, + } + + expiredPeers := account.GetInactivePeers() + assert.Len(t, expiredPeers, len(testCase.expectedPeers)) + for _, peer := range expiredPeers { + if _, ok := testCase.expectedPeers[peer.ID]; !ok { + t.Fatalf("expected to have peer %s expired", peer.ID) + } + } + }) + } +} + func TestAccount_GetPeersWithExpiration(t *testing.T) { type test struct { name string @@ -1997,6 +2081,75 @@ func TestAccount_GetPeersWithExpiration(t *testing.T) { } } +func TestAccount_GetPeersWithInactivity(t *testing.T) { + type test struct { + name string + peers map[string]*nbpeer.Peer + expectedPeers map[string]struct{} + } + + testCases := []test{ + { + name: "No account peers, no peers with expiration", + peers: map[string]*nbpeer.Peer{}, + expectedPeers: map[string]struct{}{}, + }, + { + name: "Peers with login expiration disabled, no peers with expiration", + peers: map[string]*nbpeer.Peer{ + "peer-1": { + InactivityExpirationEnabled: false, + UserID: userID, + }, + "peer-2": { + InactivityExpirationEnabled: false, + UserID: userID, + }, + }, + expectedPeers: map[string]struct{}{}, + }, + { + name: "Peers with login expiration enabled, return peers with expiration", + peers: map[string]*nbpeer.Peer{ + "peer-1": { + ID: "peer-1", + InactivityExpirationEnabled: true, + UserID: userID, + }, + "peer-2": { + InactivityExpirationEnabled: false, + UserID: userID, + }, + }, + expectedPeers: map[string]struct{}{ + "peer-1": {}, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + account := &Account{ + Peers: testCase.peers, + } + + actual := account.GetPeersWithInactivity() + assert.Len(t, actual, len(testCase.expectedPeers)) + if len(testCase.expectedPeers) > 0 { + for k := range testCase.expectedPeers { + contains := false + for _, peer := range actual { + if k == peer.ID { + contains = true + } + } + assert.True(t, contains) + } + } + }) + } +} + func TestAccount_GetNextPeerExpiration(t *testing.T) { type test struct { name string @@ -2158,6 +2311,168 @@ func TestAccount_GetNextPeerExpiration(t *testing.T) { } } +func TestAccount_GetNextInactivePeerExpiration(t *testing.T) { + type test struct { + name string + peers map[string]*nbpeer.Peer + expiration time.Duration + expirationEnabled bool + expectedNextRun bool + expectedNextExpiration time.Duration + } + + expectedNextExpiration := time.Minute + testCases := []test{ + { + name: "No peers, no expiration", + peers: map[string]*nbpeer.Peer{}, + expiration: time.Second, + expirationEnabled: false, + expectedNextRun: false, + expectedNextExpiration: time.Duration(0), + }, + { + name: "No connected peers, no expiration", + peers: map[string]*nbpeer.Peer{ + "peer-1": { + Status: &nbpeer.PeerStatus{ + Connected: false, + }, + InactivityExpirationEnabled: false, + UserID: userID, + }, + "peer-2": { + Status: &nbpeer.PeerStatus{ + Connected: false, + }, + InactivityExpirationEnabled: false, + UserID: userID, + }, + }, + expiration: time.Second, + expirationEnabled: false, + expectedNextRun: false, + expectedNextExpiration: time.Duration(0), + }, + { + name: "Connected peers with disabled expiration, no expiration", + peers: map[string]*nbpeer.Peer{ + "peer-1": { + Status: &nbpeer.PeerStatus{ + Connected: true, + }, + InactivityExpirationEnabled: false, + UserID: userID, + }, + "peer-2": { + Status: &nbpeer.PeerStatus{ + Connected: true, + }, + InactivityExpirationEnabled: false, + UserID: userID, + }, + }, + expiration: time.Second, + expirationEnabled: false, + expectedNextRun: false, + expectedNextExpiration: time.Duration(0), + }, + { + name: "Expired peers, no expiration", + peers: map[string]*nbpeer.Peer{ + "peer-1": { + Status: &nbpeer.PeerStatus{ + Connected: true, + LoginExpired: true, + }, + InactivityExpirationEnabled: true, + UserID: userID, + }, + "peer-2": { + Status: &nbpeer.PeerStatus{ + Connected: true, + LoginExpired: true, + }, + InactivityExpirationEnabled: true, + UserID: userID, + }, + }, + expiration: time.Second, + expirationEnabled: false, + expectedNextRun: false, + expectedNextExpiration: time.Duration(0), + }, + { + name: "To be expired peer, return expiration", + peers: map[string]*nbpeer.Peer{ + "peer-1": { + Status: &nbpeer.PeerStatus{ + Connected: false, + LoginExpired: false, + LastSeen: time.Now().Add(-1 * time.Second), + }, + InactivityExpirationEnabled: true, + LastLogin: time.Now().UTC(), + UserID: userID, + }, + "peer-2": { + Status: &nbpeer.PeerStatus{ + Connected: true, + LoginExpired: true, + }, + InactivityExpirationEnabled: true, + UserID: userID, + }, + }, + expiration: time.Minute, + expirationEnabled: false, + expectedNextRun: true, + expectedNextExpiration: expectedNextExpiration, + }, + { + name: "Peers added with setup keys, no expiration", + peers: map[string]*nbpeer.Peer{ + "peer-1": { + Status: &nbpeer.PeerStatus{ + Connected: true, + LoginExpired: false, + }, + InactivityExpirationEnabled: true, + SetupKey: "key", + }, + "peer-2": { + Status: &nbpeer.PeerStatus{ + Connected: true, + LoginExpired: false, + }, + InactivityExpirationEnabled: true, + SetupKey: "key", + }, + }, + expiration: time.Second, + expirationEnabled: false, + expectedNextRun: false, + expectedNextExpiration: time.Duration(0), + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + account := &Account{ + Peers: testCase.peers, + Settings: &Settings{PeerInactivityExpiration: testCase.expiration, PeerInactivityExpirationEnabled: testCase.expirationEnabled}, + } + + expiration, ok := account.GetNextInactivePeerExpiration() + assert.Equal(t, testCase.expectedNextRun, ok) + if testCase.expectedNextRun { + assert.True(t, expiration >= 0 && expiration <= testCase.expectedNextExpiration) + } else { + assert.Equal(t, expiration, testCase.expectedNextExpiration) + } + }) + } +} + func TestAccount_SetJWTGroups(t *testing.T) { // create a new account account := &Account{ diff --git a/management/server/peer_test.go b/management/server/peer_test.go index 91843651563..fb275fd866c 100644 --- a/management/server/peer_test.go +++ b/management/server/peer_test.go @@ -78,6 +78,68 @@ func TestPeer_LoginExpired(t *testing.T) { } } +func TestPeer_SessionExpired(t *testing.T) { + tt := []struct { + name string + expirationEnabled bool + lastLogin time.Time + connected bool + expected bool + accountSettings *Settings + }{ + { + name: "Peer Inactivity Expiration Disabled. Peer Inactivity Should Not Expire", + expirationEnabled: false, + connected: false, + lastLogin: time.Now().UTC().Add(-1 * time.Second), + accountSettings: &Settings{ + PeerInactivityExpirationEnabled: true, + PeerInactivityExpiration: time.Hour, + }, + expected: false, + }, + { + name: "Peer Inactivity Should Expire", + expirationEnabled: true, + connected: false, + lastLogin: time.Now().UTC().Add(-1 * time.Second), + accountSettings: &Settings{ + PeerInactivityExpirationEnabled: true, + PeerInactivityExpiration: time.Second, + }, + expected: true, + }, + { + name: "Peer Inactivity Should Not Expire", + expirationEnabled: true, + connected: true, + lastLogin: time.Now().UTC(), + accountSettings: &Settings{ + PeerInactivityExpirationEnabled: true, + PeerInactivityExpiration: time.Second, + }, + expected: false, + }, + } + + for _, c := range tt { + t.Run(c.name, func(t *testing.T) { + peerStatus := &nbpeer.PeerStatus{ + Connected: c.connected, + } + peer := &nbpeer.Peer{ + InactivityExpirationEnabled: c.expirationEnabled, + LastLogin: c.lastLogin, + Status: peerStatus, + UserID: userID, + } + + expired, _ := peer.SessionExpired(c.accountSettings.PeerInactivityExpiration) + assert.Equal(t, expired, c.expected) + }) + } +} + func TestAccountManager_GetNetworkMap(t *testing.T) { manager, err := createManager(t) if err != nil { From 7de6b2df07347595989f6ab7db8f31b1a6789f66 Mon Sep 17 00:00:00 2001 From: ctrl-zzz Date: Mon, 17 Jun 2024 15:41:27 +0200 Subject: [PATCH 05/13] docs: add comments for inactivity functionalities --- management/server/account.go | 3 ++- management/server/peer/peer.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/management/server/account.go b/management/server/account.go index d1467de9244..b4e90911b1b 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -638,7 +638,8 @@ func (a *Account) GetNextInactivePeerExpiration() (time.Duration, bool) { return *nextExpiry, true } -// GetPeersWithInactivityPeers returns a list of peers that have Peer.InactivityExpirationEnabled set to true and that were added by a user +// GetPeersWithInactivity +// returns a list of peers that have Peer.InactivityExpirationEnabled set to true and that were added by a user func (a *Account) GetPeersWithInactivity() []*nbpeer.Peer { peers := make([]*nbpeer.Peer, 0) for _, peer := range a.Peers { diff --git a/management/server/peer/peer.go b/management/server/peer/peer.go index a6b8364924e..01935ba11b4 100644 --- a/management/server/peer/peer.go +++ b/management/server/peer/peer.go @@ -222,7 +222,7 @@ func (p *Peer) MarkLoginExpired(expired bool) { } // SessionExpired indicates whether the peer's session has expired or not. -// If Peer.LastLogin plus the expiresIn duration has happened already; then login has expired. +// If Peer.LastLogin plus the expiresIn duration has happened already; then session has expired. // Return true if a session has expired, false otherwise, and time left to expiration (negative when expired). // Session expiration can be disabled/enabled on a Peer level via Peer.LoginExpirationEnabled property. // Session expiration can also be disabled/enabled globally on the Account level via Settings.PeerLoginExpirationEnabled. From b309583798ba39e46a57b323cd301b68185dab32 Mon Sep 17 00:00:00 2001 From: ctrl-zzz Date: Thu, 18 Jul 2024 22:02:50 +0200 Subject: [PATCH 06/13] style: change indentations --- management/server/account.go | 42 +++++---- management/server/activity/codes.go | 81 ++++++++-------- management/server/file_store.go | 5 +- management/server/http/accounts_handler.go | 7 +- management/server/http/api/openapi.yml | 1 + management/server/http/peers_handler.go | 105 +++++++++++---------- management/server/peer.go | 33 +++---- management/server/peer/peer.go | 38 ++++---- 8 files changed, 163 insertions(+), 149 deletions(-) diff --git a/management/server/account.go b/management/server/account.go index b4e90911b1b..8c45338cdc7 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -44,12 +44,13 @@ import ( ) const ( - PublicCategory = "public" - PrivateCategory = "private" - UnknownCategory = "unknown" - CacheExpirationMax = 7 * 24 * 3600 * time.Second // 7 days - CacheExpirationMin = 3 * 24 * 3600 * time.Second // 3 days - DefaultPeerLoginExpiration = 24 * time.Hour + PublicCategory = "public" + PrivateCategory = "private" + UnknownCategory = "unknown" + CacheExpirationMax = 7 * 24 * 3600 * time.Second // 7 days + CacheExpirationMin = 3 * 24 * 3600 * time.Second // 3 days + DefaultPeerLoginExpiration = 24 * time.Hour + DefaultPeerInactivityExpiration = 1 * time.Second ) @@ -171,8 +172,9 @@ type DefaultAccountManager struct { // singleAccountModeDomain is a domain to use in singleAccountMode setup singleAccountModeDomain string // dnsDomain is used for peer resolution. This is appended to the peer's name - dnsDomain string - peerLoginExpiry Scheduler + dnsDomain string + peerLoginExpiry Scheduler + peerInactivityExpiry Scheduler // userDeleteFromIDPEnabled allows to delete user from IDP when user is deleted from account @@ -222,15 +224,16 @@ type Settings struct { // Copy copies the Settings struct func (s *Settings) Copy() *Settings { settings := &Settings{ - PeerLoginExpirationEnabled: s.PeerLoginExpirationEnabled, - PeerLoginExpiration: s.PeerLoginExpiration, + PeerLoginExpirationEnabled: s.PeerLoginExpirationEnabled, + PeerLoginExpiration: s.PeerLoginExpiration, + JWTGroupsEnabled: s.JWTGroupsEnabled, + JWTGroupsClaimName: s.JWTGroupsClaimName, + GroupsPropagationEnabled: s.GroupsPropagationEnabled, + JWTAllowGroups: s.JWTAllowGroups, + RegularUsersViewBlocked: s.RegularUsersViewBlocked, + PeerInactivityExpirationEnabled: s.PeerInactivityExpirationEnabled, PeerInactivityExpiration: s.PeerInactivityExpiration, - JWTGroupsEnabled: s.JWTGroupsEnabled, - JWTGroupsClaimName: s.JWTGroupsClaimName, - GroupsPropagationEnabled: s.GroupsPropagationEnabled, - JWTAllowGroups: s.JWTAllowGroups, - RegularUsersViewBlocked: s.RegularUsersViewBlocked, } if s.Extra != nil { settings.Extra = s.Extra.Copy() @@ -2287,12 +2290,13 @@ func newAccountWithId(ctx context.Context, accountID, userID, domain string) *Ac NameServerGroups: nameServersGroups, DNSSettings: dnsSettings, Settings: &Settings{ - PeerLoginExpirationEnabled: true, - PeerLoginExpiration: DefaultPeerLoginExpiration, + PeerLoginExpirationEnabled: true, + PeerLoginExpiration: DefaultPeerLoginExpiration, + GroupsPropagationEnabled: true, + RegularUsersViewBlocked: true, + PeerInactivityExpirationEnabled: true, PeerInactivityExpiration: DefaultPeerInactivityExpiration, - GroupsPropagationEnabled: true, - RegularUsersViewBlocked: true, }, } diff --git a/management/server/activity/codes.go b/management/server/activity/codes.go index c6325118468..bef5379e86c 100644 --- a/management/server/activity/codes.go +++ b/management/server/activity/codes.go @@ -149,46 +149,44 @@ const ( ) var activityMap = map[Activity]Code{ - PeerAddedByUser: {"Peer added", "user.peer.add"}, - PeerAddedWithSetupKey: {"Peer added", "setupkey.peer.add"}, - UserJoined: {"User joined", "user.join"}, - UserInvited: {"User invited", "user.invite"}, - AccountCreated: {"Account created", "account.create"}, - PeerRemovedByUser: {"Peer deleted", "user.peer.delete"}, - RuleAdded: {"Rule added", "rule.add"}, - RuleUpdated: {"Rule updated", "rule.update"}, - RuleRemoved: {"Rule deleted", "rule.delete"}, - PolicyAdded: {"Policy added", "policy.add"}, - PolicyUpdated: {"Policy updated", "policy.update"}, - PolicyRemoved: {"Policy deleted", "policy.delete"}, - SetupKeyCreated: {"Setup key created", "setupkey.add"}, - SetupKeyUpdated: {"Setup key updated", "setupkey.update"}, - SetupKeyRevoked: {"Setup key revoked", "setupkey.revoke"}, - SetupKeyOverused: {"Setup key overused", "setupkey.overuse"}, - GroupCreated: {"Group created", "group.add"}, - GroupUpdated: {"Group updated", "group.update"}, - GroupAddedToPeer: {"Group added to peer", "peer.group.add"}, - GroupRemovedFromPeer: {"Group removed from peer", "peer.group.delete"}, - GroupAddedToUser: {"Group added to user", "user.group.add"}, - GroupRemovedFromUser: {"Group removed from user", "user.group.delete"}, - UserRoleUpdated: {"User role updated", "user.role.update"}, - GroupAddedToSetupKey: {"Group added to setup key", "setupkey.group.add"}, - GroupRemovedFromSetupKey: {"Group removed from user setup key", "setupkey.group.delete"}, - GroupAddedToDisabledManagementGroups: {"Group added to disabled management DNS setting", "dns.setting.disabled.management.group.add"}, - GroupRemovedFromDisabledManagementGroups: {"Group removed from disabled management DNS setting", "dns.setting.disabled.management.group.delete"}, - RouteCreated: {"Route created", "route.add"}, - RouteRemoved: {"Route deleted", "route.delete"}, - RouteUpdated: {"Route updated", "route.update"}, - PeerSSHEnabled: {"Peer SSH server enabled", "peer.ssh.enable"}, - PeerSSHDisabled: {"Peer SSH server disabled", "peer.ssh.disable"}, - PeerRenamed: {"Peer renamed", "peer.rename"}, - PeerLoginExpirationEnabled: {"Peer login expiration enabled", "peer.login.expiration.enable"}, - PeerLoginExpirationDisabled: {"Peer login expiration disabled", "peer.login.expiration.disable"}, - PeerInactivityExpirationEnabled: {"Peer inactivity expiration enabled", "peer.inactivity.expiration.enable"}, - PeerInactivityExpirationDisabled: {"Peer inactivity expiration disabled", "peer.inactivity.expiration.disable"}, - NameserverGroupCreated: {"Nameserver group created", "nameserver.group.add"}, - NameserverGroupDeleted: {"Nameserver group deleted", "nameserver.group.delete"}, - NameserverGroupUpdated: {"Nameserver group updated", "nameserver.group.update"}, + PeerAddedByUser: {"Peer added", "user.peer.add"}, + PeerAddedWithSetupKey: {"Peer added", "setupkey.peer.add"}, + UserJoined: {"User joined", "user.join"}, + UserInvited: {"User invited", "user.invite"}, + AccountCreated: {"Account created", "account.create"}, + PeerRemovedByUser: {"Peer deleted", "user.peer.delete"}, + RuleAdded: {"Rule added", "rule.add"}, + RuleUpdated: {"Rule updated", "rule.update"}, + RuleRemoved: {"Rule deleted", "rule.delete"}, + PolicyAdded: {"Policy added", "policy.add"}, + PolicyUpdated: {"Policy updated", "policy.update"}, + PolicyRemoved: {"Policy deleted", "policy.delete"}, + SetupKeyCreated: {"Setup key created", "setupkey.add"}, + SetupKeyUpdated: {"Setup key updated", "setupkey.update"}, + SetupKeyRevoked: {"Setup key revoked", "setupkey.revoke"}, + SetupKeyOverused: {"Setup key overused", "setupkey.overuse"}, + GroupCreated: {"Group created", "group.add"}, + GroupUpdated: {"Group updated", "group.update"}, + GroupAddedToPeer: {"Group added to peer", "peer.group.add"}, + GroupRemovedFromPeer: {"Group removed from peer", "peer.group.delete"}, + GroupAddedToUser: {"Group added to user", "user.group.add"}, + GroupRemovedFromUser: {"Group removed from user", "user.group.delete"}, + UserRoleUpdated: {"User role updated", "user.role.update"}, + GroupAddedToSetupKey: {"Group added to setup key", "setupkey.group.add"}, + GroupRemovedFromSetupKey: {"Group removed from user setup key", "setupkey.group.delete"}, + GroupAddedToDisabledManagementGroups: {"Group added to disabled management DNS setting", "dns.setting.disabled.management.group.add"}, + GroupRemovedFromDisabledManagementGroups: {"Group removed from disabled management DNS setting", "dns.setting.disabled.management.group.delete"}, + RouteCreated: {"Route created", "route.add"}, + RouteRemoved: {"Route deleted", "route.delete"}, + RouteUpdated: {"Route updated", "route.update"}, + PeerSSHEnabled: {"Peer SSH server enabled", "peer.ssh.enable"}, + PeerSSHDisabled: {"Peer SSH server disabled", "peer.ssh.disable"}, + PeerRenamed: {"Peer renamed", "peer.rename"}, + PeerLoginExpirationEnabled: {"Peer login expiration enabled", "peer.login.expiration.enable"}, + PeerLoginExpirationDisabled: {"Peer login expiration disabled", "peer.login.expiration.disable"}, + NameserverGroupCreated: {"Nameserver group created", "nameserver.group.add"}, + NameserverGroupDeleted: {"Nameserver group deleted", "nameserver.group.delete"}, + NameserverGroupUpdated: {"Nameserver group updated", "nameserver.group.update"}, AccountPeerLoginExpirationDurationUpdated: {"Account peer login expiration duration updated", "account.setting.peer.login.expiration.update"}, AccountPeerLoginExpirationEnabled: {"Account peer login expiration enabled", "account.setting.peer.login.expiration.enable"}, AccountPeerLoginExpirationDisabled: {"Account peer login expiration disabled", "account.setting.peer.login.expiration.disable"}, @@ -214,6 +212,9 @@ var activityMap = map[Activity]Code{ PostureCheckCreated: {"Posture check created", "posture.check.created"}, PostureCheckUpdated: {"Posture check updated", "posture.check.updated"}, PostureCheckDeleted: {"Posture check deleted", "posture.check.deleted"}, + + PeerInactivityExpirationEnabled: {"Peer inactivity expiration enabled", "peer.inactivity.expiration.enable"}, + PeerInactivityExpirationDisabled: {"Peer inactivity expiration disabled", "peer.inactivity.expiration.disable"}, } // StringCode returns a string code of the activity diff --git a/management/server/file_store.go b/management/server/file_store.go index 6bb6ccb9f92..7054162ebd7 100644 --- a/management/server/file_store.go +++ b/management/server/file_store.go @@ -122,9 +122,10 @@ func restore(ctx context.Context, file string) (*FileStore, error) { for accountID, account := range store.Accounts { if account.Settings == nil { account.Settings = &Settings{ - PeerLoginExpirationEnabled: false, + PeerLoginExpirationEnabled: false, + PeerLoginExpiration: DefaultPeerLoginExpiration, + PeerInactivityExpirationEnabled: false, - PeerLoginExpiration: DefaultPeerLoginExpiration, PeerInactivityExpiration: DefaultPeerInactivityExpiration, } } diff --git a/management/server/http/accounts_handler.go b/management/server/http/accounts_handler.go index dc40b670f4d..b79ecaa955b 100644 --- a/management/server/http/accounts_handler.go +++ b/management/server/http/accounts_handler.go @@ -74,11 +74,12 @@ func (h *AccountsHandler) UpdateAccount(w http.ResponseWriter, r *http.Request) } settings := &server.Settings{ - PeerLoginExpirationEnabled: req.Settings.PeerLoginExpirationEnabled, - PeerLoginExpiration: time.Duration(float64(time.Second.Nanoseconds()) * float64(req.Settings.PeerLoginExpiration)), + PeerLoginExpirationEnabled: req.Settings.PeerLoginExpirationEnabled, + PeerLoginExpiration: time.Duration(float64(time.Second.Nanoseconds()) * float64(req.Settings.PeerLoginExpiration)), + RegularUsersViewBlocked: req.Settings.RegularUsersViewBlocked, + PeerInactivityExpirationEnabled: req.Settings.PeerInactivityExpirationEnabled, PeerInactivityExpiration: time.Duration(float64(time.Second.Nanoseconds()) * float64(req.Settings.PeerInactivityExpiration)), - RegularUsersViewBlocked: req.Settings.RegularUsersViewBlocked, } if req.Settings.Extra != nil { diff --git a/management/server/http/api/openapi.yml b/management/server/http/api/openapi.yml index b6f4244ad8a..55f6d6fa7ae 100644 --- a/management/server/http/api/openapi.yml +++ b/management/server/http/api/openapi.yml @@ -88,6 +88,7 @@ components: $ref: '#/components/schemas/AccountExtraSettings' required: - peer_login_expiration_enabled + - peer_inactivity_expiration_enabled - peer_login_expiration - regular_users_view_blocked AccountExtraSettings: diff --git a/management/server/http/peers_handler.go b/management/server/http/peers_handler.go index 02c816852ac..88fd945e48d 100644 --- a/management/server/http/peers_handler.go +++ b/management/server/http/peers_handler.go @@ -88,10 +88,11 @@ func (h *PeersHandler) updatePeer(ctx context.Context, account *server.Account, } update := &nbpeer.Peer{ - ID: peerID, - SSHEnabled: req.SshEnabled, - Name: req.Name, - LoginExpirationEnabled: req.LoginExpirationEnabled, + ID: peerID, + SSHEnabled: req.SshEnabled, + Name: req.Name, + LoginExpirationEnabled: req.LoginExpirationEnabled, + InactivityExpirationEnabled: req.InactivityExpirationEnabled, } @@ -278,31 +279,32 @@ func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsD } return &api.Peer{ - Id: peer.ID, - Name: peer.Name, - Ip: peer.IP.String(), - ConnectionIp: peer.Location.ConnectionIP.String(), - Connected: peer.Status.Connected, - LastSeen: peer.Status.LastSeen, - Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion), - KernelVersion: peer.Meta.KernelVersion, - GeonameId: int(peer.Location.GeoNameID), - Version: peer.Meta.WtVersion, - Groups: groupsInfo, - SshEnabled: peer.SSHEnabled, - Hostname: peer.Meta.Hostname, - UserId: peer.UserID, - UiVersion: peer.Meta.UIVersion, - DnsLabel: fqdn(peer, dnsDomain), - LoginExpirationEnabled: peer.LoginExpirationEnabled, + Id: peer.ID, + Name: peer.Name, + Ip: peer.IP.String(), + ConnectionIp: peer.Location.ConnectionIP.String(), + Connected: peer.Status.Connected, + LastSeen: peer.Status.LastSeen, + Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion), + KernelVersion: peer.Meta.KernelVersion, + GeonameId: int(peer.Location.GeoNameID), + Version: peer.Meta.WtVersion, + Groups: groupsInfo, + SshEnabled: peer.SSHEnabled, + Hostname: peer.Meta.Hostname, + UserId: peer.UserID, + UiVersion: peer.Meta.UIVersion, + DnsLabel: fqdn(peer, dnsDomain), + LoginExpirationEnabled: peer.LoginExpirationEnabled, + LastLogin: peer.LastLogin, + LoginExpired: peer.Status.LoginExpired, + AccessiblePeers: accessiblePeer, + ApprovalRequired: !approved, + CountryCode: peer.Location.CountryCode, + CityName: peer.Location.CityName, + SerialNumber: peer.Meta.SystemSerialNumber, + InactivityExpirationEnabled: peer.InactivityExpirationEnabled, - LastLogin: peer.LastLogin, - LoginExpired: peer.Status.LoginExpired, - AccessiblePeers: accessiblePeer, - ApprovalRequired: !approved, - CountryCode: peer.Location.CountryCode, - CityName: peer.Location.CityName, - SerialNumber: peer.Meta.SystemSerialNumber, } } @@ -313,30 +315,31 @@ func toPeerListItemResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dn } return &api.PeerBatch{ - Id: peer.ID, - Name: peer.Name, - Ip: peer.IP.String(), - ConnectionIp: peer.Location.ConnectionIP.String(), - Connected: peer.Status.Connected, - LastSeen: peer.Status.LastSeen, - Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion), - KernelVersion: peer.Meta.KernelVersion, - GeonameId: int(peer.Location.GeoNameID), - Version: peer.Meta.WtVersion, - Groups: groupsInfo, - SshEnabled: peer.SSHEnabled, - Hostname: peer.Meta.Hostname, - UserId: peer.UserID, - UiVersion: peer.Meta.UIVersion, - DnsLabel: fqdn(peer, dnsDomain), - LoginExpirationEnabled: peer.LoginExpirationEnabled, + Id: peer.ID, + Name: peer.Name, + Ip: peer.IP.String(), + ConnectionIp: peer.Location.ConnectionIP.String(), + Connected: peer.Status.Connected, + LastSeen: peer.Status.LastSeen, + Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion), + KernelVersion: peer.Meta.KernelVersion, + GeonameId: int(peer.Location.GeoNameID), + Version: peer.Meta.WtVersion, + Groups: groupsInfo, + SshEnabled: peer.SSHEnabled, + Hostname: peer.Meta.Hostname, + UserId: peer.UserID, + UiVersion: peer.Meta.UIVersion, + DnsLabel: fqdn(peer, dnsDomain), + LoginExpirationEnabled: peer.LoginExpirationEnabled, + LastLogin: peer.LastLogin, + LoginExpired: peer.Status.LoginExpired, + AccessiblePeersCount: accessiblePeersCount, + CountryCode: peer.Location.CountryCode, + CityName: peer.Location.CityName, + SerialNumber: peer.Meta.SystemSerialNumber, + InactivityExpirationEnabled: peer.InactivityExpirationEnabled, - LastLogin: peer.LastLogin, - LoginExpired: peer.Status.LoginExpired, - AccessiblePeersCount: accessiblePeersCount, - CountryCode: peer.Location.CountryCode, - CityName: peer.Location.CityName, - SerialNumber: peer.Meta.SystemSerialNumber, } } diff --git a/management/server/peer.go b/management/server/peer.go index f930cfe79ed..6bf0dde8b3f 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -466,23 +466,24 @@ func (am *DefaultAccountManager) AddPeer(ctx context.Context, setupKey, userID s registrationTime := time.Now().UTC() newPeer := &nbpeer.Peer{ - ID: xid.New().String(), - Key: peer.Key, - SetupKey: upperKey, - IP: nextIp, - Meta: peer.Meta, - Name: peer.Meta.Hostname, - DNSLabel: newLabel, - UserID: userID, - Status: &nbpeer.PeerStatus{Connected: false, LastSeen: registrationTime}, - SSHEnabled: false, - SSHKey: peer.SSHKey, - LastLogin: registrationTime, - CreatedAt: registrationTime, - LoginExpirationEnabled: addedByUser, + ID: xid.New().String(), + Key: peer.Key, + SetupKey: upperKey, + IP: nextIp, + Meta: peer.Meta, + Name: peer.Meta.Hostname, + DNSLabel: newLabel, + UserID: userID, + Status: &nbpeer.PeerStatus{Connected: false, LastSeen: registrationTime}, + SSHEnabled: false, + SSHKey: peer.SSHKey, + LastLogin: registrationTime, + CreatedAt: registrationTime, + LoginExpirationEnabled: addedByUser, + Ephemeral: ephemeral, + Location: peer.Location, + InactivityExpirationEnabled: addedByUser, - Ephemeral: ephemeral, - Location: peer.Location, } if am.geo != nil && newPeer.Location.ConnectionIP != nil { diff --git a/management/server/peer/peer.go b/management/server/peer/peer.go index 01935ba11b4..9a53459a8c8 100644 --- a/management/server/peer/peer.go +++ b/management/server/peer/peer.go @@ -37,7 +37,8 @@ type Peer struct { SSHEnabled bool // LoginExpirationEnabled indicates whether peer's login expiration is enabled and once expired the peer has to re-login. // Works with LastLogin - LoginExpirationEnabled bool + LoginExpirationEnabled bool + InactivityExpirationEnabled bool // LastLogin the time when peer performed last login operation LastLogin time.Time @@ -171,24 +172,25 @@ func (p *Peer) Copy() *Peer { peerStatus = p.Status.Copy() } return &Peer{ - ID: p.ID, - AccountID: p.AccountID, - Key: p.Key, - SetupKey: p.SetupKey, - IP: p.IP, - Meta: p.Meta, - Name: p.Name, - DNSLabel: p.DNSLabel, - Status: peerStatus, - UserID: p.UserID, - SSHKey: p.SSHKey, - SSHEnabled: p.SSHEnabled, - LoginExpirationEnabled: p.LoginExpirationEnabled, + ID: p.ID, + AccountID: p.AccountID, + Key: p.Key, + SetupKey: p.SetupKey, + IP: p.IP, + Meta: p.Meta, + Name: p.Name, + DNSLabel: p.DNSLabel, + Status: peerStatus, + UserID: p.UserID, + SSHKey: p.SSHKey, + SSHEnabled: p.SSHEnabled, + LoginExpirationEnabled: p.LoginExpirationEnabled, + LastLogin: p.LastLogin, + CreatedAt: p.CreatedAt, + Ephemeral: p.Ephemeral, + Location: p.Location, + InactivityExpirationEnabled: p.InactivityExpirationEnabled, - LastLogin: p.LastLogin, - CreatedAt: p.CreatedAt, - Ephemeral: p.Ephemeral, - Location: p.Location, } } From 47d28a3f2795ca516316db04e008ee58513a80be Mon Sep 17 00:00:00 2001 From: ctrl-zzz Date: Thu, 25 Jul 2024 16:50:14 +0200 Subject: [PATCH 07/13] refactor: fix typos in comments --- management/server/account.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/management/server/account.go b/management/server/account.go index 8c45338cdc7..285888f57fe 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -597,7 +597,7 @@ func (a *Account) GetPeersWithExpiration() []*nbpeer.Peer { return peers } -// GetExpiredPeers returns peers that have been expired by inactivity +// GetInactivePeers returns peers that have been expired by inactivity func (a *Account) GetInactivePeers() []*nbpeer.Peer { var peers []*nbpeer.Peer for _, inactivePeer := range a.GetPeersWithInactivity() { @@ -641,8 +641,7 @@ func (a *Account) GetNextInactivePeerExpiration() (time.Duration, bool) { return *nextExpiry, true } -// GetPeersWithInactivity -// returns a list of peers that have Peer.InactivityExpirationEnabled set to true and that were added by a user +// GetPeersWithInactivity eturns a list of peers that have Peer.InactivityExpirationEnabled set to true and that were added by a user func (a *Account) GetPeersWithInactivity() []*nbpeer.Peer { peers := make([]*nbpeer.Peer, 0) for _, peer := range a.Peers { From 342c93923a6a3dae0f16ae1abfeac7963a0e785e Mon Sep 17 00:00:00 2001 From: ctrl-zzz Date: Tue, 20 Aug 2024 19:54:44 +0200 Subject: [PATCH 08/13] refactor: reduce cognitive complexity of markpeerconnected and updateaccountsettings methods --- management/server/account.go | 25 +++++++++++++++++-------- management/server/peer.go | 12 +++++++----- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/management/server/account.go b/management/server/account.go index 285888f57fe..931cf865655 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -1164,6 +1164,22 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco am.checkAndSchedulePeerLoginExpiration(ctx, account) } + err = am.handleInactivityExpirationSettings(ctx, account, oldSettings, newSettings, userID, accountID) + if err != nil { + return nil, err + } + + updatedAccount := account.UpdateSettings(newSettings) + + err = am.Store.SaveAccount(ctx, account) + if err != nil { + return nil, err + } + + return updatedAccount, nil +} + +func (am *DefaultAccountManager) handleInactivityExpirationSettings(ctx context.Context, account *Account, oldSettings, newSettings *Settings, userID, accountID string) error { if oldSettings.PeerInactivityExpirationEnabled != newSettings.PeerInactivityExpirationEnabled { event := activity.AccountPeerInactivityExpirationEnabled if !newSettings.PeerInactivityExpirationEnabled { @@ -1180,14 +1196,7 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco am.checkAndSchedulePeerInactivityExpiration(ctx, account) } - updatedAccount := account.UpdateSettings(newSettings) - - err = am.Store.SaveAccount(ctx, account) - if err != nil { - return nil, err - } - - return updatedAccount, nil + return nil } func (am *DefaultAccountManager) peerLoginExpirationJob(ctx context.Context, accountID string) func() (time.Duration, bool) { diff --git a/management/server/peer.go b/management/server/peer.go index 6bf0dde8b3f..63a9817b5fb 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -142,12 +142,14 @@ func (am *DefaultAccountManager) MarkPeerConnected(ctx context.Context, peerPubK return err } - if peer.AddedWithSSOLogin() && peer.LoginExpirationEnabled && account.Settings.PeerLoginExpirationEnabled { - am.checkAndSchedulePeerLoginExpiration(ctx, account) - } + if peer.AddedWithSSOLogin() { + if peer.LoginExpirationEnabled && account.Settings.PeerLoginExpirationEnabled { + am.checkAndSchedulePeerLoginExpiration(ctx, account) + } - if peer.AddedWithSSOLogin() && peer.InactivityExpirationEnabled && account.Settings.PeerInactivityExpirationEnabled { - am.checkAndSchedulePeerInactivityExpiration(ctx, account) + if peer.InactivityExpirationEnabled && account.Settings.PeerInactivityExpirationEnabled { + am.checkAndSchedulePeerInactivityExpiration(ctx, account) + } } if oldStatus.LoginExpired { From d90dff7b76acf9c03954c3e571ca64bc3dd43d0d Mon Sep 17 00:00:00 2001 From: Pascal Fischer Date: Tue, 8 Oct 2024 16:58:01 +0200 Subject: [PATCH 09/13] fix merge conflicts --- management/server/account.go | 2 +- management/server/http/api/openapi.yml | 9 ++++- management/server/http/api/types.gen.go | 21 +++++++++-- management/server/http/peers_handler.go | 50 ++++++++++++------------- 4 files changed, 51 insertions(+), 31 deletions(-) diff --git a/management/server/account.go b/management/server/account.go index 59a50147b94..18ef245ae0c 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -1243,7 +1243,7 @@ func (am *DefaultAccountManager) checkAndSchedulePeerLoginExpiration(ctx context // peerInactivityExpirationJob marks login expired for all inactive peers and returns the minimum duration in which the next peer of the account will expire by inactivity if found func (am *DefaultAccountManager) peerInactivityExpirationJob(ctx context.Context, accountID string) func() (time.Duration, bool) { return func() (time.Duration, bool) { - unlock := am.Store.AcquireAccountWriteLock(ctx, accountID) + unlock := am.Store.AcquireWriteLockByUID(ctx, accountID) defer unlock() account, err := am.Store.GetAccount(ctx, accountID) diff --git a/management/server/http/api/openapi.yml b/management/server/http/api/openapi.yml index d9f394af098..9d51482481a 100644 --- a/management/server/http/api/openapi.yml +++ b/management/server/http/api/openapi.yml @@ -88,8 +88,9 @@ components: $ref: '#/components/schemas/AccountExtraSettings' required: - peer_login_expiration_enabled - - peer_inactivity_expiration_enabled - peer_login_expiration + - peer_inactivity_expiration_enabled + - peer_inactivity_expiration - regular_users_view_blocked AccountExtraSettings: type: object @@ -263,6 +264,7 @@ components: - name - ssh_enabled - login_expiration_enabled + - inactivity_expiration_enabled Peer: allOf: - $ref: '#/components/schemas/PeerMinimum' @@ -339,6 +341,10 @@ components: type: string format: date-time example: "2023-05-05T09:00:35.477782Z" + inactivity_expiration_enabled: + description: Indicates whether peer inactivity expiration has been enabled or not + type: boolean + example: false approval_required: description: (Cloud only) Indicates whether peer needs approval type: boolean @@ -366,6 +372,7 @@ components: - last_seen - login_expiration_enabled - login_expired + - inactivity_expiration_enabled - os - ssh_enabled - user_id diff --git a/management/server/http/api/types.gen.go b/management/server/http/api/types.gen.go index 570ec03c5bc..e2870d5d8ef 100644 --- a/management/server/http/api/types.gen.go +++ b/management/server/http/api/types.gen.go @@ -220,6 +220,12 @@ type AccountSettings struct { // JwtGroupsEnabled Allows extract groups from JWT claim and add it to account groups. JwtGroupsEnabled *bool `json:"jwt_groups_enabled,omitempty"` + // PeerInactivityExpiration Period of time of inactivity after which peer session expires (seconds). + PeerInactivityExpiration int `json:"peer_inactivity_expiration"` + + // PeerInactivityExpirationEnabled Enables or disables peer inactivity expiration globally. After peer's session has expired the user has to log in (authenticate). Applies only to peers that were added by a user (interactive SSO login). + PeerInactivityExpirationEnabled bool `json:"peer_inactivity_expiration_enabled"` + // PeerLoginExpiration Period of time after which peer login expires (seconds). PeerLoginExpiration int `json:"peer_login_expiration"` @@ -538,6 +544,9 @@ type Peer struct { // Id Peer ID Id string `json:"id"` + // InactivityExpirationEnabled Indicates whether peer inactivity expiration has been enabled or not + InactivityExpirationEnabled bool `json:"inactivity_expiration_enabled"` + // Ip Peer's IP address Ip string `json:"ip"` @@ -613,6 +622,9 @@ type PeerBatch struct { // Id Peer ID Id string `json:"id"` + // InactivityExpirationEnabled Indicates whether peer inactivity expiration has been enabled or not + InactivityExpirationEnabled bool `json:"inactivity_expiration_enabled"` + // Ip Peer's IP address Ip string `json:"ip"` @@ -677,10 +689,11 @@ type PeerNetworkRangeCheckAction string // PeerRequest defines model for PeerRequest. type PeerRequest struct { // ApprovalRequired (Cloud only) Indicates whether peer needs approval - ApprovalRequired *bool `json:"approval_required,omitempty"` - LoginExpirationEnabled bool `json:"login_expiration_enabled"` - Name string `json:"name"` - SshEnabled bool `json:"ssh_enabled"` + ApprovalRequired *bool `json:"approval_required,omitempty"` + InactivityExpirationEnabled bool `json:"inactivity_expiration_enabled"` + LoginExpirationEnabled bool `json:"login_expiration_enabled"` + Name string `json:"name"` + SshEnabled bool `json:"ssh_enabled"` } // PersonalAccessToken defines model for PersonalAccessToken. diff --git a/management/server/http/peers_handler.go b/management/server/http/peers_handler.go index 16a1c7c1f5f..a5856a0e43c 100644 --- a/management/server/http/peers_handler.go +++ b/management/server/http/peers_handler.go @@ -7,6 +7,8 @@ import ( "net/http" "github.com/gorilla/mux" + log "github.com/sirupsen/logrus" + "github.com/netbirdio/netbird/management/server" nbgroup "github.com/netbirdio/netbird/management/server/group" "github.com/netbirdio/netbird/management/server/http/api" @@ -14,7 +16,6 @@ import ( "github.com/netbirdio/netbird/management/server/jwtclaims" nbpeer "github.com/netbirdio/netbird/management/server/peer" "github.com/netbirdio/netbird/management/server/status" - log "github.com/sirupsen/logrus" ) // PeersHandler is a handler that returns peers of the account @@ -333,30 +334,29 @@ func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsD } return &api.Peer{ - Id: peer.ID, - Name: peer.Name, - Ip: peer.IP.String(), - ConnectionIp: peer.Location.ConnectionIP.String(), - Connected: peer.Status.Connected, - LastSeen: peer.Status.LastSeen, - Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion), - KernelVersion: peer.Meta.KernelVersion, - GeonameId: int(peer.Location.GeoNameID), - Version: peer.Meta.WtVersion, - Groups: groupsInfo, - SshEnabled: peer.SSHEnabled, - Hostname: peer.Meta.Hostname, - UserId: peer.UserID, - UiVersion: peer.Meta.UIVersion, - DnsLabel: fqdn(peer, dnsDomain), - LoginExpirationEnabled: peer.LoginExpirationEnabled, - LastLogin: peer.LastLogin, - LoginExpired: peer.Status.LoginExpired, - ApprovalRequired: !approved, - CountryCode: peer.Location.CountryCode, - CityName: peer.Location.CityName, - SerialNumber: peer.Meta.SystemSerialNumber, - + Id: peer.ID, + Name: peer.Name, + Ip: peer.IP.String(), + ConnectionIp: peer.Location.ConnectionIP.String(), + Connected: peer.Status.Connected, + LastSeen: peer.Status.LastSeen, + Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion), + KernelVersion: peer.Meta.KernelVersion, + GeonameId: int(peer.Location.GeoNameID), + Version: peer.Meta.WtVersion, + Groups: groupsInfo, + SshEnabled: peer.SSHEnabled, + Hostname: peer.Meta.Hostname, + UserId: peer.UserID, + UiVersion: peer.Meta.UIVersion, + DnsLabel: fqdn(peer, dnsDomain), + LoginExpirationEnabled: peer.LoginExpirationEnabled, + LastLogin: peer.LastLogin, + LoginExpired: peer.Status.LoginExpired, + ApprovalRequired: !approved, + CountryCode: peer.Location.CountryCode, + CityName: peer.Location.CityName, + SerialNumber: peer.Meta.SystemSerialNumber, InactivityExpirationEnabled: peer.InactivityExpirationEnabled, } } From 9ea0cf47ee40f84560bfd595adcf41df5aacce27 Mon Sep 17 00:00:00 2001 From: Pascal Fischer Date: Tue, 8 Oct 2024 17:02:36 +0200 Subject: [PATCH 10/13] update default inactivity expiration --- management/server/account.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/management/server/account.go b/management/server/account.go index 18ef245ae0c..7f6ab5be19d 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -51,7 +51,7 @@ const ( CacheExpirationMin = 3 * 24 * 3600 * time.Second // 3 days DefaultPeerLoginExpiration = 24 * time.Hour - DefaultPeerInactivityExpiration = 1 * time.Second + DefaultPeerInactivityExpiration = 10 * time.Minute ) type userLoggedInOnce bool From 9504872e6c69debcadbf2ecd5806374ceafa0551 Mon Sep 17 00:00:00 2001 From: Pascal Fischer Date: Thu, 10 Oct 2024 12:47:08 +0200 Subject: [PATCH 11/13] extract updating status --- management/server/account.go | 3 +- management/server/peer.go | 81 ++++++++++++++++++++---------------- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/management/server/account.go b/management/server/account.go index 7f6ab5be19d..72da5686c19 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -634,7 +634,7 @@ func (a *Account) GetInactivePeers() []*nbpeer.Peer { // GetNextInactivePeerExpiration 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. +// This function only considers peers that haven't been expired yet and that are not connected. func (a *Account) GetNextInactivePeerExpiration() (time.Duration, bool) { peersWithExpiry := a.GetPeersWithInactivity() if len(peersWithExpiry) == 0 { @@ -642,7 +642,6 @@ func (a *Account) GetNextInactivePeerExpiration() (time.Duration, bool) { } 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 } diff --git a/management/server/peer.go b/management/server/peer.go index 3c04ba3ffef..a4c7e126675 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -110,6 +110,31 @@ func (am *DefaultAccountManager) MarkPeerConnected(ctx context.Context, peerPubK return err } + expired, err := am.updatePeerStatusAndLocation(ctx, peer, connected, realIP, account) + if err != nil { + return err + } + + if peer.AddedWithSSOLogin() { + if peer.LoginExpirationEnabled && account.Settings.PeerLoginExpirationEnabled { + am.checkAndSchedulePeerLoginExpiration(ctx, account) + } + + if peer.InactivityExpirationEnabled && account.Settings.PeerInactivityExpirationEnabled { + am.checkAndSchedulePeerInactivityExpiration(ctx, account) + } + } + + if expired { + // we need to update other peers because when peer login expires all other peers are notified to disconnect from + // the expired one. Here we notify them that connection is now allowed again. + am.updateAccountPeers(ctx, account) + } + + return nil +} + +func (am *DefaultAccountManager) updatePeerStatusAndLocation(ctx context.Context, peer *nbpeer.Peer, connected bool, realIP net.IP, account *Account) (bool, error) { oldStatus := peer.Status.Copy() newStatus := oldStatus newStatus.LastSeen = time.Now().UTC() @@ -138,28 +163,12 @@ func (am *DefaultAccountManager) MarkPeerConnected(ctx context.Context, peerPubK account.UpdatePeer(peer) - err = am.Store.SavePeerStatus(account.Id, peer.ID, *newStatus) + err := am.Store.SavePeerStatus(account.Id, peer.ID, *newStatus) if err != nil { - return err + return false, err } - if peer.AddedWithSSOLogin() { - if peer.LoginExpirationEnabled && account.Settings.PeerLoginExpirationEnabled { - am.checkAndSchedulePeerLoginExpiration(ctx, account) - } - - if peer.InactivityExpirationEnabled && account.Settings.PeerInactivityExpirationEnabled { - am.checkAndSchedulePeerInactivityExpiration(ctx, account) - } - } - - if oldStatus.LoginExpired { - // we need to update other peers because when peer login expires all other peers are notified to disconnect from - // the expired one. Here we notify them that connection is now allowed again. - am.updateAccountPeers(ctx, account) - } - - return nil + return oldStatus.LoginExpired, nil } // UpdatePeer updates peer. Only Peer.Name, Peer.SSHEnabled, Peer.LoginExpirationEnabled and Peer.InactivityExpirationEnabled can be updated. @@ -467,23 +476,23 @@ func (am *DefaultAccountManager) AddPeer(ctx context.Context, setupKey, userID s registrationTime := time.Now().UTC() newPeer = &nbpeer.Peer{ - ID: xid.New().String(), - AccountID: accountID, - Key: peer.Key, - SetupKey: upperKey, - IP: freeIP, - Meta: peer.Meta, - Name: peer.Meta.Hostname, - DNSLabel: freeLabel, - UserID: userID, - Status: &nbpeer.PeerStatus{Connected: false, LastSeen: registrationTime}, - SSHEnabled: false, - SSHKey: peer.SSHKey, - LastLogin: registrationTime, - CreatedAt: registrationTime, - LoginExpirationEnabled: addedByUser, - Ephemeral: ephemeral, - Location: peer.Location, + ID: xid.New().String(), + AccountID: accountID, + Key: peer.Key, + SetupKey: upperKey, + IP: freeIP, + Meta: peer.Meta, + Name: peer.Meta.Hostname, + DNSLabel: freeLabel, + UserID: userID, + Status: &nbpeer.PeerStatus{Connected: false, LastSeen: registrationTime}, + SSHEnabled: false, + SSHKey: peer.SSHKey, + LastLogin: registrationTime, + CreatedAt: registrationTime, + LoginExpirationEnabled: addedByUser, + Ephemeral: ephemeral, + Location: peer.Location, InactivityExpirationEnabled: addedByUser, } opEvent.TargetID = newPeer.ID From 95863a4017334c6630e9780daaab01cc123041d3 Mon Sep 17 00:00:00 2001 From: Pascal Fischer Date: Thu, 10 Oct 2024 14:16:31 +0200 Subject: [PATCH 12/13] add missing codes --- management/server/activity/codes.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/management/server/activity/codes.go b/management/server/activity/codes.go index bef5379e86c..188494241c6 100644 --- a/management/server/activity/codes.go +++ b/management/server/activity/codes.go @@ -215,6 +215,10 @@ var activityMap = map[Activity]Code{ PeerInactivityExpirationEnabled: {"Peer inactivity expiration enabled", "peer.inactivity.expiration.enable"}, PeerInactivityExpirationDisabled: {"Peer inactivity expiration disabled", "peer.inactivity.expiration.disable"}, + + AccountPeerInactivityExpirationEnabled: {"Account peer inactivity expiration enabled", "account.peer.inactivity.expiration.enable"}, + AccountPeerInactivityExpirationDisabled: {"Account peer inactivity expiration disabled", "account.peer.inactivity.expiration.disable"}, + AccountPeerInactivityExpirationDurationUpdated: {"Account peer inactivity expiration duration updated", "account.peer.inactivity.expiration.update"}, } // StringCode returns a string code of the activity From 93626baf26b18ac19864a8cc09ccb8e7314095cd Mon Sep 17 00:00:00 2001 From: Pascal Fischer Date: Thu, 10 Oct 2024 14:27:36 +0200 Subject: [PATCH 13/13] disable feature for new accounts --- management/server/account.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/management/server/account.go b/management/server/account.go index 72da5686c19..74981280448 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -2469,7 +2469,7 @@ func newAccountWithId(ctx context.Context, accountID, userID, domain string) *Ac GroupsPropagationEnabled: true, RegularUsersViewBlocked: true, - PeerInactivityExpirationEnabled: true, + PeerInactivityExpirationEnabled: false, PeerInactivityExpiration: DefaultPeerInactivityExpiration, }, }