2021-07-30 17:46:38 +02:00
package server
import (
2022-06-05 21:36:42 +02:00
"context"
2023-03-16 15:57:44 +01:00
"crypto/sha256"
2023-03-29 15:21:53 +02:00
b64 "encoding/base64"
2022-05-09 14:30:20 +02:00
"fmt"
2023-03-16 15:57:44 +01:00
"hash/crc32"
2023-02-03 21:47:20 +01:00
"math/rand"
"net"
"net/netip"
"reflect"
"regexp"
"strings"
"sync"
"time"
2022-10-13 18:26:31 +02:00
"github.com/eko/gocache/v3/cache"
cacheStore "github.com/eko/gocache/v3/store"
2023-03-16 15:57:44 +01:00
gocache "github.com/patrickmn/go-cache"
"github.com/rs/xid"
log "github.com/sirupsen/logrus"
2023-12-13 11:18:35 +01:00
"github.com/netbirdio/management-integrations/additions"
2023-05-16 12:57:56 +02:00
"github.com/netbirdio/netbird/base62"
2022-09-30 13:47:11 +02:00
nbdns "github.com/netbirdio/netbird/dns"
2023-11-28 14:34:57 +01:00
"github.com/netbirdio/netbird/management/server/account"
2023-01-02 15:11:32 +01:00
"github.com/netbirdio/netbird/management/server/activity"
2024-02-20 09:59:56 +01:00
"github.com/netbirdio/netbird/management/server/geolocation"
2022-03-26 12:08:54 +01:00
"github.com/netbirdio/netbird/management/server/idp"
"github.com/netbirdio/netbird/management/server/jwtclaims"
2023-11-28 13:45:26 +01:00
nbpeer "github.com/netbirdio/netbird/management/server/peer"
2024-02-20 09:59:56 +01:00
"github.com/netbirdio/netbird/management/server/posture"
2022-11-11 20:36:45 +01:00
"github.com/netbirdio/netbird/management/server/status"
2022-08-18 18:22:15 +02:00
"github.com/netbirdio/netbird/route"
2021-07-30 17:46:38 +02:00
)
2022-03-01 15:22:18 +01:00
const (
2023-02-13 12:21:02 +01:00
PublicCategory = "public"
PrivateCategory = "private"
UnknownCategory = "unknown"
2023-06-27 16:51:05 +02:00
GroupIssuedAPI = "api"
GroupIssuedJWT = "jwt"
2023-11-01 11:04:17 +01:00
GroupIssuedIntegration = "integration"
2023-02-13 12:21:02 +01:00
CacheExpirationMax = 7 * 24 * 3600 * time . Second // 7 days
CacheExpirationMin = 3 * 24 * 3600 * time . Second // 3 days
DefaultPeerLoginExpiration = 24 * time . Hour
2022-03-01 15:22:18 +01:00
)
2023-11-13 14:04:18 +01:00
type ExternalCacheManager cache . CacheInterface [ * idp . UserData ]
2022-10-13 18:26:31 +02:00
func cacheEntryExpiration ( ) time . Duration {
r := rand . Intn ( int ( CacheExpirationMax . Milliseconds ( ) - CacheExpirationMin . Milliseconds ( ) ) ) + int ( CacheExpirationMin . Milliseconds ( ) )
return time . Duration ( r ) * time . Millisecond
}
2022-02-22 11:28:19 +01:00
type AccountManager interface {
GetOrCreateAccountByUser ( userId , domain string ) ( * Account , error )
2022-12-05 13:09:59 +01:00
CreateSetupKey ( accountID string , keyName string , keyType SetupKeyType , expiresIn time . Duration ,
2023-09-04 11:37:39 +02:00
autoGroups [ ] string , usageLimit int , userID string , ephemeral bool ) ( * SetupKey , error )
2023-01-02 15:11:32 +01:00
SaveSetupKey ( accountID string , key * SetupKey , userID string ) ( * SetupKey , error )
2023-05-11 18:09:36 +02:00
CreateUser ( accountID , initiatorUserID string , key * UserInfo ) ( * UserInfo , error )
DeleteUser ( accountID , initiatorUserID string , targetUserID string ) error
2023-07-03 12:20:19 +02:00
InviteUser ( accountID string , initiatorUserID string , targetUserID string ) error
2022-11-05 10:24:50 +01:00
ListSetupKeys ( accountID , userID string ) ( [ ] * SetupKey , error )
2023-05-11 18:09:36 +02:00
SaveUser ( accountID , initiatorUserID string , update * User ) ( * UserInfo , error )
2023-11-13 14:04:18 +01:00
SaveOrAddUser ( accountID , initiatorUserID string , update * User , addIfNotExists bool ) ( * UserInfo , error )
2022-11-05 10:24:50 +01:00
GetSetupKey ( accountID , userID , keyID string ) ( * SetupKey , error )
2022-11-07 17:52:23 +01:00
GetAccountByUserOrAccountID ( userID , accountID , domain string ) ( * Account , error )
2022-11-11 20:36:45 +01:00
GetAccountFromToken ( claims jwtclaims . AuthorizationClaims ) ( * Account , * User , error )
2023-12-13 11:18:35 +01:00
CheckUserAccessByJWTGroups ( claims jwtclaims . AuthorizationClaims ) error
2023-03-29 15:21:53 +02:00
GetAccountFromPAT ( pat string ) ( * Account , * User , * PersonalAccessToken , error )
2023-11-28 14:23:38 +01:00
DeleteAccount ( accountID , userID string ) error
2024-02-22 12:27:08 +01:00
GetUsage ( ctx context . Context , accountID string , start time . Time , end time . Time ) ( * AccountUsageStats , error )
2023-03-30 10:54:09 +02:00
MarkPATUsed ( tokenID string ) error
2023-05-11 18:09:36 +02:00
GetUser ( claims jwtclaims . AuthorizationClaims ) ( * User , error )
2023-11-13 14:04:18 +01:00
ListUsers ( accountID string ) ( [ ] * User , error )
2023-11-28 13:45:26 +01:00
GetPeers ( accountID , userID string ) ( [ ] * nbpeer . Peer , error )
2024-02-20 09:59:56 +01:00
MarkPeerConnected ( peerKey string , connected bool , realIP net . IP ) error
2023-10-01 19:51:39 +02:00
DeletePeer ( accountID , peerID , userID string ) error
2023-11-28 13:45:26 +01:00
UpdatePeer ( accountID , userID string , peer * nbpeer . Peer ) ( * nbpeer . Peer , error )
2023-02-03 10:33:28 +01:00
GetNetworkMap ( peerID string ) ( * NetworkMap , error )
GetPeerNetwork ( peerID string ) ( * Network , error )
2023-11-28 13:45:26 +01:00
AddPeer ( setupKey , userID string , peer * nbpeer . Peer ) ( * nbpeer . Peer , * NetworkMap , error )
2023-05-11 18:09:36 +02:00
CreatePAT ( accountID string , initiatorUserID string , targetUserID string , tokenName string , expiresIn int ) ( * PersonalAccessTokenGenerated , error )
DeletePAT ( accountID string , initiatorUserID string , targetUserID string , tokenID string ) error
GetPAT ( accountID string , initiatorUserID string , targetUserID string , tokenID string ) ( * PersonalAccessToken , error )
GetAllPATs ( accountID string , initiatorUserID string , targetUserID string ) ( [ ] * PersonalAccessToken , error )
2023-02-03 10:33:28 +01:00
UpdatePeerSSHKey ( peerID string , sshKey string ) error
2022-11-05 10:24:50 +01:00
GetUsersFromAccount ( accountID , userID string ) ( [ ] * UserInfo , error )
2022-05-03 16:02:51 +02:00
GetGroup ( accountId , groupID string ) ( * Group , error )
2024-01-19 15:41:27 +01:00
GetGroupByName ( groupName , accountID string ) ( * Group , error )
2023-01-02 15:11:32 +01:00
SaveGroup ( accountID , userID string , group * Group ) error
2023-07-14 20:45:40 +02:00
DeleteGroup ( accountId , userId , groupID string ) error
2022-05-03 16:02:51 +02:00
ListGroups ( accountId string ) ( [ ] * Group , error )
2023-02-03 10:33:28 +01:00
GroupAddPeer ( accountId , groupID , peerID string ) error
2023-09-28 14:32:36 +02:00
GroupDeletePeer ( accountId , groupID , peerID string ) error
2023-03-13 15:14:18 +01:00
GetPolicy ( accountID , policyID , userID string ) ( * Policy , error )
SavePolicy ( accountID , userID string , policy * Policy ) error
DeletePolicy ( accountID , policyID , userID string ) error
ListPolicies ( accountID , userID string ) ( [ ] * Policy , error )
2022-11-05 10:24:50 +01:00
GetRoute ( accountID , routeID , userID string ) ( * route . Route , error )
2023-09-28 14:32:36 +02:00
CreateRoute ( accountID , prefix , peerID string , peerGroupIDs [ ] string , description , netID string , masquerade bool , metric int , groups [ ] string , enabled bool , userID string ) ( * route . Route , error )
2023-01-25 16:29:59 +01:00
SaveRoute ( accountID , userID string , route * route . Route ) error
DeleteRoute ( accountID , routeID , userID string ) error
2022-11-05 10:24:50 +01:00
ListRoutes ( accountID , userID string ) ( [ ] * route . Route , error )
2024-01-25 09:50:27 +01:00
GetNameServerGroup ( accountID , userID , nsGroupID string ) ( * nbdns . NameServerGroup , error )
2023-10-19 19:32:42 +02:00
CreateNameServerGroup ( accountID string , name , description string , nameServerList [ ] nbdns . NameServer , groups [ ] string , primary bool , domains [ ] string , enabled bool , userID string , searchDomainsEnabled bool ) ( * nbdns . NameServerGroup , error )
2023-01-25 16:29:59 +01:00
SaveNameServerGroup ( accountID , userID string , nsGroupToSave * nbdns . NameServerGroup ) error
DeleteNameServerGroup ( accountID , nsGroupID , userID string ) error
2024-01-25 09:50:27 +01:00
ListNameServerGroups ( accountID string , userID string ) ( [ ] * nbdns . NameServerGroup , error )
2022-11-21 11:14:42 +01:00
GetDNSDomain ( ) string
2024-02-22 12:27:08 +01:00
StoreEvent ( initiatorID , targetID , accountID string , activityID activity . ActivityDescriber , meta map [ string ] any )
2023-01-02 15:11:32 +01:00
GetEvents ( accountID , userID string ) ( [ ] * activity . Event , error )
2023-01-17 17:34:40 +01:00
GetDNSSettings ( accountID string , userID string ) ( * DNSSettings , error )
SaveDNSSettings ( accountID string , userID string , dnsSettingsToSave * DNSSettings ) error
2023-11-28 13:45:26 +01:00
GetPeer ( accountID , peerID , userID string ) ( * nbpeer . Peer , error )
2023-02-16 12:00:41 +01:00
UpdateAccountSettings ( accountID , userID string , newSettings * Settings ) ( * Account , error )
2023-11-28 13:45:26 +01:00
LoginPeer ( login PeerLogin ) ( * nbpeer . Peer , * NetworkMap , error ) // used by peer gRPC API
SyncPeer ( sync PeerSync ) ( * nbpeer . Peer , * NetworkMap , error ) // used by peer gRPC API
2023-10-11 18:11:45 +02:00
GetAllConnectedPeers ( ) ( map [ string ] struct { } , error )
2023-12-05 14:17:56 +01:00
HasConnectedChannel ( peerID string ) bool
2023-11-13 14:04:18 +01:00
GetExternalCacheManager ( ) ExternalCacheManager
2024-02-20 09:59:56 +01:00
GetPostureChecks ( accountID , postureChecksID , userID string ) ( * posture . Checks , error )
SavePostureChecks ( accountID , userID string , postureChecks * posture . Checks ) error
DeletePostureChecks ( accountID , postureChecksID , userID string ) error
ListPostureChecks ( accountID , userID string ) ( [ ] * posture . Checks , error )
2024-02-28 16:57:35 +01:00
GetIdpManager ( ) idp . Manager
2022-02-22 11:28:19 +01:00
}
type DefaultAccountManager struct {
2021-07-30 17:46:38 +02:00
Store Store
2022-10-13 18:26:31 +02:00
// cacheMux and cacheLoading helps to make sure that only a single cache reload runs at a time per accountID
cacheMux sync . Mutex
// cacheLoading keeps the accountIDs that are currently reloading. The accountID has to be removed once cache has been reloaded
2023-11-13 14:04:18 +01:00
cacheLoading map [ string ] chan struct { }
peersUpdateManager * PeersUpdateManager
idpManager idp . Manager
cacheManager cache . CacheInterface [ [ ] * idp . UserData ]
externalCacheManager ExternalCacheManager
ctx context . Context
eventStore activity . Store
2024-02-20 09:59:56 +01:00
geo * geolocation . Geolocation
2022-10-19 17:43:28 +02:00
// singleAccountMode indicates whether the instance has a single account.
// If true, then every new user will end up under the same account.
// This value will be set to false if management service has more than one account.
singleAccountMode bool
// singleAccountModeDomain is a domain to use in singleAccountMode setup
singleAccountModeDomain string
2022-11-07 15:38:21 +01:00
// dnsDomain is used for peer resolution. This is appended to the peer's name
2023-02-27 16:44:26 +01:00
dnsDomain string
peerLoginExpiry Scheduler
2023-09-19 18:08:40 +02:00
// userDeleteFromIDPEnabled allows to delete user from IDP when user is deleted from account
userDeleteFromIDPEnabled bool
2021-07-30 17:46:38 +02:00
}
2023-02-13 15:07:15 +01:00
// Settings represents Account settings structure that can be modified via API and Dashboard
type Settings struct {
// PeerLoginExpirationEnabled globally enables or disables peer login expiration
PeerLoginExpirationEnabled bool
2023-08-07 17:44:51 +02:00
2023-02-13 15:07:15 +01:00
// PeerLoginExpiration is a setting that indicates when peer login expires.
// Applies to all peers that have Peer.LoginExpirationEnabled set to true.
PeerLoginExpiration time . Duration
2023-06-27 16:51:05 +02:00
2023-08-07 17:44:51 +02:00
// GroupsPropagationEnabled allows to propagate auto groups from the user to the peer
GroupsPropagationEnabled bool
2023-06-27 16:51:05 +02:00
// JWTGroupsEnabled allows extract groups from JWT claim, which name defined in the JWTGroupsClaimName
// and add it to account groups.
JWTGroupsEnabled bool
// JWTGroupsClaimName from which we extract groups name to add it to account groups
JWTGroupsClaimName string
2023-11-27 17:04:40 +01:00
2023-12-11 16:59:15 +01:00
// JWTAllowGroups list of groups to which users are allowed access
JWTAllowGroups [ ] string ` gorm:"serializer:json" `
2023-11-28 11:44:08 +01:00
// Extra is a dictionary of Account settings
2023-11-30 11:51:35 +01:00
Extra * account . ExtraSettings ` gorm:"embedded;embeddedPrefix:extra_" `
2023-11-27 17:04:40 +01:00
}
2023-02-13 15:07:15 +01:00
// Copy copies the Settings struct
func ( s * Settings ) Copy ( ) * Settings {
2023-11-28 13:45:26 +01:00
settings := & Settings {
2023-02-13 15:07:15 +01:00
PeerLoginExpirationEnabled : s . PeerLoginExpirationEnabled ,
PeerLoginExpiration : s . PeerLoginExpiration ,
2023-06-27 16:51:05 +02:00
JWTGroupsEnabled : s . JWTGroupsEnabled ,
JWTGroupsClaimName : s . JWTGroupsClaimName ,
2023-08-07 17:44:51 +02:00
GroupsPropagationEnabled : s . GroupsPropagationEnabled ,
2023-12-11 16:59:15 +01:00
JWTAllowGroups : s . JWTAllowGroups ,
2023-02-13 15:07:15 +01:00
}
2023-11-28 13:45:26 +01:00
if s . Extra != nil {
settings . Extra = s . Extra . Copy ( )
}
return settings
}
2021-07-30 17:46:38 +02:00
// Account represents a unique account of the system
type Account struct {
2023-10-12 15:42:36 +02:00
// we have to name column to aid as it collides with Network.Id when work with associations
Id string ` gorm:"primaryKey" `
2021-12-27 13:17:15 +01:00
// User.Id it was created by
2022-03-01 15:22:18 +01:00
CreatedBy string
2024-03-02 13:49:40 +01:00
CreatedAt time . Time
2023-10-12 15:42:36 +02:00
Domain string ` gorm:"index" `
2022-03-01 15:22:18 +01:00
DomainCategory string
IsDomainPrimaryAccount bool
2023-10-12 15:42:36 +02:00
SetupKeys map [ string ] * SetupKey ` gorm:"-" `
SetupKeysG [ ] SetupKey ` json:"-" gorm:"foreignKey:AccountID;references:id" `
Network * Network ` gorm:"embedded;embeddedPrefix:network_" `
2023-11-28 13:45:26 +01:00
Peers map [ string ] * nbpeer . Peer ` gorm:"-" `
PeersG [ ] nbpeer . Peer ` json:"-" gorm:"foreignKey:AccountID;references:id" `
2023-10-12 15:42:36 +02:00
Users map [ string ] * User ` gorm:"-" `
UsersG [ ] User ` json:"-" gorm:"foreignKey:AccountID;references:id" `
Groups map [ string ] * Group ` gorm:"-" `
GroupsG [ ] Group ` json:"-" gorm:"foreignKey:AccountID;references:id" `
Policies [ ] * Policy ` gorm:"foreignKey:AccountID;references:id" `
Routes map [ string ] * route . Route ` gorm:"-" `
RoutesG [ ] route . Route ` json:"-" gorm:"foreignKey:AccountID;references:id" `
NameServerGroups map [ string ] * nbdns . NameServerGroup ` gorm:"-" `
NameServerGroupsG [ ] nbdns . NameServerGroup ` json:"-" gorm:"foreignKey:AccountID;references:id" `
DNSSettings DNSSettings ` gorm:"embedded;embeddedPrefix:dns_settings_" `
2024-02-20 09:59:56 +01:00
PostureChecks [ ] * posture . Checks ` gorm:"foreignKey:AccountID;references:id" `
2023-02-13 15:07:15 +01:00
// Settings is a dictionary of Account settings
2023-10-12 15:42:36 +02:00
Settings * Settings ` gorm:"embedded;embeddedPrefix:settings_" `
2024-02-19 17:17:36 +01:00
// deprecated on store and api level
Rules map [ string ] * Rule ` json:"-" gorm:"-" `
RulesG [ ] Rule ` json:"-" gorm:"-" `
2021-12-27 13:17:15 +01:00
}
2024-02-22 12:27:08 +01:00
// AccountUsageStats represents the current usage statistics for an account
type AccountUsageStats struct {
ActiveUsers int64 ` json:"active_users" `
TotalUsers int64 ` json:"total_users" `
ActivePeers int64 ` json:"active_peers" `
TotalPeers int64 ` json:"total_peers" `
}
2022-05-05 08:58:34 +02:00
type UserInfo struct {
2023-11-01 11:04:17 +01:00
ID string ` json:"id" `
Email string ` json:"email" `
Name string ` json:"name" `
Role string ` json:"role" `
AutoGroups [ ] string ` json:"auto_groups" `
Status string ` json:"-" `
IsServiceUser bool ` json:"is_service_user" `
IsBlocked bool ` json:"is_blocked" `
2023-11-15 16:22:00 +01:00
NonDeletable bool ` json:"non_deletable" `
2023-11-01 11:04:17 +01:00
LastLogin time . Time ` json:"last_login" `
Issued string ` json:"issued" `
IntegrationReference IntegrationReference ` json:"-" `
2022-05-05 08:58:34 +02:00
}
2022-12-06 10:11:57 +01:00
// getRoutesToSync returns the enabled routes for the peer ID and the routes
2023-02-03 10:33:28 +01:00
// from the ACL peers that have distribution groups associated with the peer ID.
// Please mind, that the returned route.Route objects will contain Peer.Key instead of Peer.ID.
2023-11-28 13:45:26 +01:00
func ( a * Account ) getRoutesToSync ( peerID string , aclPeers [ ] * nbpeer . Peer ) [ ] * route . Route {
2023-10-09 14:39:41 +02:00
routes , peerDisabledRoutes := a . getRoutingPeerRoutes ( peerID )
2022-12-08 15:15:50 +01:00
peerRoutesMembership := make ( lookupMap )
for _ , r := range append ( routes , peerDisabledRoutes ... ) {
peerRoutesMembership [ route . GetHAUniqueID ( r ) ] = struct { } { }
}
2022-12-06 10:11:57 +01:00
groupListMap := a . getPeerGroups ( peerID )
for _ , peer := range aclPeers {
2023-10-09 14:39:41 +02:00
activeRoutes , _ := a . getRoutingPeerRoutes ( peer . ID )
2022-12-08 15:15:50 +01:00
groupFilteredRoutes := a . filterRoutesByGroups ( activeRoutes , groupListMap )
filteredRoutes := a . filterRoutesFromPeersOfSameHAGroup ( groupFilteredRoutes , peerRoutesMembership )
2022-12-06 10:11:57 +01:00
routes = append ( routes , filteredRoutes ... )
}
return routes
}
2022-12-08 15:15:50 +01:00
// filterRoutesByHAMembership filters and returns a list of routes that don't share the same HA route membership
func ( a * Account ) filterRoutesFromPeersOfSameHAGroup ( routes [ ] * route . Route , peerMemberships lookupMap ) [ ] * route . Route {
var filteredRoutes [ ] * route . Route
for _ , r := range routes {
_ , found := peerMemberships [ route . GetHAUniqueID ( r ) ]
if ! found {
filteredRoutes = append ( filteredRoutes , r )
}
}
return filteredRoutes
}
2022-12-06 10:11:57 +01:00
// filterRoutesByGroups returns a list with routes that have distribution groups in the group's map
func ( a * Account ) filterRoutesByGroups ( routes [ ] * route . Route , groupListMap lookupMap ) [ ] * route . Route {
var filteredRoutes [ ] * route . Route
for _ , r := range routes {
for _ , groupID := range r . Groups {
_ , found := groupListMap [ groupID ]
if found {
filteredRoutes = append ( filteredRoutes , r )
break
2022-11-07 12:10:56 +01:00
}
}
}
2022-12-06 10:11:57 +01:00
return filteredRoutes
2022-11-07 12:10:56 +01:00
}
2023-10-09 14:39:41 +02:00
// getRoutingPeerRoutes returns the enabled and disabled lists of routes that the given routing peer serves
2023-02-03 10:33:28 +01:00
// Please mind, that the returned route.Route objects will contain Peer.Key instead of Peer.ID.
2023-10-09 14:39:41 +02:00
// If the given is not a routing peer, then the lists are empty.
func ( a * Account ) getRoutingPeerRoutes ( peerID string ) ( enabledRoutes [ ] * route . Route , disabledRoutes [ ] * route . Route ) {
peer := a . GetPeer ( peerID )
if peer == nil {
log . Errorf ( "peer %s that doesn't exist under account %s" , peerID , a . Id )
return enabledRoutes , disabledRoutes
}
// currently we support only linux routing peers
if peer . Meta . GoOS != "linux" {
return enabledRoutes , disabledRoutes
}
seenRoute := make ( map [ string ] struct { } )
2023-09-28 14:32:36 +02:00
takeRoute := func ( r * route . Route , id string ) {
2023-10-09 14:39:41 +02:00
if _ , ok := seenRoute [ r . ID ] ; ok {
2023-09-28 14:32:36 +02:00
return
}
2023-10-09 14:39:41 +02:00
seenRoute [ r . ID ] = struct { } { }
2023-09-28 14:32:36 +02:00
if r . Enabled {
2023-10-09 14:39:41 +02:00
r . Peer = peer . Key
2023-09-28 14:32:36 +02:00
enabledRoutes = append ( enabledRoutes , r )
return
}
disabledRoutes = append ( disabledRoutes , r )
}
2022-11-07 12:10:56 +01:00
for _ , r := range a . Routes {
2023-10-09 14:39:41 +02:00
for _ , groupID := range r . PeerGroups {
group := a . GetGroup ( groupID )
if group == nil {
log . Errorf ( "route %s has peers group %s that doesn't exist under account %s" , r . ID , groupID , a . Id )
continue
}
for _ , id := range group . Peers {
if id != peerID {
2023-09-28 14:32:36 +02:00
continue
}
2023-10-09 14:39:41 +02:00
newPeerRoute := r . Copy ( )
newPeerRoute . Peer = id
newPeerRoute . PeerGroups = nil
newPeerRoute . ID = r . ID + ":" + id // we have to provide unique route id when distribute network map
takeRoute ( newPeerRoute , id )
break
2022-12-08 15:15:50 +01:00
}
2023-09-28 14:32:36 +02:00
}
if r . Peer == peerID {
2023-10-09 14:39:41 +02:00
takeRoute ( r . Copy ( ) , peerID )
2022-11-07 12:10:56 +01:00
}
}
2023-10-09 14:39:41 +02:00
2022-12-08 15:15:50 +01:00
return enabledRoutes , disabledRoutes
2022-11-07 12:10:56 +01:00
}
// GetRoutesByPrefix return list of routes by account and route prefix
func ( a * Account ) GetRoutesByPrefix ( prefix netip . Prefix ) [ ] * route . Route {
var routes [ ] * route . Route
for _ , r := range a . Routes {
if r . Network . String ( ) == prefix . String ( ) {
routes = append ( routes , r )
}
}
return routes
}
2023-01-02 15:11:32 +01:00
// GetGroup returns a group by ID if exists, nil otherwise
func ( a * Account ) GetGroup ( groupID string ) * Group {
return a . Groups [ groupID ]
}
2023-03-03 18:35:38 +01:00
// GetPeerNetworkMap returns a group by ID if exists, nil otherwise
func ( a * Account ) GetPeerNetworkMap ( peerID , dnsDomain string ) * NetworkMap {
2023-11-28 11:44:08 +01:00
peer := a . Peers [ peerID ]
if peer == nil {
return & NetworkMap {
Network : a . Network . Copy ( ) ,
}
}
2023-11-28 15:09:04 +01:00
validatedPeers := additions . ValidatePeers ( [ ] * nbpeer . Peer { peer } )
2023-11-28 11:44:08 +01:00
if len ( validatedPeers ) == 0 {
return & NetworkMap {
Network : a . Network . Copy ( ) ,
}
}
2023-05-29 16:00:18 +02:00
aclPeers , firewallRules := a . getPeerConnectionResources ( peerID )
2023-03-03 18:35:38 +01:00
// exclude expired peers
2023-11-28 13:45:26 +01:00
var peersToConnect [ ] * nbpeer . Peer
var expiredPeers [ ] * nbpeer . Peer
2023-03-03 18:35:38 +01:00
for _ , p := range aclPeers {
expired , _ := p . LoginExpired ( a . Settings . PeerLoginExpiration )
2023-03-09 11:24:42 +01:00
if a . Settings . PeerLoginExpirationEnabled && expired {
2023-03-07 10:17:25 +01:00
expiredPeers = append ( expiredPeers , p )
2023-03-03 18:35:38 +01:00
continue
}
peersToConnect = append ( peersToConnect , p )
}
2023-09-28 14:32:36 +02:00
2023-10-09 14:39:41 +02:00
routesUpdate := a . getRoutesToSync ( peerID , peersToConnect )
2023-03-03 18:35:38 +01:00
dnsManagementStatus := a . getPeerDNSManagementStatus ( peerID )
dnsUpdate := nbdns . Config {
ServiceEnable : dnsManagementStatus ,
}
if dnsManagementStatus {
var zones [ ] nbdns . CustomZone
peersCustomZone := getPeersCustomZone ( a , dnsDomain )
if peersCustomZone . Domain != "" {
zones = append ( zones , peersCustomZone )
}
dnsUpdate . CustomZones = zones
dnsUpdate . NameServerGroups = getPeerNSGroups ( a , peerID )
}
return & NetworkMap {
2023-05-29 16:00:18 +02:00
Peers : peersToConnect ,
Network : a . Network . Copy ( ) ,
Routes : routesUpdate ,
DNSConfig : dnsUpdate ,
OfflinePeers : expiredPeers ,
FirewallRules : firewallRules ,
2023-03-03 18:35:38 +01:00
}
}
2023-02-27 16:44:26 +01:00
// GetExpiredPeers returns peers that have been expired
2023-11-28 13:45:26 +01:00
func ( a * Account ) GetExpiredPeers ( ) [ ] * nbpeer . Peer {
var peers [ ] * nbpeer . Peer
2023-02-27 16:44:26 +01:00
for _ , peer := range a . GetPeersWithExpiration ( ) {
expired , _ := peer . LoginExpired ( a . Settings . PeerLoginExpiration )
if expired {
peers = append ( peers , peer )
}
}
return peers
}
// 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 ( a * Account ) GetNextPeerExpiration ( ) ( time . Duration , bool ) {
peersWithExpiry := a . GetPeersWithExpiration ( )
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 ( a . Settings . PeerLoginExpiration )
if nextExpiry == nil || duration < * nextExpiry {
nextExpiry = & duration
}
}
if nextExpiry == nil {
return 0 , false
}
return * nextExpiry , true
}
2023-03-23 17:47:53 +01:00
// GetPeersWithExpiration returns a list of peers that have Peer.LoginExpirationEnabled set to true and that were added by a user
2023-11-28 13:45:26 +01:00
func ( a * Account ) GetPeersWithExpiration ( ) [ ] * nbpeer . Peer {
peers := make ( [ ] * nbpeer . Peer , 0 )
2023-02-27 16:44:26 +01:00
for _ , peer := range a . Peers {
2023-03-23 17:47:53 +01:00
if peer . LoginExpirationEnabled && peer . AddedWithSSOLogin ( ) {
2023-02-27 16:44:26 +01:00
peers = append ( peers , peer )
}
}
return peers
}
2022-11-07 12:10:56 +01:00
// GetPeers returns a list of all Account peers
2023-11-28 13:45:26 +01:00
func ( a * Account ) GetPeers ( ) [ ] * nbpeer . Peer {
var peers [ ] * nbpeer . Peer
2022-11-07 12:10:56 +01:00
for _ , peer := range a . Peers {
peers = append ( peers , peer )
}
return peers
}
2023-02-16 12:00:41 +01:00
// UpdateSettings saves new account settings
func ( a * Account ) UpdateSettings ( update * Settings ) * Account {
a . Settings = update . Copy ( )
return a
}
2022-11-07 12:10:56 +01:00
// UpdatePeer saves new or replaces existing peer
2023-11-28 13:45:26 +01:00
func ( a * Account ) UpdatePeer ( update * nbpeer . Peer ) {
2023-02-03 10:33:28 +01:00
a . Peers [ update . ID ] = update
2022-11-07 12:10:56 +01:00
}
// DeletePeer deletes peer from the account cleaning up all the references
2023-02-03 10:33:28 +01:00
func ( a * Account ) DeletePeer ( peerID string ) {
2022-11-07 12:10:56 +01:00
// delete peer from groups
for _ , g := range a . Groups {
for i , pk := range g . Peers {
2023-02-03 10:33:28 +01:00
if pk == peerID {
2022-11-07 12:10:56 +01:00
g . Peers = append ( g . Peers [ : i ] , g . Peers [ i + 1 : ] ... )
break
}
}
}
2022-11-25 18:11:07 +01:00
for _ , r := range a . Routes {
2023-02-03 10:33:28 +01:00
if r . Peer == peerID {
2022-11-25 18:11:07 +01:00
r . Enabled = false
r . Peer = ""
}
}
2023-02-03 10:33:28 +01:00
delete ( a . Peers , peerID )
2022-11-07 12:10:56 +01:00
a . Network . IncSerial ( )
}
// FindPeerByPubKey looks for a Peer by provided WireGuard public key in the Account or returns error if it wasn't found.
// It will return an object copy of the peer.
2023-11-28 13:45:26 +01:00
func ( a * Account ) FindPeerByPubKey ( peerPubKey string ) ( * nbpeer . Peer , error ) {
2022-11-07 12:10:56 +01:00
for _ , peer := range a . Peers {
if peer . Key == peerPubKey {
return peer . Copy ( ) , nil
}
}
2022-11-11 20:36:45 +01:00
return nil , status . Errorf ( status . NotFound , "peer with the public key %s not found" , peerPubKey )
2022-11-07 12:10:56 +01:00
}
2023-02-07 20:11:08 +01:00
// FindUserPeers returns a list of peers that user owns (created)
2023-11-28 13:45:26 +01:00
func ( a * Account ) FindUserPeers ( userID string ) ( [ ] * nbpeer . Peer , error ) {
peers := make ( [ ] * nbpeer . Peer , 0 )
2023-02-07 20:11:08 +01:00
for _ , peer := range a . Peers {
if peer . UserID == userID {
peers = append ( peers , peer )
}
}
return peers , nil
}
2022-11-05 10:24:50 +01:00
// FindUser looks for a given user in the Account or returns error if user wasn't found.
func ( a * Account ) FindUser ( userID string ) ( * User , error ) {
user := a . Users [ userID ]
if user == nil {
2022-11-11 20:36:45 +01:00
return nil , status . Errorf ( status . NotFound , "user %s not found" , userID )
2022-11-05 10:24:50 +01:00
}
return user , nil
}
2022-11-08 16:14:36 +01:00
// FindSetupKey looks for a given SetupKey in the Account or returns error if it wasn't found.
func ( a * Account ) FindSetupKey ( setupKey string ) ( * SetupKey , error ) {
key := a . SetupKeys [ setupKey ]
if key == nil {
2022-11-11 20:36:45 +01:00
return nil , status . Errorf ( status . NotFound , "setup key not found" )
2022-11-08 16:14:36 +01:00
}
return key , nil
}
func ( a * Account ) getUserGroups ( userID string ) ( [ ] string , error ) {
user , err := a . FindUser ( userID )
if err != nil {
return nil , err
}
return user . AutoGroups , nil
}
2023-01-17 17:34:40 +01:00
func ( a * Account ) getPeerDNSManagementStatus ( peerID string ) bool {
peerGroups := a . getPeerGroups ( peerID )
enabled := true
2023-10-11 23:00:56 +02:00
for _ , groupID := range a . DNSSettings . DisabledManagementGroups {
_ , found := peerGroups [ groupID ]
if found {
enabled = false
break
2023-01-17 17:34:40 +01:00
}
}
return enabled
}
2022-12-06 10:11:57 +01:00
func ( a * Account ) getPeerGroups ( peerID string ) lookupMap {
groupList := make ( lookupMap )
for groupID , group := range a . Groups {
for _ , id := range group . Peers {
if id == peerID {
groupList [ groupID ] = struct { } { }
break
}
}
}
return groupList
}
2022-11-08 16:14:36 +01:00
func ( a * Account ) getSetupKeyGroups ( setupKey string ) ( [ ] string , error ) {
key , err := a . FindSetupKey ( setupKey )
if err != nil {
return nil , err
}
return key . AutoGroups , nil
}
func ( a * Account ) getTakenIPs ( ) [ ] net . IP {
var takenIps [ ] net . IP
for _ , existingPeer := range a . Peers {
takenIps = append ( takenIps , existingPeer . IP )
}
return takenIps
}
2022-11-07 15:38:21 +01:00
func ( a * Account ) getPeerDNSLabels ( ) lookupMap {
existingLabels := make ( lookupMap )
for _ , peer := range a . Peers {
if peer . DNSLabel != "" {
existingLabels [ peer . DNSLabel ] = struct { } { }
}
}
return existingLabels
}
2021-12-27 13:17:15 +01:00
func ( a * Account ) Copy ( ) * Account {
2023-11-28 13:45:26 +01:00
peers := map [ string ] * nbpeer . Peer { }
2021-12-27 13:17:15 +01:00
for id , peer := range a . Peers {
peers [ id ] = peer . Copy ( )
}
users := map [ string ] * User { }
for id , user := range a . Users {
users [ id ] = user . Copy ( )
}
setupKeys := map [ string ] * SetupKey { }
for id , key := range a . SetupKeys {
setupKeys [ id ] = key . Copy ( )
}
2022-05-21 15:21:39 +02:00
groups := map [ string ] * Group { }
for id , group := range a . Groups {
groups [ id ] = group . Copy ( )
}
2023-03-13 15:14:18 +01:00
policies := [ ] * Policy { }
for _ , policy := range a . Policies {
policies = append ( policies , policy . Copy ( ) )
}
2022-09-30 13:47:11 +02:00
routes := map [ string ] * route . Route { }
2023-10-02 19:18:08 +02:00
for id , r := range a . Routes {
routes [ id ] = r . Copy ( )
2022-09-30 13:47:11 +02:00
}
nsGroups := map [ string ] * nbdns . NameServerGroup { }
for id , nsGroup := range a . NameServerGroups {
nsGroups [ id ] = nsGroup . Copy ( )
}
2023-10-11 23:00:56 +02:00
dnsSettings := a . DNSSettings . Copy ( )
2023-02-13 15:07:15 +01:00
var settings * Settings
if a . Settings != nil {
settings = a . Settings . Copy ( )
2023-01-17 17:34:40 +01:00
}
2024-02-20 09:59:56 +01:00
postureChecks := [ ] * posture . Checks { }
for _ , postureCheck := range a . PostureChecks {
postureChecks = append ( postureChecks , postureCheck . Copy ( ) )
}
2021-12-27 13:17:15 +01:00
return & Account {
2022-11-07 12:10:56 +01:00
Id : a . Id ,
CreatedBy : a . CreatedBy ,
2024-03-02 13:49:40 +01:00
CreatedAt : a . CreatedAt ,
2022-11-07 12:10:56 +01:00
Domain : a . Domain ,
DomainCategory : a . DomainCategory ,
IsDomainPrimaryAccount : a . IsDomainPrimaryAccount ,
SetupKeys : setupKeys ,
Network : a . Network . Copy ( ) ,
Peers : peers ,
Users : users ,
Groups : groups ,
2023-03-13 15:14:18 +01:00
Policies : policies ,
2022-11-07 12:10:56 +01:00
Routes : routes ,
NameServerGroups : nsGroups ,
2023-01-17 17:34:40 +01:00
DNSSettings : dnsSettings ,
2024-02-20 09:59:56 +01:00
PostureChecks : postureChecks ,
2023-02-13 15:07:15 +01:00
Settings : settings ,
2021-12-27 13:17:15 +01:00
}
2021-07-30 17:46:38 +02:00
}
2022-05-21 15:21:39 +02:00
func ( a * Account ) GetGroupAll ( ) ( * Group , error ) {
for _ , g := range a . Groups {
if g . Name == "All" {
return g , nil
}
}
return nil , fmt . Errorf ( "no group ALL found" )
}
2023-02-03 10:33:28 +01:00
// GetPeer looks up a Peer by ID
2023-11-28 13:45:26 +01:00
func ( a * Account ) GetPeer ( peerID string ) * nbpeer . Peer {
2023-02-03 10:33:28 +01:00
return a . Peers [ peerID ]
}
2023-08-18 15:36:05 +02:00
// SetJWTGroups to account and to user autoassigned groups
func ( a * Account ) SetJWTGroups ( userID string , groupsNames [ ] string ) bool {
2023-08-07 17:44:51 +02:00
user , ok := a . Users [ userID ]
if ! ok {
return false
}
existedGroupsByName := make ( map [ string ] * Group )
for _ , group := range a . Groups {
existedGroupsByName [ group . Name ] = group
}
2023-08-18 15:36:05 +02:00
// remove JWT groups from the autogroups, to sync them again
removed := 0
jwtAutoGroups := make ( map [ string ] struct { } )
for i , id := range user . AutoGroups {
if group , ok := a . Groups [ id ] ; ok && group . Issued == GroupIssuedJWT {
jwtAutoGroups [ group . Name ] = struct { } { }
user . AutoGroups = append ( user . AutoGroups [ : i - removed ] , user . AutoGroups [ i - removed + 1 : ] ... )
removed ++
}
2023-06-27 16:51:05 +02:00
}
2023-08-18 15:36:05 +02:00
// create JWT groups if they doesn't exist
// and all of them to the autogroups
2023-08-07 17:44:51 +02:00
var modified bool
2023-08-18 15:36:05 +02:00
for _ , name := range groupsNames {
2023-08-07 17:44:51 +02:00
group , ok := existedGroupsByName [ name ]
if ! ok {
group = & Group {
ID : xid . New ( ) . String ( ) ,
2023-06-27 16:51:05 +02:00
Name : name ,
Issued : GroupIssuedJWT ,
}
2023-08-07 17:44:51 +02:00
a . Groups [ group . ID ] = group
}
2023-08-18 15:36:05 +02:00
// only JWT groups will be synced
if group . Issued == GroupIssuedJWT {
user . AutoGroups = append ( user . AutoGroups , group . ID )
if _ , ok := jwtAutoGroups [ name ] ; ! ok {
2023-08-07 17:44:51 +02:00
modified = true
}
2023-08-18 15:36:05 +02:00
delete ( jwtAutoGroups , name )
2023-06-27 16:51:05 +02:00
}
}
2023-08-07 17:44:51 +02:00
2023-08-18 15:36:05 +02:00
// if not empty it means we removed some groups
if len ( jwtAutoGroups ) > 0 {
modified = true
}
2023-08-07 17:44:51 +02:00
return modified
}
// UserGroupsAddToPeers adds groups to all peers of user
func ( a * Account ) UserGroupsAddToPeers ( userID string , groups ... string ) {
userPeers := make ( map [ string ] struct { } )
for pid , peer := range a . Peers {
if peer . UserID == userID {
userPeers [ pid ] = struct { } { }
}
}
for _ , gid := range groups {
group , ok := a . Groups [ gid ]
if ! ok {
continue
}
groupPeers := make ( map [ string ] struct { } )
for _ , pid := range group . Peers {
groupPeers [ pid ] = struct { } { }
}
for pid := range userPeers {
groupPeers [ pid ] = struct { } { }
}
group . Peers = group . Peers [ : 0 ]
for pid := range groupPeers {
group . Peers = append ( group . Peers , pid )
}
}
}
// UserGroupsRemoveFromPeers removes groups from all peers of user
func ( a * Account ) UserGroupsRemoveFromPeers ( userID string , groups ... string ) {
for _ , gid := range groups {
group , ok := a . Groups [ gid ]
if ! ok {
continue
}
update := make ( [ ] string , 0 , len ( group . Peers ) )
for _ , pid := range group . Peers {
peer , ok := a . Peers [ pid ]
if ! ok {
continue
}
if peer . UserID != userID {
update = append ( update , pid )
}
}
group . Peers = update
}
2023-06-27 16:51:05 +02:00
}
2022-05-21 15:21:39 +02:00
// BuildManager creates a new DefaultAccountManager with a provided Store
2022-10-19 17:43:28 +02:00
func BuildManager ( store Store , peersUpdateManager * PeersUpdateManager , idpManager idp . Manager ,
2024-02-20 09:59:56 +01:00
singleAccountModeDomain string , dnsDomain string , eventStore activity . Store , geo * geolocation . Geolocation ,
userDeleteFromIDPEnabled bool ,
2023-02-03 21:47:20 +01:00
) ( * DefaultAccountManager , error ) {
2022-06-05 21:36:42 +02:00
am := & DefaultAccountManager {
2023-09-19 18:08:40 +02:00
Store : store ,
2024-02-20 09:59:56 +01:00
geo : geo ,
2023-09-19 18:08:40 +02:00
peersUpdateManager : peersUpdateManager ,
idpManager : idpManager ,
ctx : context . Background ( ) ,
cacheMux : sync . Mutex { } ,
cacheLoading : map [ string ] chan struct { } { } ,
dnsDomain : dnsDomain ,
eventStore : eventStore ,
peerLoginExpiry : NewDefaultScheduler ( ) ,
userDeleteFromIDPEnabled : userDeleteFromIDPEnabled ,
2021-07-30 17:46:38 +02:00
}
2022-10-19 17:43:28 +02:00
allAccounts := store . GetAllAccounts ( )
// enable single account mode only if configured by user and number of existing accounts is not grater than 1
am . singleAccountMode = singleAccountModeDomain != "" && len ( allAccounts ) <= 1
if am . singleAccountMode {
2022-12-13 13:43:29 +01:00
if ! isDomainValid ( singleAccountModeDomain ) {
2023-02-27 16:44:26 +01:00
return nil , status . Errorf ( status . InvalidArgument , "invalid domain \"%s\" provided for a single account mode. Please review your input for --single-account-mode-domain" , singleAccountModeDomain )
2022-12-13 13:43:29 +01:00
}
2022-10-19 17:43:28 +02:00
am . singleAccountModeDomain = singleAccountModeDomain
log . Infof ( "single account mode enabled, accounts number %d" , len ( allAccounts ) )
} else {
log . Infof ( "single account mode disabled, accounts number %d" , len ( allAccounts ) )
}
2022-05-21 15:21:39 +02:00
2022-10-19 17:43:28 +02:00
// if account doesn't have a default group
2022-06-06 13:45:59 +02:00
// we create 'all' group and add all peers into it
// also we create default rule with source as destination
2022-10-19 17:43:28 +02:00
for _ , account := range allAccounts {
2022-11-07 15:38:21 +01:00
shouldSave := false
2022-06-06 13:45:59 +02:00
_ , err := account . GetGroupAll ( )
if err != nil {
2023-03-13 15:14:18 +01:00
if err := addAllGroup ( account ) ; err != nil {
return nil , err
}
2022-11-07 15:38:21 +01:00
shouldSave = true
}
if shouldSave {
err = store . SaveAccount ( account )
if err != nil {
2022-06-06 13:45:59 +02:00
return nil , err
}
2022-05-21 15:21:39 +02:00
}
}
2022-10-19 17:43:28 +02:00
goCacheClient := gocache . New ( CacheExpirationMax , 30 * time . Minute )
goCacheStore := cacheStore . NewGoCache ( goCacheClient )
am . cacheManager = cache . NewLoadable [ [ ] * idp . UserData ] ( am . loadAccount , cache . New [ [ ] * idp . UserData ] ( goCacheStore ) )
2022-06-06 12:05:44 +02:00
2023-11-13 14:04:18 +01:00
// TODO: what is max expiration time? Should be quite long
am . externalCacheManager = cache . New [ * idp . UserData ] (
cacheStore . NewGoCache ( goCacheClient ) ,
)
2022-06-06 12:05:44 +02:00
if ! isNil ( am . idpManager ) {
go func ( ) {
err := am . warmupIDPCache ( )
if err != nil {
log . Warnf ( "failed warming up cache due to error: %v" , err )
2023-02-03 21:47:20 +01:00
// todo retry?
2022-06-06 12:05:44 +02:00
return
}
} ( )
}
2022-06-05 21:36:42 +02:00
return am , nil
2021-07-30 17:46:38 +02:00
}
2023-11-13 14:04:18 +01:00
func ( am * DefaultAccountManager ) GetExternalCacheManager ( ) ExternalCacheManager {
return am . externalCacheManager
}
2024-02-28 16:57:35 +01:00
func ( am * DefaultAccountManager ) GetIdpManager ( ) idp . Manager {
return am . idpManager
}
2023-02-16 12:00:41 +01:00
// UpdateAccountSettings updates Account settings.
// Only users with role UserRoleAdmin can update the account.
// User that performs the update has to belong to the account.
// Returns an updated Account
func ( am * DefaultAccountManager ) UpdateAccountSettings ( accountID , userID string , newSettings * Settings ) ( * Account , error ) {
halfYearLimit := 180 * 24 * time . Hour
if newSettings . PeerLoginExpiration > halfYearLimit {
return nil , status . Errorf ( status . InvalidArgument , "peer login expiration can't be larger than 180 days" )
}
if newSettings . PeerLoginExpiration < time . Hour {
return nil , status . Errorf ( status . InvalidArgument , "peer login expiration can't be smaller than one hour" )
}
unlock := am . Store . AcquireAccountLock ( accountID )
defer unlock ( )
2024-02-27 14:17:22 +01:00
account , err := am . Store . GetAccount ( accountID )
2023-11-28 11:44:08 +01:00
if err != nil {
return nil , err
}
2023-02-16 12:00:41 +01:00
user , err := account . FindUser ( userID )
if err != nil {
return nil , err
}
2023-12-01 17:24:57 +01:00
if ! user . HasAdminPower ( ) {
2023-02-16 12:00:41 +01:00
return nil , status . Errorf ( status . PermissionDenied , "user is not allowed to update account" )
}
2024-02-27 14:17:22 +01:00
err = additions . ValidateExtraSettings ( newSettings . Extra , account . Settings . Extra , account . Peers , userID , accountID , am . eventStore )
if err != nil {
return nil , err
}
2023-02-16 12:00:41 +01:00
oldSettings := account . Settings
if oldSettings . PeerLoginExpirationEnabled != newSettings . PeerLoginExpirationEnabled {
event := activity . AccountPeerLoginExpirationEnabled
if ! newSettings . PeerLoginExpirationEnabled {
event = activity . AccountPeerLoginExpirationDisabled
2023-02-27 16:44:26 +01:00
am . peerLoginExpiry . Cancel ( [ ] string { accountID } )
} else {
am . checkAndSchedulePeerLoginExpiration ( account )
2023-02-16 12:00:41 +01:00
}
2023-11-08 11:35:37 +01:00
am . StoreEvent ( userID , accountID , accountID , event , nil )
2023-02-16 12:00:41 +01:00
}
if oldSettings . PeerLoginExpiration != newSettings . PeerLoginExpiration {
2023-11-08 11:35:37 +01:00
am . StoreEvent ( userID , accountID , accountID , activity . AccountPeerLoginExpirationDurationUpdated , nil )
2023-02-27 16:44:26 +01:00
am . checkAndSchedulePeerLoginExpiration ( account )
2023-02-16 12:00:41 +01:00
}
updatedAccount := account . UpdateSettings ( newSettings )
err = am . Store . SaveAccount ( account )
if err != nil {
return nil , err
}
return updatedAccount , nil
}
2023-02-27 16:44:26 +01:00
func ( am * DefaultAccountManager ) peerLoginExpirationJob ( accountID string ) func ( ) ( time . Duration , bool ) {
return func ( ) ( time . Duration , bool ) {
unlock := am . Store . AcquireAccountLock ( accountID )
defer unlock ( )
account , err := am . Store . GetAccount ( accountID )
if err != nil {
log . Errorf ( "failed getting account %s expiring peers" , account . Id )
return account . GetNextPeerExpiration ( )
}
2023-09-19 18:08:40 +02:00
expiredPeers := account . GetExpiredPeers ( )
2023-02-27 16:44:26 +01:00
var peerIDs [ ] string
2023-09-19 18:08:40 +02:00
for _ , peer := range expiredPeers {
2023-02-27 16:44:26 +01:00
peerIDs = append ( peerIDs , peer . ID )
}
log . Debugf ( "discovered %d peers to expire for account %s" , len ( peerIDs ) , account . Id )
2023-09-19 18:08:40 +02:00
if err := am . expireAndUpdatePeers ( account , expiredPeers ) ; err != nil {
log . Errorf ( "failed updating account peers while expiring peers for account %s" , account . Id )
return account . GetNextPeerExpiration ( )
2023-02-27 16:44:26 +01:00
}
2023-09-19 18:08:40 +02:00
2023-02-27 16:44:26 +01:00
return account . GetNextPeerExpiration ( )
}
}
func ( am * DefaultAccountManager ) checkAndSchedulePeerLoginExpiration ( account * Account ) {
am . peerLoginExpiry . Cancel ( [ ] string { account . Id } )
if nextRun , ok := account . GetNextPeerExpiration ( ) ; ok {
go am . peerLoginExpiry . Schedule ( nextRun , account . Id , am . peerLoginExpirationJob ( account . Id ) )
}
}
2022-06-20 18:20:43 +02:00
// 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 ( userID , domain string ) ( * Account , error ) {
for i := 0 ; i < 2 ; i ++ {
accountId := xid . New ( ) . String ( )
_ , err := am . Store . GetAccount ( accountId )
statusErr , _ := status . FromError ( err )
2023-11-27 16:40:02 +01:00
switch {
case err == nil :
2022-06-20 18:20:43 +02:00
log . Warnf ( "an account with ID already exists, retrying..." )
continue
2023-11-27 16:40:02 +01:00
case statusErr . Type ( ) == status . NotFound :
2023-01-02 15:11:32 +01:00
newAccount := newAccountWithId ( accountId , userID , domain )
2023-11-08 11:35:37 +01:00
am . StoreEvent ( userID , newAccount . Id , accountId , activity . AccountCreated , nil )
2023-01-02 15:11:32 +01:00
return newAccount , nil
2023-11-27 16:40:02 +01:00
default :
2022-06-20 18:20:43 +02:00
return nil , err
}
}
2022-11-11 20:36:45 +01:00
return nil , status . Errorf ( status . Internal , "error while creating new account" )
2022-06-20 18:20:43 +02:00
}
2022-06-06 12:05:44 +02:00
func ( am * DefaultAccountManager ) warmupIDPCache ( ) error {
userData , err := am . idpManager . GetAllAccounts ( )
if err != nil {
return err
}
2023-10-11 16:09:30 +02:00
log . Infof ( "%d entries received from IdP management" , len ( userData ) )
2022-06-06 12:05:44 +02:00
2023-10-03 16:40:28 +02:00
// If the Identity Provider does not support writing AppMetadata,
// in cases like this, we expect it to return all users in an "unset" field.
// We iterate over the users in the "unset" field, look up their AccountID in our store, and
// update their AppMetadata with the AccountID.
if unsetData , ok := userData [ idp . UnsetAccountID ] ; ok {
for _ , user := range unsetData {
accountID , err := am . Store . GetAccountByUser ( user . ID )
if err == nil {
data := userData [ accountID . Id ]
if data == nil {
data = make ( [ ] * idp . UserData , 0 , 1 )
}
user . AppMetadata . WTAccountID = accountID . Id
userData [ accountID . Id ] = append ( data , user )
}
}
}
delete ( userData , idp . UnsetAccountID )
2022-06-06 12:05:44 +02:00
for accountID , users := range userData {
2022-10-13 18:26:31 +02:00
err = am . cacheManager . Set ( am . ctx , accountID , users , cacheStore . WithExpiration ( cacheEntryExpiration ( ) ) )
2022-06-06 12:05:44 +02:00
if err != nil {
return err
}
}
log . Infof ( "warmed up IDP cache with %d entries" , len ( userData ) )
return nil
}
2023-11-28 14:23:38 +01:00
// DeleteAccount deletes an account and all its users from local store and from the remote IDP if the requester is an admin and account owner
func ( am * DefaultAccountManager ) DeleteAccount ( accountID , userID string ) error {
unlock := am . Store . AcquireAccountLock ( accountID )
defer unlock ( )
account , err := am . Store . GetAccount ( accountID )
if err != nil {
return err
}
user , err := account . FindUser ( userID )
if err != nil {
return err
}
2023-12-01 17:24:57 +01:00
if ! user . HasAdminPower ( ) {
2023-11-28 14:23:38 +01:00
return status . Errorf ( status . PermissionDenied , "user is not allowed to delete account" )
}
if user . Id != account . CreatedBy {
return status . Errorf ( status . PermissionDenied , "user is not allowed to delete account. Only account owner can delete account" )
}
for _ , otherUser := range account . Users {
if otherUser . IsServiceUser {
continue
}
if otherUser . Id == userID {
continue
}
deleteUserErr := am . deleteRegularUser ( account , userID , otherUser . Id )
if deleteUserErr != nil {
return deleteUserErr
}
}
err = am . deleteRegularUser ( account , userID , userID )
if err != nil {
log . Errorf ( "failed deleting user %s. error: %s" , userID , err )
return err
}
err = am . Store . DeleteAccount ( account )
if err != nil {
log . Errorf ( "failed deleting account %s. error: %s" , accountID , err )
return err
}
2024-01-04 17:10:55 +01:00
// cancel peer login expiry job
am . peerLoginExpiry . Cancel ( [ ] string { account . Id } )
2023-11-28 14:23:38 +01:00
log . Debugf ( "account %s deleted" , accountID )
return nil
}
2024-02-22 12:27:08 +01:00
// GetUsage returns the usage stats for the given account.
// This cannot be used to calculate usage stats for a period in the past as it relies on peers' last seen time.
func ( am * DefaultAccountManager ) GetUsage ( ctx context . Context , accountID string , start time . Time , end time . Time ) ( * AccountUsageStats , error ) {
usageStats , err := am . Store . CalculateUsageStats ( ctx , accountID , start , end )
if err != nil {
return nil , fmt . Errorf ( "failed to calculate usage stats: %w" , err )
}
return usageStats , nil
}
2022-11-07 17:52:23 +01:00
// GetAccountByUserOrAccountID looks for an account by user or accountID, if no account is provided and
// userID doesn't have an account associated with it, one account is created
2024-02-22 12:27:08 +01:00
// domain is used to create a new account if no account is found
2022-11-07 17:52:23 +01:00
func ( am * DefaultAccountManager ) GetAccountByUserOrAccountID ( userID , accountID , domain string ) ( * Account , error ) {
if accountID != "" {
return am . Store . GetAccount ( accountID )
} else if userID != "" {
account , err := am . GetOrCreateAccountByUser ( userID , domain )
2022-01-24 11:21:30 +01:00
if err != nil {
2022-11-11 20:36:45 +01:00
return nil , status . Errorf ( status . NotFound , "account not found using user id: %s" , userID )
2022-01-24 11:21:30 +01:00
}
2022-11-07 17:52:23 +01:00
err = am . addAccountIDToIDPAppMeta ( userID , account )
2022-03-01 15:22:18 +01:00
if err != nil {
return nil , err
2022-01-24 11:21:30 +01:00
}
return account , nil
}
2022-11-11 20:36:45 +01:00
return nil , status . Errorf ( status . NotFound , "no valid user or account Id provided" )
2022-01-24 11:21:30 +01:00
}
2022-05-05 08:58:34 +02:00
func isNil ( i idp . Manager ) bool {
return i == nil || reflect . ValueOf ( i ) . IsNil ( )
}
2022-10-13 18:26:31 +02:00
// addAccountIDToIDPAppMeta update user's app metadata in idp manager
func ( am * DefaultAccountManager ) addAccountIDToIDPAppMeta ( userID string , account * Account ) error {
2022-05-05 08:58:34 +02:00
if ! isNil ( am . idpManager ) {
2022-10-13 18:26:31 +02:00
// user can be nil if it wasn't found (e.g., just created)
user , err := am . lookupUserInCache ( userID , account )
if err != nil {
return err
}
if user != nil && user . AppMetadata . WTAccountID == account . Id {
// it was already set, so we skip the unnecessary update
log . Debugf ( "skipping IDP App Meta update because accountID %s has been already set for user %s" ,
account . Id , userID )
return nil
}
err = am . idpManager . UpdateUserAppMetadata ( userID , idp . AppMetadata { WTAccountID : account . Id } )
2022-03-01 15:22:18 +01:00
if err != nil {
2022-11-11 20:36:45 +01:00
return status . Errorf ( status . Internal , "updating user's app metadata failed with: %v" , err )
2022-03-01 15:22:18 +01:00
}
2022-10-13 18:26:31 +02:00
// refresh cache to reflect the update
_ , err = am . refreshCache ( account . Id )
if err != nil {
return err
}
2022-03-01 15:22:18 +01:00
}
return nil
}
2022-10-13 18:26:31 +02:00
func ( am * DefaultAccountManager ) loadAccount ( _ context . Context , accountID interface { } ) ( [ ] * idp . UserData , error ) {
log . Debugf ( "account %s not found in cache, reloading" , accountID )
2023-10-02 19:18:08 +02:00
accountIDString := fmt . Sprintf ( "%v" , accountID )
account , err := am . Store . GetAccount ( accountIDString )
if err != nil {
return nil , err
}
userData , err := am . idpManager . GetAccount ( accountIDString )
if err != nil {
return nil , err
}
2023-10-11 16:09:30 +02:00
log . Debugf ( "%d entries received from IdP management" , len ( userData ) )
2023-10-02 19:18:08 +02:00
dataMap := make ( map [ string ] * idp . UserData , len ( userData ) )
for _ , datum := range userData {
dataMap [ datum . ID ] = datum
}
matchedUserData := make ( [ ] * idp . UserData , 0 )
for _ , user := range account . Users {
if user . IsServiceUser {
continue
}
datum , ok := dataMap [ user . Id ]
if ! ok {
log . Warnf ( "user %s not found in IDP" , user . Id )
continue
}
matchedUserData = append ( matchedUserData , datum )
}
return matchedUserData , nil
2022-06-05 21:36:42 +02:00
}
2022-10-13 18:26:31 +02:00
func ( am * DefaultAccountManager ) lookupUserInCacheByEmail ( email string , accountID string ) ( * idp . UserData , error ) {
data , err := am . getAccountFromCache ( accountID , false )
2022-09-22 09:06:32 +02:00
if err != nil {
return nil , err
}
2022-10-13 18:26:31 +02:00
for _ , datum := range data {
if datum . Email == email {
2022-09-22 09:06:32 +02:00
return datum , nil
}
}
2023-09-04 17:03:44 +02:00
return nil , nil //nolint:nilnil
2022-09-22 09:06:32 +02:00
}
2022-10-13 18:26:31 +02:00
// lookupUserInCache looks up user in the IdP cache and returns it. If the user wasn't found, the function returns nil
func ( am * DefaultAccountManager ) lookupUserInCache ( userID string , account * Account ) ( * idp . UserData , error ) {
users := make ( map [ string ] struct { } , len ( account . Users ) )
2023-11-13 14:04:18 +01:00
// ignore service users and users provisioned by integrations than are never logged in
2022-10-13 18:26:31 +02:00
for _ , user := range account . Users {
2023-11-13 14:04:18 +01:00
if user . IsServiceUser {
continue
}
2023-11-20 12:05:32 +01:00
if user . Issued == UserIssuedIntegration {
2023-11-13 14:04:18 +01:00
continue
2023-05-10 19:27:17 +02:00
}
2023-11-13 14:04:18 +01:00
users [ user . Id ] = struct { } { }
2022-10-13 18:26:31 +02:00
}
log . Debugf ( "looking up user %s of account %s in cache" , userID , account . Id )
userData , err := am . lookupCache ( users , account . Id )
2022-06-05 21:36:42 +02:00
if err != nil {
return nil , err
}
2022-10-13 18:26:31 +02:00
for _ , datum := range userData {
if datum . ID == userID {
return datum , nil
}
}
2024-02-13 13:10:17 +01:00
// add extra check on external cache manager. We may get to this point when the user is not yet findable in IDP,
// or it didn't have its metadata updated with am.addAccountIDToIDPAppMeta
2024-02-07 16:14:30 +01:00
user , err := account . FindUser ( userID )
if err != nil {
log . Errorf ( "failed finding user %s in account %s" , userID , account . Id )
return nil , err
}
key := user . IntegrationReference . CacheKey ( account . Id , userID )
ud , err := am . externalCacheManager . Get ( am . ctx , key )
2024-02-12 21:54:16 +01:00
if err != nil {
2024-02-13 13:10:17 +01:00
log . Debugf ( "failed to get externalCache for key: %s, error: %s" , key , err )
2024-02-07 16:14:30 +01:00
}
2024-02-12 21:54:16 +01:00
return ud , nil
2022-10-13 18:26:31 +02:00
}
func ( am * DefaultAccountManager ) refreshCache ( accountID string ) ( [ ] * idp . UserData , error ) {
return am . getAccountFromCache ( accountID , true )
}
// getAccountFromCache returns user data for a given account ensuring that cache load happens only once
func ( am * DefaultAccountManager ) getAccountFromCache ( accountID string , forceReload bool ) ( [ ] * idp . UserData , error ) {
am . cacheMux . Lock ( )
loadingChan := am . cacheLoading [ accountID ]
if loadingChan == nil {
loadingChan = make ( chan struct { } )
am . cacheLoading [ accountID ] = loadingChan
am . cacheMux . Unlock ( )
defer func ( ) {
am . cacheMux . Lock ( )
delete ( am . cacheLoading , accountID )
close ( loadingChan )
am . cacheMux . Unlock ( )
} ( )
if forceReload {
err := am . cacheManager . Delete ( am . ctx , accountID )
if err != nil {
return nil , err
}
}
return am . cacheManager . Get ( am . ctx , accountID )
}
am . cacheMux . Unlock ( )
log . Debugf ( "one request to get account %s is already running" , accountID )
select {
case <- loadingChan :
// channel has been closed meaning cache was loaded => simply return from cache
return am . cacheManager . Get ( am . ctx , accountID )
case <- time . After ( 5 * time . Second ) :
return nil , fmt . Errorf ( "timeout while waiting for account %s cache to reload" , accountID )
}
}
func ( am * DefaultAccountManager ) lookupCache ( accountUsers map [ string ] struct { } , accountID string ) ( [ ] * idp . UserData , error ) {
data , err := am . getAccountFromCache ( accountID , false )
if err != nil {
return nil , err
}
2022-06-05 21:36:42 +02:00
userDataMap := make ( map [ string ] struct { } )
2022-10-13 18:26:31 +02:00
for _ , datum := range data {
2022-06-05 21:36:42 +02:00
userDataMap [ datum . ID ] = struct { } { }
}
2023-11-20 16:47:11 +01:00
// the accountUsers ID list of non integration users from store, we check if cache has all of them
// as result of for loop knownUsersCount will have number of users are not presented in the cashed
knownUsersCount := len ( accountUsers )
2022-06-05 21:36:42 +02:00
for user := range accountUsers {
2023-11-20 16:47:11 +01:00
if _ , ok := userDataMap [ user ] ; ok {
knownUsersCount --
continue
2022-06-05 21:36:42 +02:00
}
2023-11-20 16:47:11 +01:00
log . Debugf ( "cache doesn't know about %s user" , user )
2022-06-05 21:36:42 +02:00
}
2023-11-20 16:47:11 +01:00
// if we know users that are not yet in cache more likely cache is outdated
if knownUsersCount > 0 {
log . Debugf ( "cache doesn't know about %d users from store, reloading" , knownUsersCount )
2022-06-05 21:36:42 +02:00
// reload cache once avoiding loops
2022-10-13 18:26:31 +02:00
data , err = am . refreshCache ( accountID )
2022-06-05 21:36:42 +02:00
if err != nil {
return nil , err
}
}
2022-10-13 18:26:31 +02:00
return data , err
2022-06-05 21:36:42 +02:00
}
2024-01-01 19:17:44 +01:00
func ( am * DefaultAccountManager ) removeUserFromCache ( accountID , userID string ) error {
data , err := am . getAccountFromCache ( accountID , false )
if err != nil {
return err
}
for i , datum := range data {
if datum . ID == userID {
data = append ( data [ : i ] , data [ i + 1 : ] ... )
break
}
}
return am . cacheManager . Set ( am . ctx , accountID , data , cacheStore . WithExpiration ( cacheEntryExpiration ( ) ) )
}
2022-03-01 15:22:18 +01:00
// updateAccountDomainAttributes updates the account domain attributes and then, saves the account
2022-11-11 20:36:45 +01:00
func ( am * DefaultAccountManager ) updateAccountDomainAttributes ( account * Account , claims jwtclaims . AuthorizationClaims ,
2023-02-03 21:47:20 +01:00
primaryDomain bool ,
) error {
2022-03-01 15:22:18 +01:00
account . IsDomainPrimaryAccount = primaryDomain
2022-06-20 18:20:43 +02:00
lowerDomain := strings . ToLower ( claims . Domain )
userObj := account . Users [ claims . UserId ]
if account . Domain != lowerDomain && userObj . Role == UserRoleAdmin {
account . Domain = lowerDomain
}
// prevent updating category for different domain until admin logs in
if account . Domain == lowerDomain {
account . DomainCategory = claims . DomainCategory
}
2022-03-01 15:22:18 +01:00
err := am . Store . SaveAccount ( account )
if err != nil {
2022-11-11 20:36:45 +01:00
return err
2022-03-01 15:22:18 +01:00
}
return nil
}
// handleExistingUserAccount handles existing User accounts and update its domain attributes.
//
// If there is no primary domain account yet, we set the account as primary for the domain. Otherwise,
// we compare the account's ID with the domain account ID, and if they don't match, we set the account as
// non-primary account for the domain. We don't merge accounts at this stage, because of cases when a domain
// was previously unclassified or classified as public so N users that logged int that time, has they own account
// and peers that shouldn't be lost.
2022-05-03 16:02:51 +02:00
func ( am * DefaultAccountManager ) handleExistingUserAccount (
existingAcc * Account ,
domainAcc * Account ,
claims jwtclaims . AuthorizationClaims ,
) error {
2022-03-01 15:22:18 +01:00
var err error
2022-03-10 13:47:36 +01:00
if domainAcc != nil && existingAcc . Id != domainAcc . Id {
2022-03-01 15:22:18 +01:00
err = am . updateAccountDomainAttributes ( existingAcc , claims , false )
if err != nil {
return err
}
2022-03-10 13:47:36 +01:00
} else {
err = am . updateAccountDomainAttributes ( existingAcc , claims , true )
if err != nil {
return err
}
2022-03-01 15:22:18 +01:00
}
// we should register the account ID to this user's metadata in our IDP manager
2022-10-13 18:26:31 +02:00
err = am . addAccountIDToIDPAppMeta ( claims . UserId , existingAcc )
2022-03-01 15:22:18 +01:00
if err != nil {
return err
}
return nil
}
// handleNewUserAccount validates if there is an existing primary account for the domain, if so it adds the new user to that account,
// otherwise it will create a new account and make it primary account for the domain.
2022-11-11 20:36:45 +01:00
func ( am * DefaultAccountManager ) handleNewUserAccount ( domainAcc * Account , claims jwtclaims . AuthorizationClaims ) ( * Account , error ) {
2023-02-03 21:47:20 +01:00
if claims . UserId == "" {
return nil , fmt . Errorf ( "user ID is empty" )
}
2022-03-01 15:22:18 +01:00
var (
2022-03-10 13:47:36 +01:00
account * Account
err error
2022-03-01 15:22:18 +01:00
)
lowerDomain := strings . ToLower ( claims . Domain )
// if domain already has a primary account, add regular user
if domainAcc != nil {
account = domainAcc
account . Users [ claims . UserId ] = NewRegularUser ( claims . UserId )
2022-05-09 14:30:20 +02:00
err = am . Store . SaveAccount ( account )
if err != nil {
2022-11-11 20:36:45 +01:00
return nil , err
2022-05-09 14:30:20 +02:00
}
2022-03-01 15:22:18 +01:00
} else {
2022-06-20 18:20:43 +02:00
account , err = am . newAccount ( claims . UserId , lowerDomain )
if err != nil {
return nil , err
}
2022-03-10 13:47:36 +01:00
err = am . updateAccountDomainAttributes ( account , claims , true )
if err != nil {
return nil , err
}
2022-03-01 15:22:18 +01:00
}
2022-10-13 18:26:31 +02:00
err = am . addAccountIDToIDPAppMeta ( claims . UserId , account )
if err != nil {
return nil , err
}
2023-11-08 11:35:37 +01:00
am . StoreEvent ( claims . UserId , claims . UserId , account . Id , activity . UserJoined , nil )
2023-01-02 15:11:32 +01:00
2022-10-13 18:26:31 +02:00
return account , nil
}
// redeemInvite checks whether user has been invited and redeems the invite
func ( am * DefaultAccountManager ) redeemInvite ( account * Account , userID string ) error {
// only possible with the enabled IdP manager
if am . idpManager == nil {
log . Warnf ( "invites only work with enabled IdP manager" )
return nil
}
user , err := am . lookupUserInCache ( userID , account )
if err != nil {
return err
}
if user == nil {
2022-11-11 20:36:45 +01:00
return status . Errorf ( status . NotFound , "user %s not found in the IdP" , userID )
2022-10-13 18:26:31 +02:00
}
2022-10-19 17:51:41 +02:00
if user . AppMetadata . WTPendingInvite != nil && * user . AppMetadata . WTPendingInvite {
2022-10-13 18:26:31 +02:00
log . Infof ( "redeeming invite for user %s account %s" , userID , account . Id )
// User has already logged in, meaning that IdP should have set wt_pending_invite to false.
// Our job is to just reload cache.
go func ( ) {
_ , err = am . refreshCache ( account . Id )
if err != nil {
log . Warnf ( "failed reloading cache when redeeming user %s under account %s" , userID , account . Id )
return
}
log . Debugf ( "user %s of account %s redeemed invite" , user . ID , account . Id )
2023-11-08 11:35:37 +01:00
am . StoreEvent ( userID , userID , account . Id , activity . UserJoined , nil )
2022-10-13 18:26:31 +02:00
} ( )
}
return nil
}
2023-03-30 17:32:44 +02:00
// MarkPATUsed marks a personal access token as used
2023-03-30 10:54:09 +02:00
func ( am * DefaultAccountManager ) MarkPATUsed ( tokenID string ) error {
user , err := am . Store . GetUserByTokenID ( tokenID )
if err != nil {
return err
}
account , err := am . Store . GetAccountByUser ( user . Id )
if err != nil {
return err
}
2023-09-27 15:51:49 +02:00
unlock := am . Store . AcquireAccountLock ( account . Id )
2023-03-31 12:03:53 +02:00
defer unlock ( )
account , err = am . Store . GetAccountByUser ( user . Id )
if err != nil {
return err
}
2023-03-30 10:54:09 +02:00
pat , ok := account . Users [ user . Id ] . PATs [ tokenID ]
if ! ok {
return fmt . Errorf ( "token not found" )
}
2023-04-03 15:09:35 +02:00
pat . LastUsed = time . Now ( ) . UTC ( )
2023-03-30 10:54:09 +02:00
2023-03-30 16:22:15 +02:00
return am . Store . SaveAccount ( account )
2023-03-30 10:54:09 +02:00
}
2023-03-16 15:57:44 +01:00
// GetAccountFromPAT returns Account and User associated with a personal access token
2023-03-29 15:21:53 +02:00
func ( am * DefaultAccountManager ) GetAccountFromPAT ( token string ) ( * Account , * User , * PersonalAccessToken , error ) {
2023-03-16 15:57:44 +01:00
if len ( token ) != PATLength {
2023-03-29 15:21:53 +02:00
return nil , nil , nil , fmt . Errorf ( "token has wrong length" )
2023-03-16 15:57:44 +01:00
}
prefix := token [ : len ( PATPrefix ) ]
if prefix != PATPrefix {
2023-03-29 15:21:53 +02:00
return nil , nil , nil , fmt . Errorf ( "token has wrong prefix" )
2023-03-16 15:57:44 +01:00
}
2023-03-20 11:44:12 +01:00
secret := token [ len ( PATPrefix ) : len ( PATPrefix ) + PATSecretLength ]
encodedChecksum := token [ len ( PATPrefix ) + PATSecretLength : len ( PATPrefix ) + PATSecretLength + PATChecksumLength ]
2023-03-16 15:57:44 +01:00
2023-05-16 12:57:56 +02:00
verificationChecksum , err := base62 . Decode ( encodedChecksum )
2023-03-16 15:57:44 +01:00
if err != nil {
2023-03-29 15:21:53 +02:00
return nil , nil , nil , fmt . Errorf ( "token checksum decoding failed: %w" , err )
2023-03-16 15:57:44 +01:00
}
secretChecksum := crc32 . ChecksumIEEE ( [ ] byte ( secret ) )
if secretChecksum != verificationChecksum {
2023-03-29 15:21:53 +02:00
return nil , nil , nil , fmt . Errorf ( "token checksum does not match" )
2023-03-16 15:57:44 +01:00
}
hashedToken := sha256 . Sum256 ( [ ] byte ( token ) )
2023-03-29 15:21:53 +02:00
encodedHashedToken := b64 . StdEncoding . EncodeToString ( hashedToken [ : ] )
tokenID , err := am . Store . GetTokenIDByHashedToken ( encodedHashedToken )
2023-03-16 15:57:44 +01:00
if err != nil {
2023-03-29 15:21:53 +02:00
return nil , nil , nil , err
2023-03-16 15:57:44 +01:00
}
user , err := am . Store . GetUserByTokenID ( tokenID )
if err != nil {
2023-03-29 15:21:53 +02:00
return nil , nil , nil , err
2023-03-16 15:57:44 +01:00
}
account , err := am . Store . GetAccountByUser ( user . Id )
if err != nil {
2023-03-29 15:21:53 +02:00
return nil , nil , nil , err
}
pat := user . PATs [ tokenID ]
if pat == nil {
return nil , nil , nil , fmt . Errorf ( "personal access token not found" )
2023-03-16 15:57:44 +01:00
}
2023-03-29 15:21:53 +02:00
return account , user , pat , nil
2023-03-16 15:57:44 +01:00
}
2022-10-13 18:26:31 +02:00
// GetAccountFromToken returns an account associated with this token
2022-11-11 20:36:45 +01:00
func ( am * DefaultAccountManager ) GetAccountFromToken ( claims jwtclaims . AuthorizationClaims ) ( * Account , * User , error ) {
2023-02-03 21:47:20 +01:00
if claims . UserId == "" {
return nil , nil , fmt . Errorf ( "user ID is empty" )
}
2022-10-19 17:43:28 +02:00
if am . singleAccountMode && am . singleAccountModeDomain != "" {
// This section is mostly related to self-hosted installations.
// We override incoming domain claims to group users under a single account.
claims . Domain = am . singleAccountModeDomain
claims . DomainCategory = PrivateCategory
log . Infof ( "overriding JWT Domain and DomainCategory claims since single account mode is enabled" )
}
2024-01-20 23:50:57 +01:00
newAcc , err := am . getAccountWithAuthorizationClaims ( claims )
if err != nil {
return nil , nil , err
}
unlock := am . Store . AcquireAccountLock ( newAcc . Id )
alreadyUnlocked := false
defer func ( ) {
if ! alreadyUnlocked {
unlock ( )
}
} ( )
account , err := am . Store . GetAccount ( newAcc . Id )
2022-10-13 18:26:31 +02:00
if err != nil {
2022-11-11 20:36:45 +01:00
return nil , nil , err
}
user := account . Users [ claims . UserId ]
if user == nil {
// this is not really possible because we got an account by user ID
return nil , nil , status . Errorf ( status . NotFound , "user %s not found" , claims . UserId )
2022-10-13 18:26:31 +02:00
}
2023-04-22 12:57:51 +02:00
if ! user . IsServiceUser {
err = am . redeemInvite ( account , claims . UserId )
if err != nil {
return nil , nil , err
}
2022-03-01 15:22:18 +01:00
}
2023-06-27 16:51:05 +02:00
if account . Settings . JWTGroupsEnabled {
if account . Settings . JWTGroupsClaimName == "" {
log . Errorf ( "JWT groups are enabled but no claim name is set" )
return account , user , nil
}
if claim , ok := claims . Raw [ account . Settings . JWTGroupsClaimName ] ; ok {
if slice , ok := claim . ( [ ] interface { } ) ; ok {
2023-08-18 15:36:05 +02:00
var groupsNames [ ] string
2023-06-27 16:51:05 +02:00
for _ , item := range slice {
if g , ok := item . ( string ) ; ok {
2023-08-18 15:36:05 +02:00
groupsNames = append ( groupsNames , g )
2023-06-27 16:51:05 +02:00
} else {
log . Errorf ( "JWT claim %q is not a string: %v" , account . Settings . JWTGroupsClaimName , item )
}
}
2023-08-18 15:36:05 +02:00
oldGroups := make ( [ ] string , len ( user . AutoGroups ) )
copy ( oldGroups , user . AutoGroups )
2023-08-07 17:44:51 +02:00
// if groups were added or modified, save the account
2023-08-18 15:36:05 +02:00
if account . SetJWTGroups ( claims . UserId , groupsNames ) {
2023-08-07 17:44:51 +02:00
if account . Settings . GroupsPropagationEnabled {
if user , err := account . FindUser ( claims . UserId ) ; err == nil {
2023-08-18 15:36:05 +02:00
addNewGroups := difference ( user . AutoGroups , oldGroups )
removeOldGroups := difference ( oldGroups , user . AutoGroups )
account . UserGroupsAddToPeers ( claims . UserId , addNewGroups ... )
account . UserGroupsRemoveFromPeers ( claims . UserId , removeOldGroups ... )
account . Network . IncSerial ( )
if err := am . Store . SaveAccount ( account ) ; err != nil {
log . Errorf ( "failed to save account: %v" , err )
} else {
2023-10-04 15:08:50 +02:00
am . updateAccountPeers ( account )
2024-01-20 23:50:57 +01:00
unlock ( )
alreadyUnlocked = true
2023-08-18 15:36:05 +02:00
for _ , g := range addNewGroups {
if group := account . GetGroup ( g ) ; group != nil {
2023-11-08 11:35:37 +01:00
am . StoreEvent ( user . Id , user . Id , account . Id , activity . GroupAddedToUser ,
2023-08-18 15:36:05 +02:00
map [ string ] any {
"group" : group . Name ,
"group_id" : group . ID ,
"is_service_user" : user . IsServiceUser ,
"user_name" : user . ServiceUserName } )
}
}
for _ , g := range removeOldGroups {
if group := account . GetGroup ( g ) ; group != nil {
2023-11-08 11:35:37 +01:00
am . StoreEvent ( user . Id , user . Id , account . Id , activity . GroupRemovedFromUser ,
2023-08-18 15:36:05 +02:00
map [ string ] any {
"group" : group . Name ,
"group_id" : group . ID ,
"is_service_user" : user . IsServiceUser ,
"user_name" : user . ServiceUserName } )
}
}
}
}
} else {
if err := am . Store . SaveAccount ( account ) ; err != nil {
log . Errorf ( "failed to save account: %v" , err )
2023-08-07 17:44:51 +02:00
}
2023-06-27 16:51:05 +02:00
}
}
} else {
log . Debugf ( "JWT claim %q is not a string array" , account . Settings . JWTGroupsClaimName )
}
} else {
log . Debugf ( "JWT claim %q not found" , account . Settings . JWTGroupsClaimName )
}
}
2022-11-11 20:36:45 +01:00
return account , user , nil
2022-03-01 15:22:18 +01:00
}
2022-10-13 18:26:31 +02:00
// getAccountWithAuthorizationClaims retrievs an account using JWT Claims.
2022-03-01 15:22:18 +01:00
// if domain is of the PrivateCategory category, it will evaluate
// if account is new, existing or if there is another account with the same domain
//
// Use cases:
//
// New user + New account + New domain -> create account, user role = admin (if private domain, index domain)
//
// New user + New account + Existing Private Domain -> add user to the existing account, user role = regular (not admin)
//
// New user + New account + Existing Public Domain -> create account, user role = admin
//
// Existing user + Existing account + Existing Domain -> Nothing changes (if private, index domain)
//
// Existing user + Existing account + Existing Indexed Domain -> Nothing changes
//
// Existing user + Existing account + Existing domain reclassified Domain as private -> Nothing changes (index domain)
2022-11-07 17:52:23 +01:00
func ( am * DefaultAccountManager ) getAccountWithAuthorizationClaims ( claims jwtclaims . AuthorizationClaims ) ( * Account , error ) {
2023-02-03 21:47:20 +01:00
if claims . UserId == "" {
return nil , fmt . Errorf ( "user ID is empty" )
}
2022-03-01 15:22:18 +01:00
// if Account ID is part of the claims
// it means that we've already classified the domain and user has an account
2022-09-29 10:51:18 +02:00
if claims . DomainCategory != PrivateCategory || ! isDomainValid ( claims . Domain ) {
2022-11-07 17:52:23 +01:00
return am . GetAccountByUserOrAccountID ( claims . UserId , claims . AccountId , claims . Domain )
2022-03-09 13:31:42 +01:00
} else if claims . AccountId != "" {
2022-11-07 17:52:23 +01:00
accountFromID , err := am . Store . GetAccount ( claims . AccountId )
2022-03-09 13:31:42 +01:00
if err != nil {
return nil , err
}
2022-05-09 14:30:20 +02:00
if _ , ok := accountFromID . Users [ claims . UserId ] ; ! ok {
return nil , fmt . Errorf ( "user %s is not part of the account id %s" , claims . UserId , claims . AccountId )
}
2023-08-16 16:11:26 +02:00
if accountFromID . DomainCategory == PrivateCategory || claims . DomainCategory != PrivateCategory || accountFromID . Domain != claims . Domain {
2022-03-09 13:31:42 +01:00
return accountFromID , nil
}
2022-03-01 15:22:18 +01:00
}
2022-11-07 17:52:23 +01:00
unlock := am . Store . AcquireGlobalLock ( )
defer unlock ( )
2022-03-01 15:22:18 +01:00
// We checked if the domain has a primary account already
domainAccount , err := am . Store . GetAccountByPrivateDomain ( claims . Domain )
2022-11-11 20:36:45 +01:00
if err != nil {
// if NotFound we are good to continue, otherwise return error
e , ok := status . FromError ( err )
if ! ok || e . Type ( ) != status . NotFound {
return nil , err
}
2022-03-01 15:22:18 +01:00
}
2022-11-07 12:10:56 +01:00
account , err := am . Store . GetAccountByUser ( claims . UserId )
2022-03-01 15:22:18 +01:00
if err == nil {
err = am . handleExistingUserAccount ( account , domainAccount , claims )
if err != nil {
return nil , err
}
return account , nil
2022-11-11 20:36:45 +01:00
} else if s , ok := status . FromError ( err ) ; ok && s . Type ( ) == status . NotFound {
2022-03-01 15:22:18 +01:00
return am . handleNewUserAccount ( domainAccount , claims )
} else {
// other error
return nil , err
}
}
2023-10-11 18:11:45 +02:00
// GetAllConnectedPeers returns connected peers based on peersUpdateManager.GetAllConnectedPeers()
func ( am * DefaultAccountManager ) GetAllConnectedPeers ( ) ( map [ string ] struct { } , error ) {
return am . peersUpdateManager . GetAllConnectedPeers ( ) , nil
}
2023-12-05 14:17:56 +01:00
// HasConnectedChannel returns true if peers has channel in update manager, otherwise false
func ( am * DefaultAccountManager ) HasConnectedChannel ( peerID string ) bool {
return am . peersUpdateManager . HasChannel ( peerID )
}
2023-11-27 13:01:00 +01:00
var invalidDomainRegexp = regexp . MustCompile ( ` ^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z] { 2,}$ ` )
2022-09-29 10:51:18 +02:00
func isDomainValid ( domain string ) bool {
2023-11-27 13:01:00 +01:00
return invalidDomainRegexp . MatchString ( domain )
2022-09-29 10:51:18 +02:00
}
2022-11-21 11:14:42 +01:00
// GetDNSDomain returns the configured dnsDomain
func ( am * DefaultAccountManager ) GetDNSDomain ( ) string {
return am . dnsDomain
}
2023-12-13 11:18:35 +01:00
// CheckUserAccessByJWTGroups checks if the user has access, particularly in cases where the admin enabled JWT
// group propagation and set the list of groups with access permissions.
func ( am * DefaultAccountManager ) CheckUserAccessByJWTGroups ( claims jwtclaims . AuthorizationClaims ) error {
account , _ , err := am . GetAccountFromToken ( claims )
if err != nil {
return err
}
// Ensures JWT group synchronization to the management is enabled before,
// filtering access based on the allowed groups.
if account . Settings != nil && account . Settings . JWTGroupsEnabled {
if allowedGroups := account . Settings . JWTAllowGroups ; len ( allowedGroups ) > 0 {
userJWTGroups := make ( [ ] string , 0 )
if claim , ok := claims . Raw [ account . Settings . JWTGroupsClaimName ] ; ok {
if claimGroups , ok := claim . ( [ ] interface { } ) ; ok {
for _ , g := range claimGroups {
if group , ok := g . ( string ) ; ok {
userJWTGroups = append ( userJWTGroups , group )
}
}
}
}
if ! userHasAllowedGroup ( allowedGroups , userJWTGroups ) {
return fmt . Errorf ( "user does not belong to any of the allowed JWT groups" )
}
}
}
return nil
}
2024-02-22 12:27:08 +01:00
// addAllGroup to account object if it doesn't exist
2023-03-13 15:14:18 +01:00
func addAllGroup ( account * Account ) error {
2022-05-21 15:21:39 +02:00
if len ( account . Groups ) == 0 {
allGroup := & Group {
2023-06-27 16:51:05 +02:00
ID : xid . New ( ) . String ( ) ,
Name : "All" ,
Issued : GroupIssuedAPI ,
2022-05-21 15:21:39 +02:00
}
for _ , peer := range account . Peers {
2023-02-03 10:33:28 +01:00
allGroup . Peers = append ( allGroup . Peers , peer . ID )
2022-05-21 15:21:39 +02:00
}
account . Groups = map [ string ] * Group { allGroup . ID : allGroup }
2024-02-19 17:17:36 +01:00
id := xid . New ( ) . String ( )
defaultPolicy := & Policy {
ID : id ,
2022-06-14 10:32:54 +02:00
Name : DefaultRuleName ,
Description : DefaultRuleDescription ,
2024-02-19 17:17:36 +01:00
Enabled : true ,
Rules : [ ] * PolicyRule {
{
ID : id ,
Name : DefaultRuleName ,
Description : DefaultRuleDescription ,
Enabled : true ,
Sources : [ ] string { allGroup . ID } ,
Destinations : [ ] string { allGroup . ID } ,
Bidirectional : true ,
Protocol : PolicyRuleProtocolALL ,
Action : PolicyTrafficActionAccept ,
} ,
} ,
2022-05-21 15:21:39 +02:00
}
2023-03-13 15:14:18 +01:00
account . Policies = [ ] * Policy { defaultPolicy }
2022-05-21 15:21:39 +02:00
}
2023-03-13 15:14:18 +01:00
return nil
2022-05-21 15:21:39 +02:00
}
2021-08-12 12:49:10 +02:00
// newAccountWithId creates a new Account with a default SetupKey (doesn't store in a Store) and provided id
2023-06-27 17:17:24 +02:00
func newAccountWithId ( accountID , userID , domain string ) * Account {
2021-08-01 19:06:01 +02:00
log . Debugf ( "creating new account" )
2022-01-14 14:34:27 +01:00
network := NewNetwork ( )
2023-11-28 13:45:26 +01:00
peers := make ( map [ string ] * nbpeer . Peer )
2021-12-27 13:17:15 +01:00
users := make ( map [ string ] * User )
2022-08-18 18:22:15 +02:00
routes := make ( map [ string ] * route . Route )
2023-06-27 17:17:24 +02:00
setupKeys := map [ string ] * SetupKey { }
2022-09-30 13:47:11 +02:00
nameServersGroups := make ( map [ string ] * nbdns . NameServerGroup )
2023-12-01 17:24:57 +01:00
users [ userID ] = NewOwnerUser ( userID )
2023-10-11 23:00:56 +02:00
dnsSettings := DNSSettings {
2023-01-17 17:34:40 +01:00
DisabledManagementGroups : make ( [ ] string , 0 ) ,
}
2023-06-27 17:17:24 +02:00
log . Debugf ( "created new account %s" , accountID )
2021-08-01 19:06:01 +02:00
2022-06-09 13:14:34 +02:00
acc := & Account {
2023-06-27 17:17:24 +02:00
Id : accountID ,
2024-03-02 13:49:40 +01:00
CreatedAt : time . Now ( ) . UTC ( ) ,
2023-02-13 15:07:15 +01:00
SetupKeys : setupKeys ,
Network : network ,
Peers : peers ,
Users : users ,
2023-06-27 17:17:24 +02:00
CreatedBy : userID ,
2023-02-13 15:07:15 +01:00
Domain : domain ,
Routes : routes ,
NameServerGroups : nameServersGroups ,
DNSSettings : dnsSettings ,
Settings : & Settings {
PeerLoginExpirationEnabled : true ,
PeerLoginExpiration : DefaultPeerLoginExpiration ,
2024-01-15 19:26:27 +01:00
GroupsPropagationEnabled : true ,
2023-02-13 15:07:15 +01:00
} ,
2022-02-11 17:18:18 +01:00
}
2022-06-09 13:14:34 +02:00
2023-03-13 15:14:18 +01:00
if err := addAllGroup ( acc ) ; err != nil {
log . Errorf ( "error adding all group to account %s: %v" , acc . Id , err )
}
2022-06-09 13:14:34 +02:00
return acc
2021-08-12 12:49:10 +02:00
}
2023-12-13 11:18:35 +01:00
// userHasAllowedGroup checks if a user belongs to any of the allowed groups.
func userHasAllowedGroup ( allowedGroups [ ] string , userGroups [ ] string ) bool {
for _ , userGroup := range userGroups {
for _ , allowedGroup := range allowedGroups {
if userGroup == allowedGroup {
return true
}
}
}
return false
}