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"
2024-10-12 08:35:51 +02:00
"errors"
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"
2024-06-13 08:55:09 +02:00
"slices"
2023-02-03 21:47:20 +01:00
"strings"
"sync"
"time"
2022-10-13 18:26:31 +02:00
"github.com/eko/gocache/v3/cache"
cacheStore "github.com/eko/gocache/v3/store"
2024-10-06 17:01:13 +02:00
gocache "github.com/patrickmn/go-cache"
"github.com/rs/xid"
log "github.com/sirupsen/logrus"
"golang.org/x/exp/maps"
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"
2024-06-13 13:24:24 +02:00
"github.com/netbirdio/netbird/management/domain"
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"
2024-03-27 18:48:48 +01:00
nbgroup "github.com/netbirdio/netbird/management/server/group"
2022-03-26 12:08:54 +01:00
"github.com/netbirdio/netbird/management/server/idp"
2024-03-27 18:48:48 +01:00
"github.com/netbirdio/netbird/management/server/integrated_validator"
2022-03-26 12:08:54 +01:00
"github.com/netbirdio/netbird/management/server/jwtclaims"
2024-12-11 12:58:45 +01:00
"github.com/netbirdio/netbird/management/server/networks"
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"
2024-12-11 12:58:45 +01:00
"github.com/netbirdio/netbird/management/server/store"
2024-08-07 10:52:31 +02:00
"github.com/netbirdio/netbird/management/server/telemetry"
2024-12-11 12:58:45 +01:00
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/management/server/util"
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 (
2024-12-11 12:58:45 +01:00
CacheExpirationMax = 7 * 24 * 3600 * time . Second // 7 days
CacheExpirationMin = 3 * 24 * 3600 * time . Second // 3 days
emptyUserID = "empty user ID in claims"
errorGettingDomainAccIDFmt = "error getting account ID by private domain: %v"
2022-03-01 15:22:18 +01:00
)
2024-04-22 11:10:27 +02:00
type userLoggedInOnce bool
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 {
2024-12-11 12:58:45 +01:00
GetOrCreateAccountByUser ( ctx context . Context , userId , domain string ) ( * types . Account , error )
GetAccount ( ctx context . Context , accountID string ) ( * types . Account , error )
CreateSetupKey ( ctx context . Context , accountID string , keyName string , keyType types . SetupKeyType , expiresIn time . Duration ,
autoGroups [ ] string , usageLimit int , userID string , ephemeral bool ) ( * types . SetupKey , error )
SaveSetupKey ( ctx context . Context , accountID string , key * types . SetupKey , userID string ) ( * types . SetupKey , error )
CreateUser ( ctx context . Context , accountID , initiatorUserID string , key * types . UserInfo ) ( * types . UserInfo , error )
2024-07-03 11:33:02 +02:00
DeleteUser ( ctx context . Context , accountID , initiatorUserID string , targetUserID string ) error
2024-08-08 17:01:38 +02:00
DeleteRegularUsers ( ctx context . Context , accountID , initiatorUserID string , targetUserIDs [ ] string ) error
2024-07-03 11:33:02 +02:00
InviteUser ( ctx context . Context , accountID string , initiatorUserID string , targetUserID string ) error
2024-12-11 12:58:45 +01:00
ListSetupKeys ( ctx context . Context , accountID , userID string ) ( [ ] * types . SetupKey , error )
SaveUser ( ctx context . Context , accountID , initiatorUserID string , update * types . User ) ( * types . UserInfo , error )
SaveOrAddUser ( ctx context . Context , accountID , initiatorUserID string , update * types . User , addIfNotExists bool ) ( * types . UserInfo , error )
SaveOrAddUsers ( ctx context . Context , accountID , initiatorUserID string , updates [ ] * types . User , addIfNotExists bool ) ( [ ] * types . UserInfo , error )
GetSetupKey ( ctx context . Context , accountID , userID , keyID string ) ( * types . SetupKey , error )
GetAccountByID ( ctx context . Context , accountID string , userID string ) ( * types . Account , error )
2024-10-04 17:09:40 +02:00
AccountExists ( ctx context . Context , accountID string ) ( bool , error )
2024-10-04 16:17:01 +02:00
GetAccountIDByUserID ( ctx context . Context , userID , domain string ) ( string , error )
2024-09-27 16:10:50 +02:00
GetAccountIDFromToken ( ctx context . Context , claims jwtclaims . AuthorizationClaims ) ( string , string , error )
2024-07-03 11:33:02 +02:00
CheckUserAccessByJWTGroups ( ctx context . Context , claims jwtclaims . AuthorizationClaims ) error
2024-12-11 12:58:45 +01:00
GetAccountFromPAT ( ctx context . Context , pat string ) ( * types . Account , * types . User , * types . PersonalAccessToken , error )
2024-07-03 11:33:02 +02:00
DeleteAccount ( ctx context . Context , accountID , userID string ) error
MarkPATUsed ( ctx context . Context , tokenID string ) error
2024-12-11 12:58:45 +01:00
GetUserByID ( ctx context . Context , id string ) ( * types . User , error )
GetUser ( ctx context . Context , claims jwtclaims . AuthorizationClaims ) ( * types . User , error )
ListUsers ( ctx context . Context , accountID string ) ( [ ] * types . User , error )
2024-07-03 11:33:02 +02:00
GetPeers ( ctx context . Context , accountID , userID string ) ( [ ] * nbpeer . Peer , error )
2024-12-11 12:58:45 +01:00
MarkPeerConnected ( ctx context . Context , peerKey string , connected bool , realIP net . IP , account * types . Account ) error
2024-07-03 11:33:02 +02:00
DeletePeer ( ctx context . Context , accountID , peerID , userID string ) error
UpdatePeer ( ctx context . Context , accountID , userID string , peer * nbpeer . Peer ) ( * nbpeer . Peer , error )
2024-12-11 12:58:45 +01:00
GetNetworkMap ( ctx context . Context , peerID string ) ( * types . NetworkMap , error )
GetPeerNetwork ( ctx context . Context , peerID string ) ( * types . Network , error )
AddPeer ( ctx context . Context , setupKey , userID string , peer * nbpeer . Peer ) ( * nbpeer . Peer , * types . NetworkMap , [ ] * posture . Checks , error )
CreatePAT ( ctx context . Context , accountID string , initiatorUserID string , targetUserID string , tokenName string , expiresIn int ) ( * types . PersonalAccessTokenGenerated , error )
2024-07-03 11:33:02 +02:00
DeletePAT ( ctx context . Context , accountID string , initiatorUserID string , targetUserID string , tokenID string ) error
2024-12-11 12:58:45 +01:00
GetPAT ( ctx context . Context , accountID string , initiatorUserID string , targetUserID string , tokenID string ) ( * types . PersonalAccessToken , error )
GetAllPATs ( ctx context . Context , accountID string , initiatorUserID string , targetUserID string ) ( [ ] * types . PersonalAccessToken , error )
GetUsersFromAccount ( ctx context . Context , accountID , userID string ) ( [ ] * types . UserInfo , error )
2024-07-03 11:33:02 +02:00
GetGroup ( ctx context . Context , accountId , groupID , userID string ) ( * nbgroup . Group , error )
GetAllGroups ( ctx context . Context , accountID , userID string ) ( [ ] * nbgroup . Group , error )
GetGroupByName ( ctx context . Context , groupName , accountID string ) ( * nbgroup . Group , error )
SaveGroup ( ctx context . Context , accountID , userID string , group * nbgroup . Group ) error
2024-07-15 16:04:06 +02:00
SaveGroups ( ctx context . Context , accountID , userID string , newGroups [ ] * nbgroup . Group ) error
2024-07-03 11:33:02 +02:00
DeleteGroup ( ctx context . Context , accountId , userId , groupID string ) error
2024-08-08 17:01:38 +02:00
DeleteGroups ( ctx context . Context , accountId , userId string , groupIDs [ ] string ) error
2024-07-03 11:33:02 +02:00
GroupAddPeer ( ctx context . Context , accountId , groupID , peerID string ) error
GroupDeletePeer ( ctx context . Context , accountId , groupID , peerID string ) error
2024-12-11 12:58:45 +01:00
GetPolicy ( ctx context . Context , accountID , policyID , userID string ) ( * types . Policy , error )
SavePolicy ( ctx context . Context , accountID , userID string , policy * types . Policy ) ( * types . Policy , error )
2024-07-03 11:33:02 +02:00
DeletePolicy ( ctx context . Context , accountID , policyID , userID string ) error
2024-12-11 12:58:45 +01:00
ListPolicies ( ctx context . Context , accountID , userID string ) ( [ ] * types . Policy , error )
2024-07-03 11:33:02 +02:00
GetRoute ( ctx context . Context , accountID string , routeID route . ID , userID string ) ( * route . Route , error )
2024-10-02 13:41:00 +02:00
CreateRoute ( ctx context . Context , accountID string , prefix netip . Prefix , networkType route . NetworkType , domains domain . List , peerID string , peerGroupIDs [ ] string , description string , netID route . NetID , masquerade bool , metric int , groups , accessControlGroupIDs [ ] string , enabled bool , userID string , keepRoute bool ) ( * route . Route , error )
2024-07-03 11:33:02 +02:00
SaveRoute ( ctx context . Context , accountID , userID string , route * route . Route ) error
DeleteRoute ( ctx context . Context , accountID string , routeID route . ID , userID string ) error
ListRoutes ( ctx context . Context , accountID , userID string ) ( [ ] * route . Route , error )
GetNameServerGroup ( ctx context . Context , accountID , userID , nsGroupID string ) ( * nbdns . NameServerGroup , error )
CreateNameServerGroup ( ctx context . Context , accountID string , name , description string , nameServerList [ ] nbdns . NameServer , groups [ ] string , primary bool , domains [ ] string , enabled bool , userID string , searchDomainsEnabled bool ) ( * nbdns . NameServerGroup , error )
SaveNameServerGroup ( ctx context . Context , accountID , userID string , nsGroupToSave * nbdns . NameServerGroup ) error
DeleteNameServerGroup ( ctx context . Context , accountID , nsGroupID , userID string ) error
ListNameServerGroups ( ctx context . Context , accountID string , userID string ) ( [ ] * nbdns . NameServerGroup , error )
2022-11-21 11:14:42 +01:00
GetDNSDomain ( ) string
2024-07-03 11:33:02 +02:00
StoreEvent ( ctx context . Context , initiatorID , targetID , accountID string , activityID activity . ActivityDescriber , meta map [ string ] any )
GetEvents ( ctx context . Context , accountID , userID string ) ( [ ] * activity . Event , error )
2024-12-11 12:58:45 +01:00
GetDNSSettings ( ctx context . Context , accountID string , userID string ) ( * types . DNSSettings , error )
SaveDNSSettings ( ctx context . Context , accountID string , userID string , dnsSettingsToSave * types . DNSSettings ) error
2024-07-03 11:33:02 +02:00
GetPeer ( ctx context . Context , accountID , peerID , userID string ) ( * nbpeer . Peer , error )
2024-12-11 12:58:45 +01:00
UpdateAccountSettings ( ctx context . Context , accountID , userID string , newSettings * types . Settings ) ( * types . Account , error )
LoginPeer ( ctx context . Context , login PeerLogin ) ( * nbpeer . Peer , * types . NetworkMap , [ ] * posture . Checks , error ) // used by peer gRPC API
SyncPeer ( ctx context . Context , sync PeerSync , account * types . Account ) ( * nbpeer . Peer , * types . NetworkMap , [ ] * posture . Checks , 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-07-03 11:33:02 +02:00
GetPostureChecks ( ctx context . Context , accountID , postureChecksID , userID string ) ( * posture . Checks , error )
2024-11-25 16:26:24 +01:00
SavePostureChecks ( ctx context . Context , accountID , userID string , postureChecks * posture . Checks ) ( * posture . Checks , error )
2024-07-03 11:33:02 +02:00
DeletePostureChecks ( ctx context . Context , accountID , postureChecksID , userID string ) error
ListPostureChecks ( ctx context . Context , accountID , userID string ) ( [ ] * posture . Checks , error )
2024-02-28 16:57:35 +01:00
GetIdpManager ( ) idp . Manager
2024-07-03 11:33:02 +02:00
UpdateIntegratedValidatorGroups ( ctx context . Context , accountID string , userID string , groups [ ] string ) error
GroupValidation ( ctx context . Context , accountId string , groups [ ] string ) ( bool , error )
2024-12-11 12:58:45 +01:00
GetValidatedPeers ( account * types . Account ) ( map [ string ] struct { } , error )
SyncAndMarkPeer ( ctx context . Context , accountID string , peerPubKey string , meta nbpeer . PeerSystemMeta , realIP net . IP ) ( * nbpeer . Peer , * types . NetworkMap , [ ] * posture . Checks , error )
2024-08-01 16:21:43 +02:00
OnPeerDisconnected ( ctx context . Context , accountID string , peerPubKey string ) error
2024-07-03 11:33:02 +02:00
SyncPeerMeta ( ctx context . Context , peerPubKey string , meta nbpeer . PeerSystemMeta ) error
2024-05-30 15:22:42 +02:00
FindExistingPostureCheck ( accountID string , checks * posture . ChecksDefinition ) ( * posture . Checks , error )
2024-07-03 11:33:02 +02:00
GetAccountIDForPeerKey ( ctx context . Context , peerKey string ) ( string , error )
2024-12-11 12:58:45 +01:00
GetAccountSettings ( ctx context . Context , accountID string , userID string ) ( * types . Settings , error )
2024-10-28 17:52:23 +01:00
DeleteSetupKey ( ctx context . Context , accountID , userID , keyID string ) error
2024-12-11 12:58:45 +01:00
GetNetworksManager ( ) networks . Manager
2022-02-22 11:28:19 +01:00
}
type DefaultAccountManager struct {
2024-12-11 12:58:45 +01:00
Store 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
2024-08-21 11:44:52 +02:00
requestBuffer * AccountRequestBuffer
2024-08-20 20:06:01 +02:00
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
2024-10-13 14:52:43 +02:00
peerInactivityExpiry Scheduler
2023-09-19 18:08:40 +02:00
// userDeleteFromIDPEnabled allows to delete user from IDP when user is deleted from account
userDeleteFromIDPEnabled bool
2024-03-27 18:48:48 +01:00
integratedPeerValidator integrated_validator . IntegratedValidator
2024-08-07 10:52:31 +02:00
metrics telemetry . AppMetrics
2023-03-03 18:35:38 +01:00
2024-12-11 12:58:45 +01:00
networksManager networks . Manager
2023-02-03 10:33:28 +01:00
}
2024-10-04 16:17:01 +02:00
// getJWTGroupsChanges calculates the changes needed to sync a user's JWT groups.
// Returns a bool indicating if there are changes in the JWT group membership, the updated user AutoGroups,
// newly groups to create and an error if any occurred.
2024-12-11 12:58:45 +01:00
func ( am * DefaultAccountManager ) getJWTGroupsChanges ( user * types . User , groups [ ] * nbgroup . Group , groupNames [ ] string ) ( bool , [ ] string , [ ] * nbgroup . Group , error ) {
2024-03-27 18:48:48 +01:00
existedGroupsByName := make ( map [ string ] * nbgroup . Group )
2024-10-04 16:17:01 +02:00
for _ , group := range groups {
2023-08-07 17:44:51 +02:00
existedGroupsByName [ group . Name ] = group
}
2024-10-04 16:17:01 +02:00
newUserAutoGroups , jwtGroupsMap := separateGroups ( user . AutoGroups , groups )
2024-12-11 12:58:45 +01:00
groupsToAdd := util . Difference ( groupNames , maps . Keys ( jwtGroupsMap ) )
groupsToRemove := util . Difference ( maps . Keys ( jwtGroupsMap ) , groupNames )
2024-06-13 08:55:09 +02:00
// If no groups are added or removed, we should not sync account
if len ( groupsToAdd ) == 0 && len ( groupsToRemove ) == 0 {
2024-10-04 16:17:01 +02:00
return false , nil , nil , nil
2023-06-27 16:51:05 +02:00
}
2024-10-04 16:17:01 +02:00
newGroupsToCreate := make ( [ ] * nbgroup . Group , 0 )
2023-08-07 17:44:51 +02:00
var modified bool
2024-06-13 08:55:09 +02:00
for _ , name := range groupsToAdd {
group , exists := existedGroupsByName [ name ]
if ! exists {
2024-03-27 18:48:48 +01:00
group = & nbgroup . Group {
2024-10-04 16:17:01 +02:00
ID : xid . New ( ) . String ( ) ,
AccountID : user . AccountID ,
Name : name ,
Issued : nbgroup . GroupIssuedJWT ,
2023-06-27 16:51:05 +02:00
}
2024-10-04 16:17:01 +02:00
newGroupsToCreate = append ( newGroupsToCreate , group )
2023-08-07 17:44:51 +02:00
}
2024-03-27 18:48:48 +01:00
if group . Issued == nbgroup . GroupIssuedJWT {
2024-10-04 16:17:01 +02:00
newUserAutoGroups = append ( newUserAutoGroups , group . ID )
2024-06-13 08:55:09 +02:00
modified = true
2023-06-27 16:51:05 +02:00
}
}
2023-08-07 17:44:51 +02:00
2024-06-13 08:55:09 +02:00
for name , id := range jwtGroupsMap {
if ! slices . Contains ( groupsToRemove , name ) {
2024-10-04 16:17:01 +02:00
newUserAutoGroups = append ( newUserAutoGroups , id )
2024-06-13 08:55:09 +02:00
continue
}
2023-08-18 15:36:05 +02:00
modified = true
}
2024-10-04 16:17:01 +02:00
return modified , newUserAutoGroups , newGroupsToCreate , nil
2023-08-07 17:44:51 +02:00
}
2022-05-21 15:21:39 +02:00
// BuildManager creates a new DefaultAccountManager with a provided Store
2024-08-07 10:52:31 +02:00
func BuildManager (
ctx context . Context ,
2024-12-11 12:58:45 +01:00
store store . Store ,
2024-08-07 10:52:31 +02:00
peersUpdateManager * PeersUpdateManager ,
idpManager idp . Manager ,
singleAccountModeDomain string ,
dnsDomain string ,
eventStore activity . Store ,
geo * geolocation . Geolocation ,
2024-02-20 09:59:56 +01:00
userDeleteFromIDPEnabled bool ,
2024-03-27 18:48:48 +01:00
integratedPeerValidator integrated_validator . IntegratedValidator ,
2024-08-07 10:52:31 +02:00
metrics telemetry . AppMetrics ,
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 ,
2024-12-11 12:58:45 +01:00
networksManager : networks . NewManager ( store ) ,
2023-09-19 18:08:40 +02:00
ctx : context . Background ( ) ,
cacheMux : sync . Mutex { } ,
cacheLoading : map [ string ] chan struct { } { } ,
dnsDomain : dnsDomain ,
eventStore : eventStore ,
peerLoginExpiry : NewDefaultScheduler ( ) ,
2024-10-13 14:52:43 +02:00
peerInactivityExpiry : NewDefaultScheduler ( ) ,
2023-09-19 18:08:40 +02:00
userDeleteFromIDPEnabled : userDeleteFromIDPEnabled ,
2024-03-27 18:48:48 +01:00
integratedPeerValidator : integratedPeerValidator ,
2024-08-07 10:52:31 +02:00
metrics : metrics ,
2024-08-21 11:44:52 +02:00
requestBuffer : NewAccountRequestBuffer ( ctx , store ) ,
2021-07-30 17:46:38 +02:00
}
2024-07-03 11:33:02 +02:00
allAccounts := store . GetAllAccounts ( ctx )
2022-10-19 17:43:28 +02:00
// 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
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Infof ( "single account mode enabled, accounts number %d" , len ( allAccounts ) )
2022-10-19 17:43:28 +02:00
} else {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Infof ( "single account mode disabled, accounts number %d" , len ( allAccounts ) )
2022-10-19 17:43:28 +02:00
}
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 {
2024-07-03 11:33:02 +02:00
err = store . SaveAccount ( ctx , account )
2022-11-07 15:38:21 +01:00
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 ( ) {
2024-07-03 11:33:02 +02:00
err := am . warmupIDPCache ( ctx )
2022-06-06 12:05:44 +02:00
if err != nil {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . 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
}
} ( )
}
2024-07-03 11:33:02 +02:00
am . integratedPeerValidator . SetPeerInvalidationListener ( func ( accountID string ) {
am . onPeersInvalidated ( ctx , accountID )
} )
2024-03-27 18:48:48 +01:00
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
2024-12-11 12:58:45 +01:00
func ( am * DefaultAccountManager ) UpdateAccountSettings ( ctx context . Context , accountID , userID string , newSettings * types . Settings ) ( * types . Account , error ) {
2023-02-16 12:00:41 +01:00
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" )
}
2024-07-31 14:53:32 +02:00
unlock := am . Store . AcquireWriteLockByUID ( ctx , accountID )
2023-02-16 12:00:41 +01:00
defer unlock ( )
2024-07-03 11:33:02 +02:00
account , err := am . Store . GetAccount ( ctx , 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-07-03 11:33:02 +02:00
err = am . integratedPeerValidator . ValidateExtraSettings ( ctx , newSettings . Extra , account . Settings . Extra , account . Peers , userID , accountID )
2024-02-27 14:17:22 +01:00
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
2024-07-03 11:33:02 +02:00
am . peerLoginExpiry . Cancel ( ctx , [ ] string { accountID } )
2023-02-27 16:44:26 +01:00
} else {
2024-07-03 11:33:02 +02:00
am . checkAndSchedulePeerLoginExpiration ( ctx , account )
2023-02-16 12:00:41 +01:00
}
2024-07-03 11:33:02 +02:00
am . StoreEvent ( ctx , userID , accountID , accountID , event , nil )
2023-02-16 12:00:41 +01:00
}
if oldSettings . PeerLoginExpiration != newSettings . PeerLoginExpiration {
2024-07-03 11:33:02 +02:00
am . StoreEvent ( ctx , userID , accountID , accountID , activity . AccountPeerLoginExpirationDurationUpdated , nil )
am . checkAndSchedulePeerLoginExpiration ( ctx , account )
2023-02-16 12:00:41 +01:00
}
2024-10-13 14:52:43 +02:00
err = am . handleInactivityExpirationSettings ( ctx , account , oldSettings , newSettings , userID , accountID )
if err != nil {
return nil , err
}
2024-11-21 15:10:34 +01:00
err = am . handleGroupsPropagationSettings ( ctx , oldSettings , newSettings , userID , accountID )
if err != nil {
return nil , fmt . Errorf ( "groups propagation failed: %w" , err )
}
2023-02-16 12:00:41 +01:00
updatedAccount := account . UpdateSettings ( newSettings )
2024-07-03 11:33:02 +02:00
err = am . Store . SaveAccount ( ctx , account )
2023-02-16 12:00:41 +01:00
if err != nil {
return nil , err
}
return updatedAccount , nil
}
2024-12-11 12:58:45 +01:00
func ( am * DefaultAccountManager ) handleGroupsPropagationSettings ( ctx context . Context , oldSettings , newSettings * types . Settings , userID , accountID string ) error {
2024-11-21 15:10:34 +01:00
if oldSettings . GroupsPropagationEnabled != newSettings . GroupsPropagationEnabled {
if newSettings . GroupsPropagationEnabled {
am . StoreEvent ( ctx , userID , accountID , accountID , activity . UserGroupPropagationEnabled , nil )
// Todo: retroactively add user groups to all peers
} else {
am . StoreEvent ( ctx , userID , accountID , accountID , activity . UserGroupPropagationDisabled , nil )
}
}
return nil
}
2024-12-11 12:58:45 +01:00
func ( am * DefaultAccountManager ) handleInactivityExpirationSettings ( ctx context . Context , account * types . Account , oldSettings , newSettings * types . Settings , userID , accountID string ) error {
2024-11-15 18:21:27 +01:00
if newSettings . PeerInactivityExpirationEnabled {
if oldSettings . PeerInactivityExpiration != newSettings . PeerInactivityExpiration {
oldSettings . PeerInactivityExpiration = newSettings . PeerInactivityExpiration
am . StoreEvent ( ctx , userID , accountID , accountID , activity . AccountPeerInactivityExpirationDurationUpdated , nil )
2024-10-13 14:52:43 +02:00
am . checkAndSchedulePeerInactivityExpiration ( ctx , account )
}
2024-11-15 18:21:27 +01:00
} else {
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 )
}
2024-10-13 14:52:43 +02:00
}
return nil
}
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) peerLoginExpirationJob ( ctx context . Context , accountID string ) func ( ) ( time . Duration , bool ) {
2023-02-27 16:44:26 +01:00
return func ( ) ( time . Duration , bool ) {
2024-07-31 14:53:32 +02:00
unlock := am . Store . AcquireWriteLockByUID ( ctx , accountID )
2023-02-27 16:44:26 +01:00
defer unlock ( )
2024-07-03 11:33:02 +02:00
account , err := am . Store . GetAccount ( ctx , accountID )
2023-02-27 16:44:26 +01:00
if err != nil {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Errorf ( "failed getting account %s expiring peers" , accountID )
2023-02-27 16:44:26 +01:00
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 )
}
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Debugf ( "discovered %d peers to expire for account %s" , len ( peerIDs ) , account . Id )
2023-02-27 16:44:26 +01:00
2024-07-03 11:33:02 +02:00
if err := am . expireAndUpdatePeers ( ctx , account , expiredPeers ) ; err != nil {
log . WithContext ( ctx ) . Errorf ( "failed updating account peers while expiring peers for account %s" , account . Id )
2023-09-19 18:08:40 +02:00
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 ( )
}
}
2024-12-11 12:58:45 +01:00
func ( am * DefaultAccountManager ) checkAndSchedulePeerLoginExpiration ( ctx context . Context , account * types . Account ) {
2024-07-03 11:33:02 +02:00
am . peerLoginExpiry . Cancel ( ctx , [ ] string { account . Id } )
2023-02-27 16:44:26 +01:00
if nextRun , ok := account . GetNextPeerExpiration ( ) ; ok {
2024-07-03 11:33:02 +02:00
go am . peerLoginExpiry . Schedule ( ctx , nextRun , account . Id , am . peerLoginExpirationJob ( ctx , account . Id ) )
2023-02-27 16:44:26 +01:00
}
}
2024-10-13 14:52:43 +02:00
// 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 . AcquireWriteLockByUID ( ctx , accountID )
defer unlock ( )
account , err := am . Store . GetAccount ( ctx , accountID )
if err != nil {
2024-11-07 16:33:57 +01:00
log . Errorf ( "failed getting account %s expiring peers" , accountID )
2024-10-13 14:52:43 +02:00
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
2024-12-11 12:58:45 +01:00
func ( am * DefaultAccountManager ) checkAndSchedulePeerInactivityExpiration ( ctx context . Context , account * types . Account ) {
2024-10-13 14:52:43 +02:00
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 ) )
}
}
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
2024-12-11 12:58:45 +01:00
func ( am * DefaultAccountManager ) newAccount ( ctx context . Context , userID , domain string ) ( * types . Account , error ) {
2022-06-20 18:20:43 +02:00
for i := 0 ; i < 2 ; i ++ {
accountId := xid . New ( ) . String ( )
2024-07-03 11:33:02 +02:00
_ , err := am . Store . GetAccount ( ctx , accountId )
2022-06-20 18:20:43 +02:00
statusErr , _ := status . FromError ( err )
2023-11-27 16:40:02 +01:00
switch {
case err == nil :
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Warnf ( "an account with ID already exists, retrying..." )
2022-06-20 18:20:43 +02:00
continue
2023-11-27 16:40:02 +01:00
case statusErr . Type ( ) == status . NotFound :
2024-07-03 11:33:02 +02:00
newAccount := newAccountWithId ( ctx , accountId , userID , domain )
am . StoreEvent ( ctx , 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
}
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) warmupIDPCache ( ctx context . Context ) error {
userData , err := am . idpManager . GetAllAccounts ( ctx )
2022-06-06 12:05:44 +02:00
if err != nil {
return err
}
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . 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 {
2024-07-03 11:33:02 +02:00
accountID , err := am . Store . GetAccountByUser ( ctx , user . ID )
2023-10-03 16:40:28 +02:00
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 )
2024-04-22 11:10:27 +02:00
rcvdUsers := 0
2022-06-06 12:05:44 +02:00
for accountID , users := range userData {
2024-04-22 11:10:27 +02:00
rcvdUsers += len ( users )
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
}
}
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Infof ( "warmed up IDP cache with %d entries for %d accounts" , rcvdUsers , len ( userData ) )
2022-06-06 12:05:44 +02:00
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
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) DeleteAccount ( ctx context . Context , accountID , userID string ) error {
2024-07-31 14:53:32 +02:00
unlock := am . Store . AcquireWriteLockByUID ( ctx , accountID )
2023-11-28 14:23:38 +01:00
defer unlock ( )
2024-07-03 11:33:02 +02:00
account , err := am . Store . GetAccount ( ctx , accountID )
2023-11-28 14:23:38 +01:00
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" )
}
2024-12-11 12:58:45 +01:00
if user . Role != types . UserRoleOwner {
2023-11-28 14:23:38 +01:00
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
}
2024-07-03 11:33:02 +02:00
deleteUserErr := am . deleteRegularUser ( ctx , account , userID , otherUser . Id )
2023-11-28 14:23:38 +01:00
if deleteUserErr != nil {
return deleteUserErr
}
}
2024-07-03 11:33:02 +02:00
err = am . deleteRegularUser ( ctx , account , userID , userID )
2023-11-28 14:23:38 +01:00
if err != nil {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Errorf ( "failed deleting user %s. error: %s" , userID , err )
2023-11-28 14:23:38 +01:00
return err
}
2024-07-03 11:33:02 +02:00
err = am . Store . DeleteAccount ( ctx , account )
2023-11-28 14:23:38 +01:00
if err != nil {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Errorf ( "failed deleting account %s. error: %s" , accountID , err )
2023-11-28 14:23:38 +01:00
return err
}
2024-01-04 17:10:55 +01:00
// cancel peer login expiry job
2024-07-03 11:33:02 +02:00
am . peerLoginExpiry . Cancel ( ctx , [ ] string { account . Id } )
2024-01-04 17:10:55 +01:00
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Debugf ( "account %s deleted" , accountID )
2023-11-28 14:23:38 +01:00
return nil
}
2024-10-04 17:09:40 +02:00
// AccountExists checks if an account exists.
func ( am * DefaultAccountManager ) AccountExists ( ctx context . Context , accountID string ) ( bool , error ) {
2024-12-11 12:58:45 +01:00
return am . Store . AccountExists ( ctx , store . LockingStrengthShare , accountID )
2024-10-04 17:09:40 +02:00
}
2024-10-04 16:17:01 +02:00
// GetAccountIDByUserID retrieves the account ID based on the userID provided.
// If user does have an account, it returns the user's account ID.
2024-09-27 16:10:50 +02:00
// If the user doesn't have an account, it creates one using the provided domain.
// Returns the account ID or an error if none is found or created.
2024-10-04 16:17:01 +02:00
func ( am * DefaultAccountManager ) GetAccountIDByUserID ( ctx context . Context , userID , domain string ) ( string , error ) {
if userID == "" {
return "" , status . Errorf ( status . NotFound , "no valid userID provided" )
2024-09-27 16:10:50 +02:00
}
2024-10-04 16:17:01 +02:00
accountID , err := am . Store . GetAccountIDByUserID ( userID )
if err != nil {
if s , ok := status . FromError ( err ) ; ok && s . Type ( ) == status . NotFound {
account , err := am . GetOrCreateAccountByUser ( ctx , userID , domain )
if err != nil {
return "" , status . Errorf ( status . NotFound , "account not found or created for user id: %s" , userID )
}
2024-09-27 16:10:50 +02:00
2024-10-12 08:35:51 +02:00
if err = am . addAccountIDToIDPAppMeta ( ctx , userID , account . Id ) ; err != nil {
2024-10-04 16:17:01 +02:00
return "" , err
}
return account . Id , nil
2022-01-24 11:21:30 +01:00
}
2024-10-04 16:17:01 +02:00
return "" , err
2022-01-24 11:21:30 +01:00
}
2024-10-04 16:17:01 +02:00
return accountID , nil
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
2024-10-12 08:35:51 +02:00
func ( am * DefaultAccountManager ) addAccountIDToIDPAppMeta ( ctx context . Context , userID string , accountID string ) error {
2022-05-05 08:58:34 +02:00
if ! isNil ( am . idpManager ) {
2024-12-11 12:58:45 +01:00
accountUsers , err := am . Store . GetAccountUsers ( ctx , store . LockingStrengthShare , accountID )
2024-10-12 08:35:51 +02:00
if err != nil {
return err
}
2024-12-11 12:58:45 +01:00
cachedAccount := & types . Account {
2024-10-17 19:21:35 +02:00
Id : accountID ,
2024-12-11 12:58:45 +01:00
Users : make ( map [ string ] * types . User ) ,
2024-10-12 08:35:51 +02:00
}
for _ , user := range accountUsers {
cachedAccount . Users [ user . Id ] = user
}
2022-10-13 18:26:31 +02:00
// user can be nil if it wasn't found (e.g., just created)
2024-10-12 08:35:51 +02:00
user , err := am . lookupUserInCache ( ctx , userID , cachedAccount )
2022-10-13 18:26:31 +02:00
if err != nil {
return err
}
2024-10-12 08:35:51 +02:00
if user != nil && user . AppMetadata . WTAccountID == accountID {
2022-10-13 18:26:31 +02:00
// it was already set, so we skip the unnecessary update
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Debugf ( "skipping IDP App Meta update because accountID %s has been already set for user %s" ,
2024-10-12 08:35:51 +02:00
accountID , userID )
2022-10-13 18:26:31 +02:00
return nil
}
2024-10-12 08:35:51 +02:00
err = am . idpManager . UpdateUserAppMetadata ( ctx , userID , idp . AppMetadata { WTAccountID : accountID } )
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
2024-10-12 08:35:51 +02:00
_ , err = am . refreshCache ( ctx , accountID )
2022-10-13 18:26:31 +02:00
if err != nil {
return err
}
2022-03-01 15:22:18 +01:00
}
return nil
}
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) loadAccount ( ctx context . Context , accountID interface { } ) ( [ ] * idp . UserData , error ) {
log . WithContext ( ctx ) . Debugf ( "account %s not found in cache, reloading" , accountID )
2023-10-02 19:18:08 +02:00
accountIDString := fmt . Sprintf ( "%v" , accountID )
2024-07-03 11:33:02 +02:00
account , err := am . Store . GetAccount ( ctx , accountIDString )
2023-10-02 19:18:08 +02:00
if err != nil {
return nil , err
}
2024-07-03 11:33:02 +02:00
userData , err := am . idpManager . GetAccount ( ctx , accountIDString )
2023-10-02 19:18:08 +02:00
if err != nil {
return nil , err
}
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . 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 {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Warnf ( "user %s not found in IDP" , user . Id )
2023-10-02 19:18:08 +02:00
continue
}
matchedUserData = append ( matchedUserData , datum )
}
return matchedUserData , nil
2022-06-05 21:36:42 +02:00
}
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) lookupUserInCacheByEmail ( ctx context . Context , email string , accountID string ) ( * idp . UserData , error ) {
data , err := am . getAccountFromCache ( ctx , 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
2024-12-11 12:58:45 +01:00
func ( am * DefaultAccountManager ) lookupUserInCache ( ctx context . Context , userID string , account * types . Account ) ( * idp . UserData , error ) {
2024-04-22 11:10:27 +02:00
users := make ( map [ string ] userLoggedInOnce , 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
}
2024-12-11 12:58:45 +01:00
if user . Issued == types . UserIssuedIntegration {
2023-11-13 14:04:18 +01:00
continue
2023-05-10 19:27:17 +02:00
}
2024-04-22 11:10:27 +02:00
users [ user . Id ] = userLoggedInOnce ( ! user . LastLogin . IsZero ( ) )
2022-10-13 18:26:31 +02:00
}
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Debugf ( "looking up user %s of account %s in cache" , userID , account . Id )
userData , err := am . lookupCache ( ctx , 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 {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Errorf ( "failed finding user %s in account %s" , userID , account . Id )
2024-02-07 16:14:30 +01:00
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-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . 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
}
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) refreshCache ( ctx context . Context , accountID string ) ( [ ] * idp . UserData , error ) {
return am . getAccountFromCache ( ctx , accountID , true )
2022-10-13 18:26:31 +02:00
}
// getAccountFromCache returns user data for a given account ensuring that cache load happens only once
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) getAccountFromCache ( ctx context . Context , accountID string , forceReload bool ) ( [ ] * idp . UserData , error ) {
2022-10-13 18:26:31 +02:00
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 ( )
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Debugf ( "one request to get account %s is already running" , accountID )
2022-10-13 18:26:31 +02:00
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 )
}
}
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) lookupCache ( ctx context . Context , accountUsers map [ string ] userLoggedInOnce , accountID string ) ( [ ] * idp . UserData , error ) {
2024-04-23 19:23:43 +02:00
var data [ ] * idp . UserData
var err error
maxAttempts := 2
2024-07-03 11:33:02 +02:00
data , err = am . getAccountFromCache ( ctx , accountID , false )
2022-10-13 18:26:31 +02:00
if err != nil {
return nil , err
}
2022-06-05 21:36:42 +02:00
2024-04-23 19:23:43 +02:00
for attempt := 1 ; attempt <= maxAttempts ; attempt ++ {
2024-07-03 11:33:02 +02:00
if am . isCacheFresh ( ctx , accountUsers , data ) {
2024-04-23 19:23:43 +02:00
return data , nil
}
if attempt > 1 {
time . Sleep ( 200 * time . Millisecond )
}
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Infof ( "refreshing cache for account %s" , accountID )
data , err = am . refreshCache ( ctx , accountID )
2024-04-23 19:23:43 +02:00
if err != nil {
return nil , err
}
if attempt == maxAttempts {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Warnf ( "cache for account %s reached maximum refresh attempts (%d)" , accountID , maxAttempts )
2024-04-23 19:23:43 +02:00
}
}
return data , nil
}
// isCacheFresh checks if the cache is refreshed already by comparing the accountUsers with the cache data by user count and user invite status
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) isCacheFresh ( ctx context . Context , accountUsers map [ string ] userLoggedInOnce , data [ ] * idp . UserData ) bool {
2024-04-22 11:10:27 +02:00
userDataMap := make ( map [ string ] * idp . UserData , len ( data ) )
2022-10-13 18:26:31 +02:00
for _ , datum := range data {
2024-04-22 11:10:27 +02:00
userDataMap [ datum . ID ] = datum
2022-06-05 21:36:42 +02:00
}
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 )
2024-04-22 11:10:27 +02:00
for user , loggedInOnce := range accountUsers {
if datum , ok := userDataMap [ user ] ; ok {
// check if the matching user data has a pending invite and if the user has logged in once, forcing the cache to be refreshed
if datum . AppMetadata . WTPendingInvite != nil && * datum . AppMetadata . WTPendingInvite && loggedInOnce == true { //nolint:gosimple
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Infof ( "user %s has a pending invite and has logged in once, cache invalid" , user )
2024-04-23 19:23:43 +02:00
return false
2024-04-22 11:10:27 +02:00
}
2023-11-20 16:47:11 +01:00
knownUsersCount --
continue
2022-06-05 21:36:42 +02:00
}
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . 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
2024-04-23 19:23:43 +02:00
if knownUsersCount > 0 {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Infof ( "cache invalid. Users unknown to the cache: %d" , knownUsersCount )
2024-04-23 19:23:43 +02:00
return false
2022-06-05 21:36:42 +02:00
}
2024-04-23 19:23:43 +02:00
return true
2022-06-05 21:36:42 +02:00
}
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) removeUserFromCache ( ctx context . Context , accountID , userID string ) error {
data , err := am . getAccountFromCache ( ctx , accountID , false )
2024-01-01 19:17:44 +01:00
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 ( ) ) )
}
2024-10-12 08:35:51 +02:00
// updateAccountDomainAttributesIfNotUpToDate updates the account domain attributes if they are not up to date and then, saves the account changes
func ( am * DefaultAccountManager ) updateAccountDomainAttributesIfNotUpToDate ( ctx context . Context , accountID string , claims jwtclaims . AuthorizationClaims ,
2023-02-03 21:47:20 +01:00
primaryDomain bool ,
) error {
2024-10-12 08:35:51 +02:00
if claims . Domain == "" {
log . WithContext ( ctx ) . Errorf ( "claims don't contain a valid domain, skipping domain attributes update. Received claims: %v" , claims )
return nil
}
2022-06-20 18:20:43 +02:00
2024-10-12 08:35:51 +02:00
unlockAccount := am . Store . AcquireWriteLockByUID ( ctx , accountID )
defer unlockAccount ( )
2024-03-14 14:17:22 +01:00
2024-12-11 12:58:45 +01:00
accountDomain , domainCategory , err := am . Store . GetAccountDomainAndCategory ( ctx , store . LockingStrengthShare , accountID )
2024-10-12 08:35:51 +02:00
if err != nil {
log . WithContext ( ctx ) . Errorf ( "error getting account domain and category: %v" , err )
return err
2022-06-20 18:20:43 +02:00
}
2024-10-12 08:35:51 +02:00
if domainIsUpToDate ( accountDomain , domainCategory , claims ) {
return nil
}
2024-12-11 12:58:45 +01:00
user , err := am . Store . GetUserByUserID ( ctx , store . LockingStrengthShare , claims . UserId )
2022-03-01 15:22:18 +01:00
if err != nil {
2024-10-12 08:35:51 +02:00
log . WithContext ( ctx ) . Errorf ( "error getting user: %v" , err )
2022-11-11 20:36:45 +01:00
return err
2022-03-01 15:22:18 +01:00
}
2024-10-12 08:35:51 +02:00
newDomain := accountDomain
newCategoty := domainCategory
lowerDomain := strings . ToLower ( claims . Domain )
if accountDomain != lowerDomain && user . HasAdminPower ( ) {
newDomain = lowerDomain
}
if accountDomain == lowerDomain {
newCategoty = claims . DomainCategory
}
return am . Store . UpdateAccountDomainAttributes ( ctx , accountID , newDomain , newCategoty , primaryDomain )
2022-03-01 15:22:18 +01:00
}
// handleExistingUserAccount handles existing User accounts and update its domain attributes.
2024-10-12 08:35:51 +02:00
// 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 (
2024-07-03 11:33:02 +02:00
ctx context . Context ,
2024-10-12 08:35:51 +02:00
userAccountID string ,
domainAccountID string ,
2022-05-03 16:02:51 +02:00
claims jwtclaims . AuthorizationClaims ,
) error {
2024-10-12 08:35:51 +02:00
primaryDomain := domainAccountID == "" || userAccountID == domainAccountID
err := am . updateAccountDomainAttributesIfNotUpToDate ( ctx , userAccountID , claims , primaryDomain )
2024-04-23 19:09:58 +02:00
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
2024-10-12 08:35:51 +02:00
err = am . addAccountIDToIDPAppMeta ( ctx , claims . UserId , userAccountID )
2022-03-01 15:22:18 +01:00
if err != nil {
return err
}
return nil
}
2024-10-12 08:35:51 +02:00
// addNewPrivateAccount validates if there is an existing primary account for the domain, if so it adds the new user to that account,
2022-03-01 15:22:18 +01:00
// otherwise it will create a new account and make it primary account for the domain.
2024-10-12 08:35:51 +02:00
func ( am * DefaultAccountManager ) addNewPrivateAccount ( ctx context . Context , domainAccountID string , claims jwtclaims . AuthorizationClaims ) ( string , error ) {
2023-02-03 21:47:20 +01:00
if claims . UserId == "" {
2024-10-12 08:35:51 +02:00
return "" , fmt . Errorf ( "user ID is empty" )
2023-02-03 21:47:20 +01:00
}
2024-10-12 08:35:51 +02:00
2022-03-01 15:22:18 +01:00
lowerDomain := strings . ToLower ( claims . Domain )
2024-10-12 08:35:51 +02:00
newAccount , err := am . newAccount ( ctx , claims . UserId , lowerDomain )
if err != nil {
return "" , err
2022-03-01 15:22:18 +01:00
}
2024-10-12 08:35:51 +02:00
newAccount . Domain = lowerDomain
newAccount . DomainCategory = claims . DomainCategory
newAccount . IsDomainPrimaryAccount = true
err = am . Store . SaveAccount ( ctx , newAccount )
2022-10-13 18:26:31 +02:00
if err != nil {
2024-10-12 08:35:51 +02:00
return "" , err
}
err = am . addAccountIDToIDPAppMeta ( ctx , claims . UserId , newAccount . Id )
if err != nil {
return "" , err
}
am . StoreEvent ( ctx , claims . UserId , claims . UserId , newAccount . Id , activity . UserJoined , nil )
return newAccount . Id , nil
}
func ( am * DefaultAccountManager ) addNewUserToDomainAccount ( ctx context . Context , domainAccountID string , claims jwtclaims . AuthorizationClaims ) ( string , error ) {
unlockAccount := am . Store . AcquireWriteLockByUID ( ctx , domainAccountID )
defer unlockAccount ( )
2024-12-11 12:58:45 +01:00
usersMap := make ( map [ string ] * types . User )
usersMap [ claims . UserId ] = types . NewRegularUser ( claims . UserId )
2024-10-12 08:35:51 +02:00
err := am . Store . SaveUsers ( domainAccountID , usersMap )
if err != nil {
return "" , err
2022-10-13 18:26:31 +02:00
}
2024-10-12 08:35:51 +02:00
err = am . addAccountIDToIDPAppMeta ( ctx , claims . UserId , domainAccountID )
if err != nil {
return "" , err
}
2023-01-02 15:11:32 +01:00
2024-10-12 08:35:51 +02:00
am . StoreEvent ( ctx , claims . UserId , claims . UserId , domainAccountID , activity . UserJoined , nil )
return domainAccountID , nil
2022-10-13 18:26:31 +02:00
}
// redeemInvite checks whether user has been invited and redeems the invite
2024-09-27 16:10:50 +02:00
func ( am * DefaultAccountManager ) redeemInvite ( ctx context . Context , accountID string , userID string ) error {
2022-10-13 18:26:31 +02:00
// only possible with the enabled IdP manager
if am . idpManager == nil {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Warnf ( "invites only work with enabled IdP manager" )
2022-10-13 18:26:31 +02:00
return nil
}
2024-09-27 16:10:50 +02:00
account , err := am . Store . GetAccount ( ctx , accountID )
if err != nil {
return err
}
2024-07-03 11:33:02 +02:00
user , err := am . lookupUserInCache ( ctx , userID , account )
2022-10-13 18:26:31 +02:00
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 {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Infof ( "redeeming invite for user %s account %s" , userID , account . Id )
2022-10-13 18:26:31 +02:00
// 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 ( ) {
2024-07-03 11:33:02 +02:00
_ , err = am . refreshCache ( ctx , account . Id )
2022-10-13 18:26:31 +02:00
if err != nil {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Warnf ( "failed reloading cache when redeeming user %s under account %s" , userID , account . Id )
2022-10-13 18:26:31 +02:00
return
}
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Debugf ( "user %s of account %s redeemed invite" , user . ID , account . Id )
am . StoreEvent ( ctx , 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
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) MarkPATUsed ( ctx context . Context , tokenID string ) error {
2023-03-30 10:54:09 +02:00
2024-07-03 11:33:02 +02:00
user , err := am . Store . GetUserByTokenID ( ctx , tokenID )
2023-03-30 10:54:09 +02:00
if err != nil {
return err
}
2024-07-03 11:33:02 +02:00
account , err := am . Store . GetAccountByUser ( ctx , user . Id )
2023-03-30 10:54:09 +02:00
if err != nil {
return err
}
2024-07-31 14:53:32 +02:00
unlock := am . Store . AcquireWriteLockByUID ( ctx , account . Id )
2023-03-31 12:03:53 +02:00
defer unlock ( )
2024-07-03 11:33:02 +02:00
account , err = am . Store . GetAccountByUser ( ctx , user . Id )
2023-03-31 12:03:53 +02:00
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
2024-07-03 11:33:02 +02:00
return am . Store . SaveAccount ( ctx , account )
2023-03-30 10:54:09 +02:00
}
2024-09-27 16:10:50 +02:00
// GetAccount returns an account associated with this account ID.
2024-12-11 12:58:45 +01:00
func ( am * DefaultAccountManager ) GetAccount ( ctx context . Context , accountID string ) ( * types . Account , error ) {
2024-09-27 16:10:50 +02:00
return am . Store . GetAccount ( ctx , accountID )
}
2023-03-16 15:57:44 +01:00
// GetAccountFromPAT returns Account and User associated with a personal access token
2024-12-11 12:58:45 +01:00
func ( am * DefaultAccountManager ) GetAccountFromPAT ( ctx context . Context , token string ) ( * types . Account , * types . User , * types . PersonalAccessToken , error ) {
if len ( token ) != types . 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
}
2024-12-11 12:58:45 +01:00
prefix := token [ : len ( types . PATPrefix ) ]
if prefix != types . 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
}
2024-12-11 12:58:45 +01:00
secret := token [ len ( types . PATPrefix ) : len ( types . PATPrefix ) + types . PATSecretLength ]
encodedChecksum := token [ len ( types . PATPrefix ) + types . PATSecretLength : len ( types . PATPrefix ) + types . PATSecretLength + types . 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 [ : ] )
2024-07-03 11:33:02 +02:00
tokenID , err := am . Store . GetTokenIDByHashedToken ( ctx , 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
}
2024-07-03 11:33:02 +02:00
user , err := am . Store . GetUserByTokenID ( ctx , tokenID )
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
}
2024-07-03 11:33:02 +02:00
account , err := am . Store . GetAccountByUser ( ctx , user . Id )
2023-03-16 15:57:44 +01:00
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
}
2024-09-27 16:10:50 +02:00
// GetAccountByID returns an account associated with this account ID.
2024-12-11 12:58:45 +01:00
func ( am * DefaultAccountManager ) GetAccountByID ( ctx context . Context , accountID string , userID string ) ( * types . Account , error ) {
user , err := am . Store . GetUserByUserID ( ctx , store . LockingStrengthShare , userID )
2024-09-27 16:10:50 +02:00
if err != nil {
return nil , err
}
2024-10-06 17:01:13 +02:00
if user . AccountID != accountID {
2024-09-27 16:10:50 +02:00
return nil , status . Errorf ( status . PermissionDenied , "the user has no permission to access account data" )
}
return am . Store . GetAccount ( ctx , accountID )
}
// GetAccountIDFromToken returns an account ID associated with this token.
func ( am * DefaultAccountManager ) GetAccountIDFromToken ( ctx context . Context , claims jwtclaims . AuthorizationClaims ) ( string , string , error ) {
2023-02-03 21:47:20 +01:00
if claims . UserId == "" {
2024-10-12 08:35:51 +02:00
return "" , "" , errors . New ( emptyUserID )
2023-02-03 21:47:20 +01:00
}
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
2024-12-11 12:58:45 +01:00
claims . DomainCategory = types . PrivateCategory
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Debugf ( "overriding JWT Domain and DomainCategory claims since single account mode is enabled" )
2022-10-19 17:43:28 +02:00
}
2024-09-27 16:10:50 +02:00
accountID , err := am . getAccountIDWithAuthorizationClaims ( ctx , claims )
2024-01-20 23:50:57 +01:00
if err != nil {
2024-09-27 16:10:50 +02:00
return "" , "" , err
2024-01-20 23:50:57 +01:00
}
2024-12-11 12:58:45 +01:00
user , err := am . Store . GetUserByUserID ( ctx , store . LockingStrengthShare , claims . UserId )
2022-10-13 18:26:31 +02:00
if err != nil {
2022-11-11 20:36:45 +01:00
// this is not really possible because we got an account by user ID
2024-09-27 16:10:50 +02:00
return "" , "" , status . Errorf ( status . NotFound , "user %s not found" , claims . UserId )
2022-10-13 18:26:31 +02:00
}
2024-10-04 16:17:01 +02:00
if user . AccountID != accountID {
return "" , "" , status . Errorf ( status . PermissionDenied , "user %s is not part of the account %s" , claims . UserId , accountID )
}
2024-04-22 11:10:27 +02:00
if ! user . IsServiceUser && claims . Invited {
2024-09-27 16:10:50 +02:00
err = am . redeemInvite ( ctx , accountID , user . Id )
2023-04-22 12:57:51 +02:00
if err != nil {
2024-09-27 16:10:50 +02:00
return "" , "" , err
2023-04-22 12:57:51 +02:00
}
2022-03-01 15:22:18 +01:00
}
2024-10-04 16:17:01 +02:00
if err = am . syncJWTGroups ( ctx , accountID , claims ) ; err != nil {
2024-09-27 16:10:50 +02:00
return "" , "" , err
}
return accountID , user . Id , nil
}
// syncJWTGroups processes the JWT groups for a user, updates the account based on the groups,
// and propagates changes to peers if group propagation is enabled.
2024-10-04 16:17:01 +02:00
func ( am * DefaultAccountManager ) syncJWTGroups ( ctx context . Context , accountID string , claims jwtclaims . AuthorizationClaims ) error {
2024-12-11 12:58:45 +01:00
settings , err := am . Store . GetAccountSettings ( ctx , store . LockingStrengthShare , accountID )
2024-09-27 16:10:50 +02:00
if err != nil {
return err
}
if settings == nil || ! settings . JWTGroupsEnabled {
return nil
}
if settings . JWTGroupsClaimName == "" {
2024-10-04 16:17:01 +02:00
log . WithContext ( ctx ) . Debugf ( "JWT groups are enabled but no claim name is set" )
2024-09-27 16:10:50 +02:00
return nil
}
jwtGroupsNames := extractJWTGroups ( ctx , settings . JWTGroupsClaimName , claims )
2024-10-22 12:29:17 +02:00
unlockAccount := am . Store . AcquireWriteLockByUID ( ctx , accountID )
2024-10-04 16:17:01 +02:00
defer func ( ) {
2024-10-22 12:29:17 +02:00
if unlockAccount != nil {
unlockAccount ( )
2024-10-04 16:17:01 +02:00
}
} ( )
2024-09-27 16:10:50 +02:00
2024-10-04 16:17:01 +02:00
var addNewGroups [ ] string
var removeOldGroups [ ] string
var hasChanges bool
2024-12-11 12:58:45 +01:00
var user * types . User
err = am . Store . ExecuteInTransaction ( ctx , func ( transaction store . Store ) error {
user , err = transaction . GetUserByUserID ( ctx , store . LockingStrengthShare , claims . UserId )
2024-10-04 16:17:01 +02:00
if err != nil {
return fmt . Errorf ( "error getting user: %w" , err )
}
2024-09-27 16:10:50 +02:00
2024-12-11 12:58:45 +01:00
groups , err := transaction . GetAccountGroups ( ctx , store . LockingStrengthShare , accountID )
2024-10-04 16:17:01 +02:00
if err != nil {
return fmt . Errorf ( "error getting account groups: %w" , err )
2023-06-27 16:51:05 +02:00
}
2023-08-18 15:36:05 +02:00
2024-10-04 16:17:01 +02:00
changed , updatedAutoGroups , newGroupsToCreate , err := am . getJWTGroupsChanges ( user , groups , jwtGroupsNames )
if err != nil {
return fmt . Errorf ( "error getting JWT groups changes: %w" , err )
}
hasChanges = changed
// skip update if no changes
if ! changed {
2024-09-27 16:10:50 +02:00
return nil
}
2024-12-11 12:58:45 +01:00
if err = transaction . SaveGroups ( ctx , store . LockingStrengthUpdate , newGroupsToCreate ) ; err != nil {
2024-10-04 16:17:01 +02:00
return fmt . Errorf ( "error saving groups: %w" , err )
}
2024-12-11 12:58:45 +01:00
addNewGroups = util . Difference ( updatedAutoGroups , user . AutoGroups )
removeOldGroups = util . Difference ( user . AutoGroups , updatedAutoGroups )
2024-10-04 16:17:01 +02:00
user . AutoGroups = updatedAutoGroups
2024-12-11 12:58:45 +01:00
if err = transaction . SaveUser ( ctx , store . LockingStrengthUpdate , user ) ; err != nil {
2024-10-04 16:17:01 +02:00
return fmt . Errorf ( "error saving user: %w" , err )
}
2024-09-27 16:10:50 +02:00
// Propagate changes to peers if group propagation is enabled
if settings . GroupsPropagationEnabled {
2024-12-11 12:58:45 +01:00
groups , err = transaction . GetAccountGroups ( ctx , store . LockingStrengthShare , accountID )
2024-10-04 16:17:01 +02:00
if err != nil {
return fmt . Errorf ( "error getting account groups: %w" , err )
}
groupsMap := make ( map [ string ] * nbgroup . Group , len ( groups ) )
for _ , group := range groups {
groupsMap [ group . ID ] = group
}
2024-12-11 12:58:45 +01:00
peers , err := transaction . GetUserPeers ( ctx , store . LockingStrengthShare , accountID , claims . UserId )
2024-10-04 16:17:01 +02:00
if err != nil {
return fmt . Errorf ( "error getting user peers: %w" , err )
}
updatedGroups , err := am . updateUserPeersInGroups ( groupsMap , peers , addNewGroups , removeOldGroups )
if err != nil {
return fmt . Errorf ( "error modifying user peers in groups: %w" , err )
}
2024-12-11 12:58:45 +01:00
if err = transaction . SaveGroups ( ctx , store . LockingStrengthUpdate , updatedGroups ) ; err != nil {
2024-10-04 16:17:01 +02:00
return fmt . Errorf ( "error saving groups: %w" , err )
}
2024-12-11 12:58:45 +01:00
if err = transaction . IncrementNetworkSerial ( ctx , store . LockingStrengthUpdate , accountID ) ; err != nil {
2024-10-04 16:17:01 +02:00
return fmt . Errorf ( "error incrementing network serial: %w" , err )
2024-09-27 16:10:50 +02:00
}
}
2024-10-22 12:29:17 +02:00
unlockAccount ( )
unlockAccount = nil
2024-10-04 16:17:01 +02:00
return nil
} )
if err != nil {
return err
}
if ! hasChanges {
return nil
}
2024-09-27 16:10:50 +02:00
2024-10-04 16:17:01 +02:00
for _ , g := range addNewGroups {
2024-12-11 12:58:45 +01:00
group , err := am . Store . GetGroupByID ( ctx , store . LockingStrengthShare , accountID , g )
2024-10-04 16:17:01 +02:00
if err != nil {
log . WithContext ( ctx ) . Debugf ( "group %s not found while saving user activity event of account %s" , g , accountID )
} else {
meta := map [ string ] any {
"group" : group . Name , "group_id" : group . ID ,
"is_service_user" : user . IsServiceUser , "user_name" : user . ServiceUserName ,
}
am . StoreEvent ( ctx , user . Id , user . Id , accountID , activity . GroupAddedToUser , meta )
}
}
for _ , g := range removeOldGroups {
2024-12-11 12:58:45 +01:00
group , err := am . Store . GetGroupByID ( ctx , store . LockingStrengthShare , accountID , g )
2024-10-04 16:17:01 +02:00
if err != nil {
log . WithContext ( ctx ) . Debugf ( "group %s not found while saving user activity event of account %s" , g , accountID )
} else {
meta := map [ string ] any {
"group" : group . Name , "group_id" : group . ID ,
"is_service_user" : user . IsServiceUser , "user_name" : user . ServiceUserName ,
2023-06-27 16:51:05 +02:00
}
2024-10-04 16:17:01 +02:00
am . StoreEvent ( ctx , user . Id , user . Id , accountID , activity . GroupRemovedFromUser , meta )
2023-06-27 16:51:05 +02:00
}
}
2024-10-04 16:17:01 +02:00
if settings . GroupsPropagationEnabled {
2024-11-15 18:09:32 +01:00
removedGroupAffectsPeers , err := areGroupChangesAffectPeers ( ctx , am . Store , accountID , removeOldGroups )
2024-10-04 16:17:01 +02:00
if err != nil {
2024-11-15 18:09:32 +01:00
return err
2024-10-04 16:17:01 +02:00
}
2024-11-15 18:09:32 +01:00
newGroupsAffectsPeers , err := areGroupChangesAffectPeers ( ctx , am . Store , accountID , addNewGroups )
if err != nil {
return err
}
if removedGroupAffectsPeers || newGroupsAffectsPeers {
2024-10-23 12:05:02 +02:00
log . WithContext ( ctx ) . Tracef ( "user %s: JWT group membership changed, updating account peers" , claims . UserId )
2024-11-15 18:09:32 +01:00
am . updateAccountPeers ( ctx , accountID )
2024-10-23 12:05:02 +02:00
}
2024-10-04 16:17:01 +02:00
}
2024-09-27 16:10:50 +02:00
return nil
2022-03-01 15:22:18 +01:00
}
2024-09-27 16:10:50 +02:00
// getAccountIDWithAuthorizationClaims retrieves an account ID using JWT Claims.
2024-10-12 08:35:51 +02:00
// if domain is not private or domain is invalid, it will return the account ID by user ID.
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:
//
2024-10-12 08:35:51 +02:00
// New user + New account + New domain -> create account, user role = owner (if private domain, index domain)
2022-03-01 15:22:18 +01:00
//
2024-10-12 08:35:51 +02:00
// New user + New account + Existing Private Domain -> add user to the existing account, user role = user (not admin)
2022-03-01 15:22:18 +01:00
//
2024-10-12 08:35:51 +02:00
// New user + New account + Existing Public Domain -> create account, user role = owner
2022-03-01 15:22:18 +01:00
//
// 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)
2024-09-27 16:10:50 +02:00
func ( am * DefaultAccountManager ) getAccountIDWithAuthorizationClaims ( ctx context . Context , claims jwtclaims . AuthorizationClaims ) ( string , error ) {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Tracef ( "getting account with authorization claims. User ID: \"%s\", Account ID: \"%s\", Domain: \"%s\", Domain Category: \"%s\"" ,
2024-05-27 12:29:28 +02:00
claims . UserId , claims . AccountId , claims . Domain , claims . DomainCategory )
2024-10-12 08:35:51 +02:00
2023-02-03 21:47:20 +01:00
if claims . UserId == "" {
2024-10-12 08:35:51 +02:00
return "" , errors . New ( emptyUserID )
2023-02-03 21:47:20 +01:00
}
2024-09-27 16:10:50 +02:00
2024-12-11 12:58:45 +01:00
if claims . DomainCategory != types . PrivateCategory || ! isDomainValid ( claims . Domain ) {
2024-10-04 16:17:01 +02:00
return am . GetAccountIDByUserID ( ctx , claims . UserId , claims . Domain )
2024-10-12 08:35:51 +02:00
}
2024-09-27 16:10:50 +02:00
2024-10-12 08:35:51 +02:00
if claims . AccountId != "" {
return am . handlePrivateAccountWithIDFromClaim ( ctx , claims )
}
2024-09-27 16:10:50 +02:00
2024-10-12 08:35:51 +02:00
// We checked if the domain has a primary account already
domainAccountID , cancel , err := am . getPrivateDomainWithGlobalLock ( ctx , claims . Domain )
if cancel != nil {
defer cancel ( )
}
if err != nil {
return "" , err
}
userAccountID , err := am . Store . GetAccountIDByUserID ( claims . UserId )
if handleNotFound ( err ) != nil {
log . WithContext ( ctx ) . Errorf ( "error getting account ID by user ID: %v" , err )
return "" , err
}
if userAccountID != "" {
if err = am . handleExistingUserAccount ( ctx , userAccountID , domainAccountID , claims ) ; err != nil {
2024-09-27 16:10:50 +02:00
return "" , err
}
2024-10-12 08:35:51 +02:00
return userAccountID , nil
2022-03-01 15:22:18 +01:00
}
2024-10-12 08:35:51 +02:00
if domainAccountID != "" {
return am . addNewUserToDomainAccount ( ctx , domainAccountID , claims )
}
return am . addNewPrivateAccount ( ctx , domainAccountID , claims )
}
func ( am * DefaultAccountManager ) getPrivateDomainWithGlobalLock ( ctx context . Context , domain string ) ( string , context . CancelFunc , error ) {
2024-12-11 12:58:45 +01:00
domainAccountID , err := am . Store . GetAccountIDByPrivateDomain ( ctx , store . LockingStrengthShare , domain )
2024-10-12 08:35:51 +02:00
if handleNotFound ( err ) != nil {
log . WithContext ( ctx ) . Errorf ( errorGettingDomainAccIDFmt , err )
return "" , nil , err
}
if domainAccountID != "" {
return domainAccountID , nil , nil
}
log . WithContext ( ctx ) . Debugf ( "no primary account found for domain %s, acquiring global lock" , domain )
cancel := am . Store . AcquireGlobalLock ( ctx )
// check again if the domain has a primary account because of simultaneous requests
2024-12-11 12:58:45 +01:00
domainAccountID , err = am . Store . GetAccountIDByPrivateDomain ( ctx , store . LockingStrengthShare , domain )
2024-10-12 08:35:51 +02:00
if handleNotFound ( err ) != nil {
2024-10-19 18:32:17 +02:00
cancel ( )
2024-10-12 08:35:51 +02:00
log . WithContext ( ctx ) . Errorf ( errorGettingDomainAccIDFmt , err )
return "" , nil , err
}
return domainAccountID , cancel , nil
}
func ( am * DefaultAccountManager ) handlePrivateAccountWithIDFromClaim ( ctx context . Context , claims jwtclaims . AuthorizationClaims ) ( string , error ) {
userAccountID , err := am . Store . GetAccountIDByUserID ( claims . UserId )
if err != nil {
log . WithContext ( ctx ) . Errorf ( "error getting account ID by user ID: %v" , err )
return "" , err
}
if userAccountID != claims . AccountId {
return "" , fmt . Errorf ( "user %s is not part of the account id %s" , claims . UserId , claims . AccountId )
}
2024-12-11 12:58:45 +01:00
accountDomain , domainCategory , err := am . Store . GetAccountDomainAndCategory ( ctx , store . LockingStrengthShare , claims . AccountId )
2024-10-12 08:35:51 +02:00
if handleNotFound ( err ) != nil {
log . WithContext ( ctx ) . Errorf ( "error getting account domain and category: %v" , err )
return "" , err
}
if domainIsUpToDate ( accountDomain , domainCategory , claims ) {
return claims . AccountId , nil
}
2022-03-01 15:22:18 +01:00
// We checked if the domain has a primary account already
2024-12-11 12:58:45 +01:00
domainAccountID , err := am . Store . GetAccountIDByPrivateDomain ( ctx , store . LockingStrengthShare , claims . Domain )
2024-10-12 08:35:51 +02:00
if handleNotFound ( err ) != nil {
log . WithContext ( ctx ) . Errorf ( errorGettingDomainAccIDFmt , err )
return "" , err
}
err = am . handleExistingUserAccount ( ctx , claims . AccountId , domainAccountID , claims )
2022-11-11 20:36:45 +01:00
if err != nil {
2024-10-12 08:35:51 +02:00
return "" , err
2022-03-01 15:22:18 +01:00
}
2024-10-12 08:35:51 +02:00
return claims . AccountId , nil
}
2024-09-27 16:10:50 +02:00
2024-10-12 08:35:51 +02:00
func handleNotFound ( err error ) error {
if err == nil {
return nil
}
2024-09-27 16:10:50 +02:00
2024-10-12 08:35:51 +02:00
e , ok := status . FromError ( err )
if ! ok || e . Type ( ) != status . NotFound {
return err
2022-03-01 15:22:18 +01:00
}
2024-10-12 08:35:51 +02:00
return nil
}
func domainIsUpToDate ( domain string , domainCategory string , claims jwtclaims . AuthorizationClaims ) bool {
2024-12-11 12:58:45 +01:00
return domainCategory == types . PrivateCategory || claims . DomainCategory != types . PrivateCategory || domain != claims . Domain
2022-03-01 15:22:18 +01:00
}
2024-12-11 12:58:45 +01:00
func ( am * DefaultAccountManager ) SyncAndMarkPeer ( ctx context . Context , accountID string , peerPubKey string , meta nbpeer . PeerSystemMeta , realIP net . IP ) ( * nbpeer . Peer , * types . NetworkMap , [ ] * posture . Checks , error ) {
2024-07-31 14:53:32 +02:00
accountUnlock := am . Store . AcquireReadLockByUID ( ctx , accountID )
defer accountUnlock ( )
2024-08-01 16:21:43 +02:00
peerUnlock := am . Store . AcquireWriteLockByUID ( ctx , peerPubKey )
defer peerUnlock ( )
2024-05-07 14:30:03 +02:00
2024-07-03 11:33:02 +02:00
account , err := am . Store . GetAccount ( ctx , accountID )
2024-05-07 14:30:03 +02:00
if err != nil {
2024-11-12 14:19:22 +01:00
return nil , nil , nil , status . NewGetAccountError ( err )
2024-05-07 14:30:03 +02:00
}
2024-07-03 11:33:02 +02:00
peer , netMap , postureChecks , err := am . SyncPeer ( ctx , PeerSync { WireGuardPubKey : peerPubKey , Meta : meta } , account )
2024-05-07 14:30:03 +02:00
if err != nil {
2024-11-12 14:19:22 +01:00
return nil , nil , nil , fmt . Errorf ( "error syncing peer: %w" , err )
2024-05-07 14:30:03 +02:00
}
2024-07-03 11:33:02 +02:00
err = am . MarkPeerConnected ( ctx , peerPubKey , true , realIP , account )
2024-05-07 14:30:03 +02:00
if err != nil {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Warnf ( "failed marking peer as connected %s %v" , peerPubKey , err )
2024-05-07 14:30:03 +02:00
}
2024-06-22 16:41:16 +02:00
return peer , netMap , postureChecks , nil
2024-05-07 14:30:03 +02:00
}
2024-08-01 16:21:43 +02:00
func ( am * DefaultAccountManager ) OnPeerDisconnected ( ctx context . Context , accountID string , peerPubKey string ) error {
2024-07-31 14:53:32 +02:00
accountUnlock := am . Store . AcquireReadLockByUID ( ctx , accountID )
defer accountUnlock ( )
2024-08-01 16:21:43 +02:00
peerUnlock := am . Store . AcquireWriteLockByUID ( ctx , peerPubKey )
defer peerUnlock ( )
2024-05-07 14:30:03 +02:00
2024-07-03 11:33:02 +02:00
account , err := am . Store . GetAccount ( ctx , accountID )
2024-05-07 14:30:03 +02:00
if err != nil {
2024-11-12 14:19:22 +01:00
return status . NewGetAccountError ( err )
2024-05-07 14:30:03 +02:00
}
2024-07-31 21:51:45 +02:00
err = am . MarkPeerConnected ( ctx , peerPubKey , false , nil , account )
2024-05-07 14:30:03 +02:00
if err != nil {
2024-11-15 16:59:03 +01:00
log . WithContext ( ctx ) . Warnf ( "failed marking peer as disconnected %s %v" , peerPubKey , err )
2024-05-07 14:30:03 +02:00
}
return nil
}
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) SyncPeerMeta ( ctx context . Context , peerPubKey string , meta nbpeer . PeerSystemMeta ) error {
accountID , err := am . Store . GetAccountIDByPeerPubKey ( ctx , peerPubKey )
2024-06-13 13:24:24 +02:00
if err != nil {
return err
}
2024-07-31 14:53:32 +02:00
unlock := am . Store . AcquireReadLockByUID ( ctx , accountID )
2024-06-13 13:24:24 +02:00
defer unlock ( )
2024-11-15 16:59:03 +01:00
unlockPeer := am . Store . AcquireWriteLockByUID ( ctx , peerPubKey )
defer unlockPeer ( )
2024-07-03 11:33:02 +02:00
account , err := am . Store . GetAccount ( ctx , accountID )
2024-06-13 13:24:24 +02:00
if err != nil {
return err
}
2024-07-03 11:33:02 +02:00
_ , _ , _ , err = am . SyncPeer ( ctx , PeerSync { WireGuardPubKey : peerPubKey , Meta : meta , UpdateAccountPeers : true } , account )
2024-06-13 13:24:24 +02:00
if err != nil {
2024-07-03 11:33:02 +02:00
return mapError ( ctx , err )
2024-06-13 13:24:24 +02:00
}
return nil
}
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.
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) CheckUserAccessByJWTGroups ( ctx context . Context , claims jwtclaims . AuthorizationClaims ) error {
2024-09-27 16:10:50 +02:00
accountID , _ , err := am . GetAccountIDFromToken ( ctx , claims )
if err != nil {
return err
}
2024-12-11 12:58:45 +01:00
settings , err := am . Store . GetAccountSettings ( ctx , store . LockingStrengthShare , accountID )
2023-12-13 11:18:35 +01:00
if err != nil {
return err
}
// Ensures JWT group synchronization to the management is enabled before,
// filtering access based on the allowed groups.
2024-09-27 16:10:50 +02:00
if settings != nil && settings . JWTGroupsEnabled {
if allowedGroups := settings . JWTAllowGroups ; len ( allowedGroups ) > 0 {
userJWTGroups := extractJWTGroups ( ctx , settings . JWTGroupsClaimName , claims )
2023-12-13 11:18:35 +01:00
if ! userHasAllowedGroup ( allowedGroups , userJWTGroups ) {
return fmt . Errorf ( "user does not belong to any of the allowed JWT groups" )
}
}
}
return nil
}
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) onPeersInvalidated ( ctx context . Context , accountID string ) {
log . WithContext ( ctx ) . Debugf ( "validated peers has been invalidated for account %s" , accountID )
2024-11-15 18:09:32 +01:00
am . updateAccountPeers ( ctx , accountID )
2024-03-27 18:48:48 +01:00
}
2024-05-30 15:22:42 +02:00
func ( am * DefaultAccountManager ) FindExistingPostureCheck ( accountID string , checks * posture . ChecksDefinition ) ( * posture . Checks , error ) {
return am . Store . GetPostureCheckByChecksDefinition ( accountID , checks )
}
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) GetAccountIDForPeerKey ( ctx context . Context , peerKey string ) ( string , error ) {
return am . Store . GetAccountIDByPeerPubKey ( ctx , peerKey )
}
2024-12-11 12:58:45 +01:00
func ( am * DefaultAccountManager ) handleUserPeer ( ctx context . Context , peer * nbpeer . Peer , settings * types . Settings ) ( bool , error ) {
user , err := am . Store . GetUserByUserID ( ctx , store . LockingStrengthShare , peer . UserID )
2024-08-19 12:50:11 +02:00
if err != nil {
return false , err
}
err = checkIfPeerOwnerIsBlocked ( peer , user )
if err != nil {
return false , err
}
if peerLoginExpired ( ctx , peer , settings ) {
err = am . handleExpiredPeer ( ctx , user , peer )
if err != nil {
return false , err
}
return true , nil
}
return false , nil
}
2024-12-11 12:58:45 +01:00
func ( am * DefaultAccountManager ) getFreeDNSLabel ( ctx context . Context , s store . Store , accountID string , peerHostName string ) ( string , error ) {
existingLabels , err := s . GetPeerLabelsInAccount ( ctx , store . LockingStrengthShare , accountID )
2024-09-16 15:47:03 +02:00
if err != nil {
return "" , fmt . Errorf ( "failed to get peer dns labels: %w" , err )
}
labelMap := ConvertSliceToMap ( existingLabels )
2024-12-11 12:58:45 +01:00
newLabel , err := types . GetPeerHostLabel ( peerHostName , labelMap )
2024-09-16 15:47:03 +02:00
if err != nil {
return "" , fmt . Errorf ( "failed to get new host label: %w" , err )
}
if newLabel == "" {
return "" , fmt . Errorf ( "failed to get new host label: %w" , err )
}
return newLabel , nil
}
2024-12-11 12:58:45 +01:00
func ( am * DefaultAccountManager ) GetAccountSettings ( ctx context . Context , accountID string , userID string ) ( * types . Settings , error ) {
user , err := am . Store . GetUserByUserID ( ctx , store . LockingStrengthShare , userID )
2024-09-27 16:10:50 +02:00
if err != nil {
return nil , err
}
if user . AccountID != accountID || ( ! user . HasAdminPower ( ) && ! user . IsServiceUser ) {
return nil , status . Errorf ( status . PermissionDenied , "the user has no permission to access account data" )
}
2024-12-11 12:58:45 +01:00
return am . Store . GetAccountSettings ( ctx , store . LockingStrengthShare , accountID )
}
func ( am * DefaultAccountManager ) GetNetworksManager ( ) networks . Manager {
return am . networksManager
2024-09-27 16:10:50 +02:00
}
2024-02-22 12:27:08 +01:00
// addAllGroup to account object if it doesn't exist
2024-12-11 12:58:45 +01:00
func addAllGroup ( account * types . Account ) error {
2022-05-21 15:21:39 +02:00
if len ( account . Groups ) == 0 {
2024-03-27 18:48:48 +01:00
allGroup := & nbgroup . Group {
2023-06-27 16:51:05 +02:00
ID : xid . New ( ) . String ( ) ,
Name : "All" ,
2024-03-27 18:48:48 +01:00
Issued : nbgroup . 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
}
2024-03-27 18:48:48 +01:00
account . Groups = map [ string ] * nbgroup . Group { allGroup . ID : allGroup }
2022-05-21 15:21:39 +02:00
2024-02-19 17:17:36 +01:00
id := xid . New ( ) . String ( )
2024-12-11 12:58:45 +01:00
defaultPolicy := & types . Policy {
2024-02-19 17:17:36 +01:00
ID : id ,
2024-12-11 12:58:45 +01:00
Name : types . DefaultRuleName ,
Description : types . DefaultRuleDescription ,
2024-02-19 17:17:36 +01:00
Enabled : true ,
2024-12-11 12:58:45 +01:00
Rules : [ ] * types . PolicyRule {
2024-02-19 17:17:36 +01:00
{
ID : id ,
2024-12-11 12:58:45 +01:00
Name : types . DefaultRuleName ,
Description : types . DefaultRuleDescription ,
2024-02-19 17:17:36 +01:00
Enabled : true ,
Sources : [ ] string { allGroup . ID } ,
Destinations : [ ] string { allGroup . ID } ,
Bidirectional : true ,
2024-12-11 12:58:45 +01:00
Protocol : types . PolicyRuleProtocolALL ,
Action : types . PolicyTrafficActionAccept ,
2024-02-19 17:17:36 +01:00
} ,
} ,
2022-05-21 15:21:39 +02:00
}
2023-03-13 15:14:18 +01:00
2024-12-11 12:58:45 +01:00
account . Policies = [ ] * types . 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
2024-12-11 12:58:45 +01:00
func newAccountWithId ( ctx context . Context , accountID , userID , domain string ) * types . Account {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Debugf ( "creating new account" )
2021-08-01 19:06:01 +02:00
2024-12-11 12:58:45 +01:00
network := types . NewNetwork ( )
2023-11-28 13:45:26 +01:00
peers := make ( map [ string ] * nbpeer . Peer )
2024-12-11 12:58:45 +01:00
users := make ( map [ string ] * types . User )
2024-05-06 14:47:49 +02:00
routes := make ( map [ route . ID ] * route . Route )
2024-12-11 12:58:45 +01:00
setupKeys := map [ string ] * types . SetupKey { }
2022-09-30 13:47:11 +02:00
nameServersGroups := make ( map [ string ] * nbdns . NameServerGroup )
2024-10-04 16:17:01 +02:00
2024-12-11 12:58:45 +01:00
owner := types . NewOwnerUser ( userID )
2024-10-04 16:17:01 +02:00
owner . AccountID = accountID
users [ userID ] = owner
2024-12-11 12:58:45 +01:00
dnsSettings := types . DNSSettings {
2023-01-17 17:34:40 +01:00
DisabledManagementGroups : make ( [ ] string , 0 ) ,
}
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Debugf ( "created new account %s" , accountID )
2021-08-01 19:06:01 +02:00
2024-12-11 12:58:45 +01:00
acc := & types . 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 ,
2024-12-11 12:58:45 +01:00
Settings : & types . Settings {
2023-02-13 15:07:15 +01:00
PeerLoginExpirationEnabled : true ,
2024-12-11 12:58:45 +01:00
PeerLoginExpiration : types . DefaultPeerLoginExpiration ,
2024-01-15 19:26:27 +01:00
GroupsPropagationEnabled : true ,
2024-03-27 16:11:45 +01:00
RegularUsersViewBlocked : true ,
2024-10-13 14:52:43 +02:00
PeerInactivityExpirationEnabled : false ,
2024-12-11 12:58:45 +01:00
PeerInactivityExpiration : types . DefaultPeerInactivityExpiration ,
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 {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Errorf ( "error adding all group to account %s: %v" , acc . Id , err )
2023-03-13 15:14:18 +01:00
}
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
2024-09-27 16:10:50 +02:00
// extractJWTGroups extracts the group names from a JWT token's claims.
func extractJWTGroups ( ctx context . Context , claimName string , claims jwtclaims . AuthorizationClaims ) [ ] string {
userJWTGroups := make ( [ ] string , 0 )
if claim , ok := claims . Raw [ claimName ] ; ok {
if claimGroups , ok := claim . ( [ ] interface { } ) ; ok {
for _ , g := range claimGroups {
if group , ok := g . ( string ) ; ok {
userJWTGroups = append ( userJWTGroups , group )
} else {
log . WithContext ( ctx ) . Debugf ( "JWT claim %q contains a non-string group (type: %T): %v" , claimName , g , g )
}
}
}
} else {
log . WithContext ( ctx ) . Debugf ( "JWT claim %q is not a string array" , claimName )
}
return userJWTGroups
}
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
}
2024-06-13 08:55:09 +02:00
// separateGroups separates user's auto groups into non-JWT and JWT groups.
// Returns the list of standard auto groups and a map of JWT auto groups,
// where the keys are the group names and the values are the group IDs.
2024-10-04 16:17:01 +02:00
func separateGroups ( autoGroups [ ] string , allGroups [ ] * nbgroup . Group ) ( [ ] string , map [ string ] string ) {
2024-06-13 08:55:09 +02:00
newAutoGroups := make ( [ ] string , 0 )
jwtAutoGroups := make ( map [ string ] string ) // map of group name to group ID
2024-10-04 16:17:01 +02:00
allGroupsMap := make ( map [ string ] * nbgroup . Group , len ( allGroups ) )
for _ , group := range allGroups {
allGroupsMap [ group . ID ] = group
}
2024-06-13 08:55:09 +02:00
for _ , id := range autoGroups {
2024-10-04 16:17:01 +02:00
if group , ok := allGroupsMap [ id ] ; ok {
2024-06-13 08:55:09 +02:00
if group . Issued == nbgroup . GroupIssuedJWT {
jwtAutoGroups [ group . Name ] = id
} else {
newAutoGroups = append ( newAutoGroups , id )
}
}
}
2024-10-04 16:17:01 +02:00
2024-06-13 08:55:09 +02:00
return newAutoGroups , jwtAutoGroups
}