2021-07-30 17:46:38 +02:00
package server
import (
2022-06-05 21:36:42 +02:00
"context"
2023-03-16 15:57:44 +01:00
"crypto/sha256"
2023-03-29 15:21:53 +02:00
b64 "encoding/base64"
2022-05-09 14:30:20 +02:00
"fmt"
2023-03-16 15:57:44 +01:00
"hash/crc32"
2023-02-03 21:47:20 +01:00
"math/rand"
"net"
"net/netip"
"reflect"
"regexp"
"strings"
"sync"
"time"
2023-03-16 15:57:44 +01:00
"codeberg.org/ac/base62"
2022-10-13 18:26:31 +02:00
"github.com/eko/gocache/v3/cache"
cacheStore "github.com/eko/gocache/v3/store"
2023-03-16 15:57:44 +01:00
gocache "github.com/patrickmn/go-cache"
"github.com/rs/xid"
log "github.com/sirupsen/logrus"
2022-09-30 13:47:11 +02:00
nbdns "github.com/netbirdio/netbird/dns"
2023-01-02 15:11:32 +01:00
"github.com/netbirdio/netbird/management/server/activity"
2022-03-26 12:08:54 +01:00
"github.com/netbirdio/netbird/management/server/idp"
"github.com/netbirdio/netbird/management/server/jwtclaims"
2022-11-11 20:36:45 +01:00
"github.com/netbirdio/netbird/management/server/status"
2022-08-18 18:22:15 +02:00
"github.com/netbirdio/netbird/route"
2021-07-30 17:46:38 +02:00
)
2022-03-01 15:22:18 +01:00
const (
2023-02-13 12:21:02 +01:00
PublicCategory = "public"
PrivateCategory = "private"
UnknownCategory = "unknown"
CacheExpirationMax = 7 * 24 * 3600 * time . Second // 7 days
CacheExpirationMin = 3 * 24 * 3600 * time . Second // 3 days
DefaultPeerLoginExpiration = 24 * time . Hour
2022-03-01 15:22:18 +01:00
)
2022-10-13 18:26:31 +02:00
func cacheEntryExpiration ( ) time . Duration {
r := rand . Intn ( int ( CacheExpirationMax . Milliseconds ( ) - CacheExpirationMin . Milliseconds ( ) ) ) + int ( CacheExpirationMin . Milliseconds ( ) )
return time . Duration ( r ) * time . Millisecond
}
2022-02-22 11:28:19 +01:00
type AccountManager interface {
GetOrCreateAccountByUser ( userId , domain string ) ( * Account , error )
2022-12-05 13:09:59 +01:00
CreateSetupKey ( accountID string , keyName string , keyType SetupKeyType , expiresIn time . Duration ,
2023-01-02 15:11:32 +01:00
autoGroups [ ] string , usageLimit int , userID string ) ( * SetupKey , error )
SaveSetupKey ( accountID string , key * SetupKey , userID string ) ( * SetupKey , error )
2023-04-22 12:57:51 +02:00
CreateUser ( accountID , executingUserID string , key * UserInfo ) ( * UserInfo , error )
DeleteUser ( accountID , executingUserID string , targetUserID string ) error
2022-11-05 10:24:50 +01:00
ListSetupKeys ( accountID , userID string ) ( [ ] * SetupKey , error )
2023-01-02 15:11:32 +01:00
SaveUser ( accountID , userID string , update * User ) ( * UserInfo , error )
2022-11-05 10:24:50 +01:00
GetSetupKey ( accountID , userID , keyID string ) ( * SetupKey , error )
2022-11-07 17:52:23 +01:00
GetAccountByUserOrAccountID ( userID , accountID , domain string ) ( * Account , error )
2023-04-22 12:57:51 +02:00
GetAccountByUserID ( userID string ) ( * Account , error )
2022-11-11 20:36:45 +01:00
GetAccountFromToken ( claims jwtclaims . AuthorizationClaims ) ( * Account , * User , error )
2023-03-29 15:21:53 +02:00
GetAccountFromPAT ( pat string ) ( * Account , * User , * PersonalAccessToken , error )
2023-03-30 10:54:09 +02:00
MarkPATUsed ( tokenID string ) error
2023-04-22 12:57:51 +02:00
IsUserAdmin ( userID string ) ( bool , error )
2022-02-22 11:28:19 +01:00
AccountExists ( accountId string ) ( * bool , error )
2023-02-03 10:33:28 +01:00
GetPeerByKey ( peerKey string ) ( * Peer , error )
2022-11-05 10:24:50 +01:00
GetPeers ( accountID , userID string ) ( [ ] * Peer , error )
2022-02-22 11:28:19 +01:00
MarkPeerConnected ( peerKey string , connected bool ) error
2023-02-03 10:33:28 +01:00
DeletePeer ( accountID , peerID , userID string ) ( * Peer , error )
2022-02-22 11:28:19 +01:00
GetPeerByIP ( accountId string , peerIP string ) ( * Peer , error )
2023-01-25 16:29:59 +01:00
UpdatePeer ( accountID , userID string , peer * Peer ) ( * Peer , error )
2023-02-03 10:33:28 +01:00
GetNetworkMap ( peerID string ) ( * NetworkMap , error )
GetPeerNetwork ( peerID string ) ( * Network , error )
2023-03-03 18:35:38 +01:00
AddPeer ( setupKey , userID string , peer * Peer ) ( * Peer , * NetworkMap , error )
2023-03-30 15:23:24 +02:00
CreatePAT ( accountID string , executingUserID string , targetUserID string , tokenName string , expiresIn int ) ( * PersonalAccessTokenGenerated , error )
DeletePAT ( accountID string , executingUserID string , targetUserID string , tokenID string ) error
GetPAT ( accountID string , executingUserID string , targetUserID string , tokenID string ) ( * PersonalAccessToken , error )
GetAllPATs ( accountID string , executingUserID string , targetUserID string ) ( [ ] * PersonalAccessToken , error )
2023-02-03 10:33:28 +01:00
UpdatePeerSSHKey ( peerID string , sshKey string ) error
2022-11-05 10:24:50 +01:00
GetUsersFromAccount ( accountID , userID string ) ( [ ] * UserInfo , error )
2022-05-03 16:02:51 +02:00
GetGroup ( accountId , groupID string ) ( * Group , error )
2023-01-02 15:11:32 +01:00
SaveGroup ( accountID , userID string , group * Group ) error
2022-06-14 10:32:54 +02:00
UpdateGroup ( accountID string , groupID string , operations [ ] GroupUpdateOperation ) ( * Group , error )
2022-05-03 16:02:51 +02:00
DeleteGroup ( accountId , groupID string ) error
ListGroups ( accountId string ) ( [ ] * Group , error )
2023-02-03 10:33:28 +01:00
GroupAddPeer ( accountId , groupID , peerID string ) error
2022-05-03 16:02:51 +02:00
GroupDeletePeer ( accountId , groupID , peerKey string ) error
GroupListPeers ( accountId , groupID string ) ( [ ] * Peer , error )
2023-03-13 15:14:18 +01:00
GetPolicy ( accountID , policyID , userID string ) ( * Policy , error )
SavePolicy ( accountID , userID string , policy * Policy ) error
DeletePolicy ( accountID , policyID , userID string ) error
ListPolicies ( accountID , userID string ) ( [ ] * Policy , error )
2022-11-05 10:24:50 +01:00
GetRoute ( accountID , routeID , userID string ) ( * route . Route , error )
2023-02-03 10:33:28 +01:00
CreateRoute ( accountID string , prefix , peerID , description , netID string , masquerade bool , metric int , groups [ ] string , enabled bool , userID string ) ( * route . Route , error )
2023-01-25 16:29:59 +01:00
SaveRoute ( accountID , userID string , route * route . Route ) error
UpdateRoute ( accountID , routeID string , operations [ ] RouteUpdateOperation ) ( * route . Route , error )
DeleteRoute ( accountID , routeID , userID string ) error
2022-11-05 10:24:50 +01:00
ListRoutes ( accountID , userID string ) ( [ ] * route . Route , error )
2022-09-30 13:47:11 +02:00
GetNameServerGroup ( accountID , nsGroupID string ) ( * nbdns . NameServerGroup , error )
2023-01-25 16:29:59 +01:00
CreateNameServerGroup ( accountID string , name , description string , nameServerList [ ] nbdns . NameServer , groups [ ] string , primary bool , domains [ ] string , enabled bool , userID string ) ( * nbdns . NameServerGroup , error )
SaveNameServerGroup ( accountID , userID string , nsGroupToSave * nbdns . NameServerGroup ) error
UpdateNameServerGroup ( accountID , nsGroupID , userID string , operations [ ] NameServerGroupUpdateOperation ) ( * nbdns . NameServerGroup , error )
DeleteNameServerGroup ( accountID , nsGroupID , userID string ) error
2022-09-30 13:47:11 +02:00
ListNameServerGroups ( accountID string ) ( [ ] * nbdns . NameServerGroup , error )
2022-11-21 11:14:42 +01:00
GetDNSDomain ( ) string
2023-01-02 15:11:32 +01:00
GetEvents ( accountID , userID string ) ( [ ] * activity . Event , error )
2023-01-17 17:34:40 +01:00
GetDNSSettings ( accountID string , userID string ) ( * DNSSettings , error )
SaveDNSSettings ( accountID string , userID string , dnsSettingsToSave * DNSSettings ) error
2023-02-07 20:11:08 +01:00
GetPeer ( accountID , peerID , userID string ) ( * Peer , error )
2023-02-16 12:00:41 +01:00
UpdateAccountSettings ( accountID , userID string , newSettings * Settings ) ( * Account , error )
2023-03-13 15:14:18 +01:00
LoginPeer ( login PeerLogin ) ( * Peer , * NetworkMap , error ) // used by peer gRPC API
SyncPeer ( sync PeerSync ) ( * Peer , * NetworkMap , error ) // used by peer gRPC API
2022-02-22 11:28:19 +01:00
}
type DefaultAccountManager struct {
2021-07-30 17:46:38 +02:00
Store Store
2022-10-13 18:26:31 +02:00
// cacheMux and cacheLoading helps to make sure that only a single cache reload runs at a time per accountID
cacheMux sync . Mutex
// cacheLoading keeps the accountIDs that are currently reloading. The accountID has to be removed once cache has been reloaded
cacheLoading map [ string ] chan struct { }
2021-09-07 18:36:46 +02:00
peersUpdateManager * PeersUpdateManager
2022-01-24 11:21:30 +01:00
idpManager idp . Manager
2022-10-13 18:26:31 +02:00
cacheManager cache . CacheInterface [ [ ] * idp . UserData ]
2022-06-05 21:36:42 +02:00
ctx context . Context
2023-01-02 15:11:32 +01:00
eventStore activity . Store
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
2021-07-30 17:46:38 +02:00
}
2023-02-13 15:07:15 +01:00
// Settings represents Account settings structure that can be modified via API and Dashboard
type Settings struct {
// PeerLoginExpirationEnabled globally enables or disables peer login expiration
PeerLoginExpirationEnabled bool
// PeerLoginExpiration is a setting that indicates when peer login expires.
// Applies to all peers that have Peer.LoginExpirationEnabled set to true.
PeerLoginExpiration time . Duration
}
// Copy copies the Settings struct
func ( s * Settings ) Copy ( ) * Settings {
return & Settings {
PeerLoginExpirationEnabled : s . PeerLoginExpirationEnabled ,
PeerLoginExpiration : s . PeerLoginExpiration ,
}
}
2021-07-30 17:46:38 +02:00
// Account represents a unique account of the system
type Account struct {
2021-12-27 13:17:15 +01:00
Id string
// User.Id it was created by
2022-03-01 15:22:18 +01:00
CreatedBy string
Domain string
DomainCategory string
IsDomainPrimaryAccount bool
SetupKeys map [ string ] * SetupKey
Network * Network
Peers map [ string ] * Peer
Users map [ string ] * User
2022-05-03 16:02:51 +02:00
Groups map [ string ] * Group
2022-05-21 15:21:39 +02:00
Rules map [ string ] * Rule
2023-03-13 15:14:18 +01:00
Policies [ ] * Policy
2022-08-18 18:22:15 +02:00
Routes map [ string ] * route . Route
2022-09-30 13:47:11 +02:00
NameServerGroups map [ string ] * nbdns . NameServerGroup
2023-01-17 17:34:40 +01:00
DNSSettings * DNSSettings
2023-02-13 15:07:15 +01:00
// Settings is a dictionary of Account settings
Settings * Settings
2021-12-27 13:17:15 +01:00
}
2022-05-05 08:58:34 +02:00
type UserInfo struct {
2023-04-22 12:57:51 +02:00
ID string ` json:"id" `
Email string ` json:"email" `
Name string ` json:"name" `
Role string ` json:"role" `
AutoGroups [ ] string ` json:"auto_groups" `
Status string ` json:"-" `
IsServiceUser bool ` json:"is_service_user" `
2022-05-05 08:58:34 +02:00
}
2022-12-06 10:11:57 +01:00
// getRoutesToSync returns the enabled routes for the peer ID and the routes
2023-02-03 10:33:28 +01:00
// from the ACL peers that have distribution groups associated with the peer ID.
// Please mind, that the returned route.Route objects will contain Peer.Key instead of Peer.ID.
2022-12-06 10:11:57 +01:00
func ( a * Account ) getRoutesToSync ( peerID string , aclPeers [ ] * Peer ) [ ] * route . Route {
2022-12-08 15:15:50 +01:00
routes , peerDisabledRoutes := a . getEnabledAndDisabledRoutesByPeer ( peerID )
peerRoutesMembership := make ( lookupMap )
for _ , r := range append ( routes , peerDisabledRoutes ... ) {
peerRoutesMembership [ route . GetHAUniqueID ( r ) ] = struct { } { }
}
2022-12-06 10:11:57 +01:00
groupListMap := a . getPeerGroups ( peerID )
for _ , peer := range aclPeers {
2023-02-03 10:33:28 +01:00
activeRoutes , _ := a . getEnabledAndDisabledRoutesByPeer ( peer . ID )
2022-12-08 15:15:50 +01:00
groupFilteredRoutes := a . filterRoutesByGroups ( activeRoutes , groupListMap )
filteredRoutes := a . filterRoutesFromPeersOfSameHAGroup ( groupFilteredRoutes , peerRoutesMembership )
2022-12-06 10:11:57 +01:00
routes = append ( routes , filteredRoutes ... )
}
return routes
}
2022-12-08 15:15:50 +01:00
// filterRoutesByHAMembership filters and returns a list of routes that don't share the same HA route membership
func ( a * Account ) filterRoutesFromPeersOfSameHAGroup ( routes [ ] * route . Route , peerMemberships lookupMap ) [ ] * route . Route {
var filteredRoutes [ ] * route . Route
for _ , r := range routes {
_ , found := peerMemberships [ route . GetHAUniqueID ( r ) ]
if ! found {
filteredRoutes = append ( filteredRoutes , r )
}
}
return filteredRoutes
}
2022-12-06 10:11:57 +01:00
// filterRoutesByGroups returns a list with routes that have distribution groups in the group's map
func ( a * Account ) filterRoutesByGroups ( routes [ ] * route . Route , groupListMap lookupMap ) [ ] * route . Route {
var filteredRoutes [ ] * route . Route
for _ , r := range routes {
for _ , groupID := range r . Groups {
_ , found := groupListMap [ groupID ]
if found {
filteredRoutes = append ( filteredRoutes , r )
break
2022-11-07 12:10:56 +01:00
}
}
}
2022-12-06 10:11:57 +01:00
return filteredRoutes
2022-11-07 12:10:56 +01:00
}
2023-02-03 10:33:28 +01:00
// getEnabledAndDisabledRoutesByPeer returns the enabled and disabled lists of routes that belong to a peer.
// Please mind, that the returned route.Route objects will contain Peer.Key instead of Peer.ID.
func ( a * Account ) getEnabledAndDisabledRoutesByPeer ( peerID string ) ( [ ] * route . Route , [ ] * route . Route ) {
2022-12-08 15:15:50 +01:00
var enabledRoutes [ ] * route . Route
var disabledRoutes [ ] * route . Route
2022-11-07 12:10:56 +01:00
for _ , r := range a . Routes {
2023-02-03 10:33:28 +01:00
if r . Peer == peerID {
// We need to set Peer.Key instead of Peer.ID because this object will be sent to agents as part of a network map.
// Ideally we should have a separate field for that, but fine for now.
peer := a . GetPeer ( peerID )
if peer == nil {
log . Errorf ( "route %s has peer %s that doesn't exist under account %s" , r . ID , peerID , a . Id )
continue
}
raut := r . Copy ( )
raut . Peer = peer . Key
2022-12-08 15:15:50 +01:00
if r . Enabled {
2023-02-03 10:33:28 +01:00
enabledRoutes = append ( enabledRoutes , raut )
2022-12-08 15:15:50 +01:00
continue
}
2023-02-03 10:33:28 +01:00
disabledRoutes = append ( disabledRoutes , raut )
2022-11-07 12:10:56 +01:00
}
}
2022-12-08 15:15:50 +01:00
return enabledRoutes , disabledRoutes
2022-11-07 12:10:56 +01:00
}
// GetRoutesByPrefix return list of routes by account and route prefix
func ( a * Account ) GetRoutesByPrefix ( prefix netip . Prefix ) [ ] * route . Route {
var routes [ ] * route . Route
for _ , r := range a . Routes {
if r . Network . String ( ) == prefix . String ( ) {
routes = append ( routes , r )
}
}
return routes
}
2023-01-25 16:29:59 +01:00
// GetPeerByIP returns peer by it's IP if exists under account or nil otherwise
func ( a * Account ) GetPeerByIP ( peerIP string ) * Peer {
for _ , peer := range a . Peers {
if peerIP == peer . IP . String ( ) {
return peer
}
}
return nil
}
2023-01-02 15:11:32 +01:00
// GetGroup returns a group by ID if exists, nil otherwise
func ( a * Account ) GetGroup ( groupID string ) * Group {
return a . Groups [ groupID ]
}
2023-03-03 18:35:38 +01:00
// GetPeerNetworkMap returns a group by ID if exists, nil otherwise
func ( a * Account ) GetPeerNetworkMap ( peerID , dnsDomain string ) * NetworkMap {
2023-04-12 09:39:17 +02:00
aclPeers := a . getPeersByACL ( peerID )
2023-03-03 18:35:38 +01:00
// exclude expired peers
var peersToConnect [ ] * Peer
2023-03-07 10:17:25 +01:00
var expiredPeers [ ] * Peer
2023-03-03 18:35:38 +01:00
for _ , p := range aclPeers {
expired , _ := p . LoginExpired ( a . Settings . PeerLoginExpiration )
2023-03-09 11:24:42 +01:00
if a . Settings . PeerLoginExpirationEnabled && expired {
2023-03-07 10:17:25 +01:00
expiredPeers = append ( expiredPeers , p )
2023-03-03 18:35:38 +01:00
continue
}
peersToConnect = append ( peersToConnect , p )
}
// Please mind, that the returned route.Route objects will contain Peer.Key instead of Peer.ID.
routesUpdate := a . getRoutesToSync ( peerID , peersToConnect )
dnsManagementStatus := a . getPeerDNSManagementStatus ( peerID )
dnsUpdate := nbdns . Config {
ServiceEnable : dnsManagementStatus ,
}
if dnsManagementStatus {
var zones [ ] nbdns . CustomZone
peersCustomZone := getPeersCustomZone ( a , dnsDomain )
if peersCustomZone . Domain != "" {
zones = append ( zones , peersCustomZone )
}
dnsUpdate . CustomZones = zones
dnsUpdate . NameServerGroups = getPeerNSGroups ( a , peerID )
}
return & NetworkMap {
2023-03-07 10:17:25 +01:00
Peers : peersToConnect ,
Network : a . Network . Copy ( ) ,
Routes : routesUpdate ,
DNSConfig : dnsUpdate ,
OfflinePeers : expiredPeers ,
2023-03-03 18:35:38 +01:00
}
}
2023-02-27 16:44:26 +01:00
// GetExpiredPeers returns peers that have been expired
func ( a * Account ) GetExpiredPeers ( ) [ ] * Peer {
var peers [ ] * Peer
for _ , peer := range a . GetPeersWithExpiration ( ) {
expired , _ := peer . LoginExpired ( a . Settings . PeerLoginExpiration )
if expired {
peers = append ( peers , peer )
}
}
return peers
}
// GetNextPeerExpiration returns the minimum duration in which the next peer of the account will expire if it was found.
// If there is no peer that expires this function returns false and a duration of 0.
// This function only considers peers that haven't been expired yet and that are connected.
func ( a * Account ) GetNextPeerExpiration ( ) ( time . Duration , bool ) {
peersWithExpiry := a . GetPeersWithExpiration ( )
if len ( peersWithExpiry ) == 0 {
return 0 , false
}
var nextExpiry * time . Duration
for _ , peer := range peersWithExpiry {
// consider only connected peers because others will require login on connecting to the management server
if peer . Status . LoginExpired || ! peer . Status . Connected {
continue
}
_ , duration := peer . LoginExpired ( a . Settings . PeerLoginExpiration )
if nextExpiry == nil || duration < * nextExpiry {
nextExpiry = & duration
}
}
if nextExpiry == nil {
return 0 , false
}
return * nextExpiry , true
}
2023-03-23 17:47:53 +01:00
// GetPeersWithExpiration returns a list of peers that have Peer.LoginExpirationEnabled set to true and that were added by a user
2023-02-27 16:44:26 +01:00
func ( a * Account ) GetPeersWithExpiration ( ) [ ] * Peer {
peers := make ( [ ] * Peer , 0 )
for _ , peer := range a . Peers {
2023-03-23 17:47:53 +01:00
if peer . LoginExpirationEnabled && peer . AddedWithSSOLogin ( ) {
2023-02-27 16:44:26 +01:00
peers = append ( peers , peer )
}
}
return peers
}
2022-11-07 12:10:56 +01:00
// GetPeers returns a list of all Account peers
func ( a * Account ) GetPeers ( ) [ ] * Peer {
var peers [ ] * Peer
for _ , peer := range a . Peers {
peers = append ( peers , peer )
}
return peers
}
2023-02-16 12:00:41 +01:00
// UpdateSettings saves new account settings
func ( a * Account ) UpdateSettings ( update * Settings ) * Account {
a . Settings = update . Copy ( )
return a
}
2022-11-07 12:10:56 +01:00
// UpdatePeer saves new or replaces existing peer
func ( a * Account ) UpdatePeer ( update * Peer ) {
2023-02-03 10:33:28 +01:00
a . Peers [ update . ID ] = update
2022-11-07 12:10:56 +01:00
}
// DeletePeer deletes peer from the account cleaning up all the references
2023-02-03 10:33:28 +01:00
func ( a * Account ) DeletePeer ( peerID string ) {
2022-11-07 12:10:56 +01:00
// delete peer from groups
for _ , g := range a . Groups {
for i , pk := range g . Peers {
2023-02-03 10:33:28 +01:00
if pk == peerID {
2022-11-07 12:10:56 +01:00
g . Peers = append ( g . Peers [ : i ] , g . Peers [ i + 1 : ] ... )
break
}
}
}
2022-11-25 18:11:07 +01:00
for _ , r := range a . Routes {
2023-02-03 10:33:28 +01:00
if r . Peer == peerID {
2022-11-25 18:11:07 +01:00
r . Enabled = false
r . Peer = ""
}
}
2023-02-03 10:33:28 +01:00
delete ( a . Peers , peerID )
2022-11-07 12:10:56 +01:00
a . Network . IncSerial ( )
}
// FindPeerByPubKey looks for a Peer by provided WireGuard public key in the Account or returns error if it wasn't found.
// It will return an object copy of the peer.
func ( a * Account ) FindPeerByPubKey ( peerPubKey string ) ( * Peer , error ) {
for _ , peer := range a . Peers {
if peer . Key == peerPubKey {
return peer . Copy ( ) , nil
}
}
2022-11-11 20:36:45 +01:00
return nil , status . Errorf ( status . NotFound , "peer with the public key %s not found" , peerPubKey )
2022-11-07 12:10:56 +01:00
}
2023-02-07 20:11:08 +01:00
// FindUserPeers returns a list of peers that user owns (created)
func ( a * Account ) FindUserPeers ( userID string ) ( [ ] * Peer , error ) {
peers := make ( [ ] * Peer , 0 )
for _ , peer := range a . Peers {
if peer . UserID == userID {
peers = append ( peers , peer )
}
}
return peers , nil
}
2022-11-05 10:24:50 +01:00
// FindUser looks for a given user in the Account or returns error if user wasn't found.
func ( a * Account ) FindUser ( userID string ) ( * User , error ) {
user := a . Users [ userID ]
if user == nil {
2022-11-11 20:36:45 +01:00
return nil , status . Errorf ( status . NotFound , "user %s not found" , userID )
2022-11-05 10:24:50 +01:00
}
return user , nil
}
2022-11-08 16:14:36 +01:00
// FindSetupKey looks for a given SetupKey in the Account or returns error if it wasn't found.
func ( a * Account ) FindSetupKey ( setupKey string ) ( * SetupKey , error ) {
key := a . SetupKeys [ setupKey ]
if key == nil {
2022-11-11 20:36:45 +01:00
return nil , status . Errorf ( status . NotFound , "setup key not found" )
2022-11-08 16:14:36 +01:00
}
return key , nil
}
func ( a * Account ) getUserGroups ( userID string ) ( [ ] string , error ) {
user , err := a . FindUser ( userID )
if err != nil {
return nil , err
}
return user . AutoGroups , nil
}
2023-01-17 17:34:40 +01:00
func ( a * Account ) getPeerDNSManagementStatus ( peerID string ) bool {
peerGroups := a . getPeerGroups ( peerID )
enabled := true
if a . DNSSettings != nil {
for _ , groupID := range a . DNSSettings . DisabledManagementGroups {
_ , found := peerGroups [ groupID ]
if found {
enabled = false
break
}
}
}
return enabled
}
2022-12-06 10:11:57 +01:00
func ( a * Account ) getPeerGroups ( peerID string ) lookupMap {
groupList := make ( lookupMap )
for groupID , group := range a . Groups {
for _ , id := range group . Peers {
if id == peerID {
groupList [ groupID ] = struct { } { }
break
}
}
}
return groupList
}
2022-11-08 16:14:36 +01:00
func ( a * Account ) getSetupKeyGroups ( setupKey string ) ( [ ] string , error ) {
key , err := a . FindSetupKey ( setupKey )
if err != nil {
return nil , err
}
return key . AutoGroups , nil
}
func ( a * Account ) getTakenIPs ( ) [ ] net . IP {
var takenIps [ ] net . IP
for _ , existingPeer := range a . Peers {
takenIps = append ( takenIps , existingPeer . IP )
}
return takenIps
}
2022-11-07 15:38:21 +01:00
func ( a * Account ) getPeerDNSLabels ( ) lookupMap {
existingLabels := make ( lookupMap )
for _ , peer := range a . Peers {
if peer . DNSLabel != "" {
existingLabels [ peer . DNSLabel ] = struct { } { }
}
}
return existingLabels
}
2021-12-27 13:17:15 +01:00
func ( a * Account ) Copy ( ) * Account {
peers := map [ string ] * Peer { }
for id , peer := range a . Peers {
peers [ id ] = peer . Copy ( )
}
users := map [ string ] * User { }
for id , user := range a . Users {
users [ id ] = user . Copy ( )
}
setupKeys := map [ string ] * SetupKey { }
for id , key := range a . SetupKeys {
setupKeys [ id ] = key . Copy ( )
}
2022-05-21 15:21:39 +02:00
groups := map [ string ] * Group { }
for id , group := range a . Groups {
groups [ id ] = group . Copy ( )
}
rules := map [ string ] * Rule { }
for id , rule := range a . Rules {
rules [ id ] = rule . Copy ( )
}
2023-03-13 15:14:18 +01:00
policies := [ ] * Policy { }
for _ , policy := range a . Policies {
policies = append ( policies , policy . Copy ( ) )
}
2022-09-30 13:47:11 +02:00
routes := map [ string ] * route . Route { }
for id , route := range a . Routes {
routes [ id ] = route . Copy ( )
}
nsGroups := map [ string ] * nbdns . NameServerGroup { }
for id , nsGroup := range a . NameServerGroups {
nsGroups [ id ] = nsGroup . Copy ( )
}
2023-01-17 17:34:40 +01:00
var dnsSettings * DNSSettings
if a . DNSSettings != nil {
2023-02-13 15:07:15 +01:00
dnsSettings = a . DNSSettings . Copy ( )
}
var settings * Settings
if a . Settings != nil {
settings = a . Settings . Copy ( )
2023-01-17 17:34:40 +01:00
}
2021-12-27 13:17:15 +01:00
return & Account {
2022-11-07 12:10:56 +01:00
Id : a . Id ,
CreatedBy : a . CreatedBy ,
Domain : a . Domain ,
DomainCategory : a . DomainCategory ,
IsDomainPrimaryAccount : a . IsDomainPrimaryAccount ,
SetupKeys : setupKeys ,
Network : a . Network . Copy ( ) ,
Peers : peers ,
Users : users ,
Groups : groups ,
Rules : rules ,
2023-03-13 15:14:18 +01:00
Policies : policies ,
2022-11-07 12:10:56 +01:00
Routes : routes ,
NameServerGroups : nsGroups ,
2023-01-17 17:34:40 +01:00
DNSSettings : dnsSettings ,
2023-02-13 15:07:15 +01:00
Settings : settings ,
2021-12-27 13:17:15 +01:00
}
2021-07-30 17:46:38 +02:00
}
2022-05-21 15:21:39 +02:00
func ( a * Account ) GetGroupAll ( ) ( * Group , error ) {
for _ , g := range a . Groups {
if g . Name == "All" {
return g , nil
}
}
return nil , fmt . Errorf ( "no group ALL found" )
}
2023-02-03 10:33:28 +01:00
// GetPeer looks up a Peer by ID
func ( a * Account ) GetPeer ( peerID string ) * Peer {
return a . Peers [ peerID ]
}
2022-05-21 15:21:39 +02:00
// BuildManager creates a new DefaultAccountManager with a provided Store
2022-10-19 17:43:28 +02:00
func BuildManager ( store Store , peersUpdateManager * PeersUpdateManager , idpManager idp . Manager ,
2023-02-03 21:47:20 +01:00
singleAccountModeDomain string , dnsDomain string , eventStore activity . Store ,
) ( * DefaultAccountManager , error ) {
2022-06-05 21:36:42 +02:00
am := & DefaultAccountManager {
2021-09-07 18:36:46 +02:00
Store : store ,
peersUpdateManager : peersUpdateManager ,
2022-01-24 11:21:30 +01:00
idpManager : idpManager ,
2022-06-05 21:36:42 +02:00
ctx : context . Background ( ) ,
2022-10-13 18:26:31 +02:00
cacheMux : sync . Mutex { } ,
cacheLoading : map [ string ] chan struct { } { } ,
2022-11-07 15:38:21 +01:00
dnsDomain : dnsDomain ,
2023-01-02 15:11:32 +01:00
eventStore : eventStore ,
2023-02-27 16:44:26 +01:00
peerLoginExpiry : NewDefaultScheduler ( ) ,
2021-07-30 17:46:38 +02:00
}
2022-10-19 17:43:28 +02:00
allAccounts := store . GetAllAccounts ( )
// enable single account mode only if configured by user and number of existing accounts is not grater than 1
am . singleAccountMode = singleAccountModeDomain != "" && len ( allAccounts ) <= 1
if am . singleAccountMode {
2022-12-13 13:43:29 +01:00
if ! isDomainValid ( singleAccountModeDomain ) {
2023-02-27 16:44:26 +01:00
return nil , status . Errorf ( status . InvalidArgument , "invalid domain \"%s\" provided for a single account mode. Please review your input for --single-account-mode-domain" , singleAccountModeDomain )
2022-12-13 13:43:29 +01:00
}
2022-10-19 17:43:28 +02:00
am . singleAccountModeDomain = singleAccountModeDomain
log . Infof ( "single account mode enabled, accounts number %d" , len ( allAccounts ) )
} else {
log . Infof ( "single account mode disabled, accounts number %d" , len ( allAccounts ) )
}
2022-05-21 15:21:39 +02:00
2022-10-19 17:43:28 +02:00
// if account doesn't have a default group
2022-06-06 13:45:59 +02:00
// we create 'all' group and add all peers into it
// also we create default rule with source as destination
2022-10-19 17:43:28 +02:00
for _ , account := range allAccounts {
2022-11-07 15:38:21 +01:00
shouldSave := false
2022-06-06 13:45:59 +02:00
_ , err := account . GetGroupAll ( )
if err != nil {
2023-03-13 15:14:18 +01:00
if err := addAllGroup ( account ) ; err != nil {
return nil , err
}
2022-11-07 15:38:21 +01:00
shouldSave = true
}
if shouldSave {
err = store . SaveAccount ( account )
if err != nil {
2022-06-06 13:45:59 +02:00
return nil , err
}
2022-05-21 15:21:39 +02:00
}
}
2022-10-19 17:43:28 +02:00
goCacheClient := gocache . New ( CacheExpirationMax , 30 * time . Minute )
goCacheStore := cacheStore . NewGoCache ( goCacheClient )
2022-06-05 21:36:42 +02:00
2022-10-19 17:43:28 +02:00
am . cacheManager = cache . NewLoadable [ [ ] * idp . UserData ] ( am . loadAccount , cache . New [ [ ] * idp . UserData ] ( goCacheStore ) )
2022-06-06 12:05:44 +02:00
if ! isNil ( am . idpManager ) {
go func ( ) {
err := am . warmupIDPCache ( )
if err != nil {
log . Warnf ( "failed warming up cache due to error: %v" , err )
2023-02-03 21:47:20 +01:00
// todo retry?
2022-06-06 12:05:44 +02:00
return
}
} ( )
}
2022-06-05 21:36:42 +02:00
return am , nil
2021-07-30 17:46:38 +02:00
}
2023-02-16 12:00:41 +01:00
// UpdateAccountSettings updates Account settings.
// Only users with role UserRoleAdmin can update the account.
// User that performs the update has to belong to the account.
// Returns an updated Account
func ( am * DefaultAccountManager ) UpdateAccountSettings ( accountID , userID string , newSettings * Settings ) ( * Account , error ) {
halfYearLimit := 180 * 24 * time . Hour
if newSettings . PeerLoginExpiration > halfYearLimit {
return nil , status . Errorf ( status . InvalidArgument , "peer login expiration can't be larger than 180 days" )
}
if newSettings . PeerLoginExpiration < time . Hour {
return nil , status . Errorf ( status . InvalidArgument , "peer login expiration can't be smaller than one hour" )
}
unlock := am . Store . AcquireAccountLock ( accountID )
defer unlock ( )
account , err := am . Store . GetAccountByUser ( userID )
if err != nil {
return nil , err
}
user , err := account . FindUser ( userID )
if err != nil {
return nil , err
}
if ! user . IsAdmin ( ) {
return nil , status . Errorf ( status . PermissionDenied , "user is not allowed to update account" )
}
oldSettings := account . Settings
if oldSettings . PeerLoginExpirationEnabled != newSettings . PeerLoginExpirationEnabled {
event := activity . AccountPeerLoginExpirationEnabled
if ! newSettings . PeerLoginExpirationEnabled {
event = activity . AccountPeerLoginExpirationDisabled
2023-02-27 16:44:26 +01:00
am . peerLoginExpiry . Cancel ( [ ] string { accountID } )
} else {
am . checkAndSchedulePeerLoginExpiration ( account )
2023-02-16 12:00:41 +01:00
}
am . storeEvent ( userID , accountID , accountID , event , nil )
}
if oldSettings . PeerLoginExpiration != newSettings . PeerLoginExpiration {
am . storeEvent ( userID , accountID , accountID , activity . AccountPeerLoginExpirationDurationUpdated , nil )
2023-02-27 16:44:26 +01:00
am . checkAndSchedulePeerLoginExpiration ( account )
2023-02-16 12:00:41 +01:00
}
updatedAccount := account . UpdateSettings ( newSettings )
err = am . Store . SaveAccount ( account )
if err != nil {
return nil , err
}
return updatedAccount , nil
}
2023-02-27 16:44:26 +01:00
func ( am * DefaultAccountManager ) peerLoginExpirationJob ( accountID string ) func ( ) ( time . Duration , bool ) {
return func ( ) ( time . Duration , bool ) {
unlock := am . Store . AcquireAccountLock ( accountID )
defer unlock ( )
account , err := am . Store . GetAccount ( accountID )
if err != nil {
log . Errorf ( "failed getting account %s expiring peers" , account . Id )
return account . GetNextPeerExpiration ( )
}
var peerIDs [ ] string
for _ , peer := range account . GetExpiredPeers ( ) {
if peer . Status . LoginExpired {
continue
}
peerIDs = append ( peerIDs , peer . ID )
peer . MarkLoginExpired ( true )
account . UpdatePeer ( peer )
err = am . Store . SavePeerStatus ( account . Id , peer . ID , * peer . Status )
if err != nil {
log . Errorf ( "failed saving peer status while expiring peer %s" , peer . ID )
return account . GetNextPeerExpiration ( )
}
}
log . Debugf ( "discovered %d peers to expire for account %s" , len ( peerIDs ) , account . Id )
if len ( peerIDs ) != 0 {
// this will trigger peer disconnect from the management service
am . peersUpdateManager . CloseChannels ( peerIDs )
2023-03-10 17:39:29 +01:00
err = am . updateAccountPeers ( account )
2023-02-27 16:44:26 +01:00
if err != nil {
log . Errorf ( "failed updating account peers while expiring peers for account %s" , accountID )
return account . GetNextPeerExpiration ( )
}
}
return account . GetNextPeerExpiration ( )
}
}
func ( am * DefaultAccountManager ) checkAndSchedulePeerLoginExpiration ( account * Account ) {
am . peerLoginExpiry . Cancel ( [ ] string { account . Id } )
if nextRun , ok := account . GetNextPeerExpiration ( ) ; ok {
go am . peerLoginExpiry . Schedule ( nextRun , account . Id , am . peerLoginExpirationJob ( account . Id ) )
}
}
2022-06-20 18:20:43 +02:00
// newAccount creates a new Account with a generated ID and generated default setup keys.
// If ID is already in use (due to collision) we try one more time before returning error
func ( am * DefaultAccountManager ) newAccount ( userID , domain string ) ( * Account , error ) {
for i := 0 ; i < 2 ; i ++ {
accountId := xid . New ( ) . String ( )
_ , err := am . Store . GetAccount ( accountId )
statusErr , _ := status . FromError ( err )
if err == nil {
log . Warnf ( "an account with ID already exists, retrying..." )
continue
2022-11-11 20:36:45 +01:00
} else if statusErr . Type ( ) == status . NotFound {
2023-01-02 15:11:32 +01:00
newAccount := newAccountWithId ( accountId , userID , domain )
2023-01-24 10:17:24 +01:00
am . storeEvent ( userID , newAccount . Id , accountId , activity . AccountCreated , nil )
2023-01-02 15:11:32 +01:00
return newAccount , nil
2022-06-20 18:20:43 +02:00
} else {
return nil , err
}
}
2022-11-11 20:36:45 +01:00
return nil , status . Errorf ( status . Internal , "error while creating new account" )
2022-06-20 18:20:43 +02:00
}
2022-06-06 12:05:44 +02:00
func ( am * DefaultAccountManager ) warmupIDPCache ( ) error {
userData , err := am . idpManager . GetAllAccounts ( )
if err != nil {
return err
}
for accountID , users := range userData {
2022-10-13 18:26:31 +02:00
err = am . cacheManager . Set ( am . ctx , accountID , users , cacheStore . WithExpiration ( cacheEntryExpiration ( ) ) )
2022-06-06 12:05:44 +02:00
if err != nil {
return err
}
}
log . Infof ( "warmed up IDP cache with %d entries" , len ( userData ) )
return nil
}
2022-11-07 17:52:23 +01:00
// GetAccountByUserOrAccountID looks for an account by user or accountID, if no account is provided and
// userID doesn't have an account associated with it, one account is created
func ( am * DefaultAccountManager ) GetAccountByUserOrAccountID ( userID , accountID , domain string ) ( * Account , error ) {
if accountID != "" {
return am . Store . GetAccount ( accountID )
} else if userID != "" {
account , err := am . GetOrCreateAccountByUser ( userID , domain )
2022-01-24 11:21:30 +01:00
if err != nil {
2022-11-11 20:36:45 +01:00
return nil , status . Errorf ( status . NotFound , "account not found using user id: %s" , userID )
2022-01-24 11:21:30 +01:00
}
2022-11-07 17:52:23 +01:00
err = am . addAccountIDToIDPAppMeta ( userID , account )
2022-03-01 15:22:18 +01:00
if err != nil {
return nil , err
2022-01-24 11:21:30 +01:00
}
return account , nil
}
2022-11-11 20:36:45 +01:00
return nil , status . Errorf ( status . NotFound , "no valid user or account Id provided" )
2022-01-24 11:21:30 +01:00
}
2022-05-05 08:58:34 +02:00
func isNil ( i idp . Manager ) bool {
return i == nil || reflect . ValueOf ( i ) . IsNil ( )
}
2022-10-13 18:26:31 +02:00
// addAccountIDToIDPAppMeta update user's app metadata in idp manager
func ( am * DefaultAccountManager ) addAccountIDToIDPAppMeta ( userID string , account * Account ) error {
2022-05-05 08:58:34 +02:00
if ! isNil ( am . idpManager ) {
2022-10-13 18:26:31 +02:00
// user can be nil if it wasn't found (e.g., just created)
user , err := am . lookupUserInCache ( userID , account )
if err != nil {
return err
}
if user != nil && user . AppMetadata . WTAccountID == account . Id {
// it was already set, so we skip the unnecessary update
log . Debugf ( "skipping IDP App Meta update because accountID %s has been already set for user %s" ,
account . Id , userID )
return nil
}
err = am . idpManager . UpdateUserAppMetadata ( userID , idp . AppMetadata { WTAccountID : account . Id } )
2022-03-01 15:22:18 +01:00
if err != nil {
2022-11-11 20:36:45 +01:00
return status . Errorf ( status . Internal , "updating user's app metadata failed with: %v" , err )
2022-03-01 15:22:18 +01:00
}
2022-10-13 18:26:31 +02:00
// refresh cache to reflect the update
_ , err = am . refreshCache ( account . Id )
if err != nil {
return err
}
2022-03-01 15:22:18 +01:00
}
return nil
}
2022-10-13 18:26:31 +02:00
func ( am * DefaultAccountManager ) loadAccount ( _ context . Context , accountID interface { } ) ( [ ] * idp . UserData , error ) {
log . Debugf ( "account %s not found in cache, reloading" , accountID )
2022-06-06 12:05:44 +02:00
return am . idpManager . GetAccount ( fmt . Sprintf ( "%v" , accountID ) )
2022-06-05 21:36:42 +02:00
}
2022-10-13 18:26:31 +02:00
func ( am * DefaultAccountManager ) lookupUserInCacheByEmail ( email string , accountID string ) ( * idp . UserData , error ) {
data , err := am . getAccountFromCache ( accountID , false )
2022-09-22 09:06:32 +02:00
if err != nil {
return nil , err
}
2022-10-13 18:26:31 +02:00
for _ , datum := range data {
if datum . Email == email {
2022-09-22 09:06:32 +02:00
return datum , nil
}
}
2022-10-13 18:26:31 +02:00
return nil , nil
2022-09-22 09:06:32 +02:00
}
2022-10-13 18:26:31 +02:00
// lookupUserInCache looks up user in the IdP cache and returns it. If the user wasn't found, the function returns nil
func ( am * DefaultAccountManager ) lookupUserInCache ( userID string , account * Account ) ( * idp . UserData , error ) {
users := make ( map [ string ] struct { } , len ( account . Users ) )
for _ , user := range account . Users {
users [ user . Id ] = struct { } { }
}
log . Debugf ( "looking up user %s of account %s in cache" , userID , account . Id )
userData , err := am . lookupCache ( users , account . Id )
2022-06-05 21:36:42 +02:00
if err != nil {
return nil , err
}
2022-10-13 18:26:31 +02:00
for _ , datum := range userData {
if datum . ID == userID {
return datum , nil
}
}
return nil , nil
}
func ( am * DefaultAccountManager ) refreshCache ( accountID string ) ( [ ] * idp . UserData , error ) {
return am . getAccountFromCache ( accountID , true )
}
// getAccountFromCache returns user data for a given account ensuring that cache load happens only once
func ( am * DefaultAccountManager ) getAccountFromCache ( accountID string , forceReload bool ) ( [ ] * idp . UserData , error ) {
am . cacheMux . Lock ( )
loadingChan := am . cacheLoading [ accountID ]
if loadingChan == nil {
loadingChan = make ( chan struct { } )
am . cacheLoading [ accountID ] = loadingChan
am . cacheMux . Unlock ( )
defer func ( ) {
am . cacheMux . Lock ( )
delete ( am . cacheLoading , accountID )
close ( loadingChan )
am . cacheMux . Unlock ( )
} ( )
if forceReload {
err := am . cacheManager . Delete ( am . ctx , accountID )
if err != nil {
return nil , err
}
}
return am . cacheManager . Get ( am . ctx , accountID )
}
am . cacheMux . Unlock ( )
log . Debugf ( "one request to get account %s is already running" , accountID )
select {
case <- loadingChan :
// channel has been closed meaning cache was loaded => simply return from cache
return am . cacheManager . Get ( am . ctx , accountID )
case <- time . After ( 5 * time . Second ) :
return nil , fmt . Errorf ( "timeout while waiting for account %s cache to reload" , accountID )
}
}
func ( am * DefaultAccountManager ) lookupCache ( accountUsers map [ string ] struct { } , accountID string ) ( [ ] * idp . UserData , error ) {
data , err := am . getAccountFromCache ( accountID , false )
if err != nil {
return nil , err
}
2022-06-05 21:36:42 +02:00
userDataMap := make ( map [ string ] struct { } )
2022-10-13 18:26:31 +02:00
for _ , datum := range data {
2022-06-05 21:36:42 +02:00
userDataMap [ datum . ID ] = struct { } { }
}
// check whether we need to reload the cache
// the accountUsers ID list is the source of truth and all the users should be in the cache
2022-10-13 18:26:31 +02:00
reload := len ( accountUsers ) != len ( data )
2022-06-05 21:36:42 +02:00
for user := range accountUsers {
if _ , ok := userDataMap [ user ] ; ! ok {
reload = true
}
}
if reload {
// reload cache once avoiding loops
2022-10-13 18:26:31 +02:00
data , err = am . refreshCache ( accountID )
2022-06-05 21:36:42 +02:00
if err != nil {
return nil , err
}
}
2022-10-13 18:26:31 +02:00
return data , err
2022-06-05 21:36:42 +02:00
}
2022-03-01 15:22:18 +01:00
// updateAccountDomainAttributes updates the account domain attributes and then, saves the account
2022-11-11 20:36:45 +01:00
func ( am * DefaultAccountManager ) updateAccountDomainAttributes ( account * Account , claims jwtclaims . AuthorizationClaims ,
2023-02-03 21:47:20 +01:00
primaryDomain bool ,
) error {
2022-03-01 15:22:18 +01:00
account . IsDomainPrimaryAccount = primaryDomain
2022-06-20 18:20:43 +02:00
lowerDomain := strings . ToLower ( claims . Domain )
userObj := account . Users [ claims . UserId ]
if account . Domain != lowerDomain && userObj . Role == UserRoleAdmin {
account . Domain = lowerDomain
}
// prevent updating category for different domain until admin logs in
if account . Domain == lowerDomain {
account . DomainCategory = claims . DomainCategory
}
2022-03-01 15:22:18 +01:00
err := am . Store . SaveAccount ( account )
if err != nil {
2022-11-11 20:36:45 +01:00
return err
2022-03-01 15:22:18 +01:00
}
return nil
}
// handleExistingUserAccount handles existing User accounts and update its domain attributes.
//
// If there is no primary domain account yet, we set the account as primary for the domain. Otherwise,
// we compare the account's ID with the domain account ID, and if they don't match, we set the account as
// non-primary account for the domain. We don't merge accounts at this stage, because of cases when a domain
// was previously unclassified or classified as public so N users that logged int that time, has they own account
// and peers that shouldn't be lost.
2022-05-03 16:02:51 +02:00
func ( am * DefaultAccountManager ) handleExistingUserAccount (
existingAcc * Account ,
domainAcc * Account ,
claims jwtclaims . AuthorizationClaims ,
) error {
2022-03-01 15:22:18 +01:00
var err error
2022-03-10 13:47:36 +01:00
if domainAcc != nil && existingAcc . Id != domainAcc . Id {
2022-03-01 15:22:18 +01:00
err = am . updateAccountDomainAttributes ( existingAcc , claims , false )
if err != nil {
return err
}
2022-03-10 13:47:36 +01:00
} else {
err = am . updateAccountDomainAttributes ( existingAcc , claims , true )
if err != nil {
return err
}
2022-03-01 15:22:18 +01:00
}
// we should register the account ID to this user's metadata in our IDP manager
2022-10-13 18:26:31 +02:00
err = am . addAccountIDToIDPAppMeta ( claims . UserId , existingAcc )
2022-03-01 15:22:18 +01:00
if err != nil {
return err
}
return nil
}
// handleNewUserAccount validates if there is an existing primary account for the domain, if so it adds the new user to that account,
// otherwise it will create a new account and make it primary account for the domain.
2022-11-11 20:36:45 +01:00
func ( am * DefaultAccountManager ) handleNewUserAccount ( domainAcc * Account , claims jwtclaims . AuthorizationClaims ) ( * Account , error ) {
2023-02-03 21:47:20 +01:00
if claims . UserId == "" {
return nil , fmt . Errorf ( "user ID is empty" )
}
2022-03-01 15:22:18 +01:00
var (
2022-03-10 13:47:36 +01:00
account * Account
err error
2022-03-01 15:22:18 +01:00
)
lowerDomain := strings . ToLower ( claims . Domain )
// if domain already has a primary account, add regular user
if domainAcc != nil {
account = domainAcc
account . Users [ claims . UserId ] = NewRegularUser ( claims . UserId )
2022-05-09 14:30:20 +02:00
err = am . Store . SaveAccount ( account )
if err != nil {
2022-11-11 20:36:45 +01:00
return nil , err
2022-05-09 14:30:20 +02:00
}
2022-03-01 15:22:18 +01:00
} else {
2022-06-20 18:20:43 +02:00
account , err = am . newAccount ( claims . UserId , lowerDomain )
if err != nil {
return nil , err
}
2022-03-10 13:47:36 +01:00
err = am . updateAccountDomainAttributes ( account , claims , true )
if err != nil {
return nil , err
}
2022-03-01 15:22:18 +01:00
}
2022-10-13 18:26:31 +02:00
err = am . addAccountIDToIDPAppMeta ( claims . UserId , account )
if err != nil {
return nil , err
}
2023-01-24 10:17:24 +01:00
am . storeEvent ( claims . UserId , claims . UserId , account . Id , activity . UserJoined , nil )
2023-01-02 15:11:32 +01:00
2022-10-13 18:26:31 +02:00
return account , nil
}
// redeemInvite checks whether user has been invited and redeems the invite
func ( am * DefaultAccountManager ) redeemInvite ( account * Account , userID string ) error {
// only possible with the enabled IdP manager
if am . idpManager == nil {
log . Warnf ( "invites only work with enabled IdP manager" )
return nil
}
user , err := am . lookupUserInCache ( userID , account )
if err != nil {
return err
}
if user == nil {
2022-11-11 20:36:45 +01:00
return status . Errorf ( status . NotFound , "user %s not found in the IdP" , userID )
2022-10-13 18:26:31 +02:00
}
2022-10-19 17:51:41 +02:00
if user . AppMetadata . WTPendingInvite != nil && * user . AppMetadata . WTPendingInvite {
2022-10-13 18:26:31 +02:00
log . Infof ( "redeeming invite for user %s account %s" , userID , account . Id )
// User has already logged in, meaning that IdP should have set wt_pending_invite to false.
// Our job is to just reload cache.
go func ( ) {
_ , err = am . refreshCache ( account . Id )
if err != nil {
log . Warnf ( "failed reloading cache when redeeming user %s under account %s" , userID , account . Id )
return
}
log . Debugf ( "user %s of account %s redeemed invite" , user . ID , account . Id )
2023-01-24 10:17:24 +01:00
am . storeEvent ( userID , userID , account . Id , activity . UserJoined , nil )
2022-10-13 18:26:31 +02:00
} ( )
}
return nil
}
2023-03-30 17:32:44 +02:00
// MarkPATUsed marks a personal access token as used
2023-03-30 10:54:09 +02:00
func ( am * DefaultAccountManager ) MarkPATUsed ( tokenID string ) error {
unlock := am . Store . AcquireGlobalLock ( )
user , err := am . Store . GetUserByTokenID ( tokenID )
if err != nil {
return err
}
account , err := am . Store . GetAccountByUser ( user . Id )
if err != nil {
return err
}
2023-03-31 12:03:53 +02:00
unlock ( )
unlock = am . Store . AcquireAccountLock ( account . Id )
defer unlock ( )
account , err = am . Store . GetAccountByUser ( user . Id )
if err != nil {
return err
}
2023-03-30 10:54:09 +02:00
pat , ok := account . Users [ user . Id ] . PATs [ tokenID ]
if ! ok {
return fmt . Errorf ( "token not found" )
}
2023-04-03 15:09:35 +02:00
pat . LastUsed = time . Now ( ) . UTC ( )
2023-03-30 10:54:09 +02:00
2023-03-30 16:22:15 +02:00
return am . Store . SaveAccount ( account )
2023-03-30 10:54:09 +02:00
}
2023-03-16 15:57:44 +01:00
// GetAccountFromPAT returns Account and User associated with a personal access token
2023-03-29 15:21:53 +02:00
func ( am * DefaultAccountManager ) GetAccountFromPAT ( token string ) ( * Account , * User , * PersonalAccessToken , error ) {
2023-03-16 15:57:44 +01:00
if len ( token ) != PATLength {
2023-03-29 15:21:53 +02:00
return nil , nil , nil , fmt . Errorf ( "token has wrong length" )
2023-03-16 15:57:44 +01:00
}
prefix := token [ : len ( PATPrefix ) ]
if prefix != PATPrefix {
2023-03-29 15:21:53 +02:00
return nil , nil , nil , fmt . Errorf ( "token has wrong prefix" )
2023-03-16 15:57:44 +01:00
}
2023-03-20 11:44:12 +01:00
secret := token [ len ( PATPrefix ) : len ( PATPrefix ) + PATSecretLength ]
encodedChecksum := token [ len ( PATPrefix ) + PATSecretLength : len ( PATPrefix ) + PATSecretLength + PATChecksumLength ]
2023-03-16 15:57:44 +01:00
verificationChecksum , err := base62 . Decode ( encodedChecksum )
if err != nil {
2023-03-29 15:21:53 +02:00
return nil , nil , nil , fmt . Errorf ( "token checksum decoding failed: %w" , err )
2023-03-16 15:57:44 +01:00
}
secretChecksum := crc32 . ChecksumIEEE ( [ ] byte ( secret ) )
if secretChecksum != verificationChecksum {
2023-03-29 15:21:53 +02:00
return nil , nil , nil , fmt . Errorf ( "token checksum does not match" )
2023-03-16 15:57:44 +01:00
}
hashedToken := sha256 . Sum256 ( [ ] byte ( token ) )
2023-03-29 15:21:53 +02:00
encodedHashedToken := b64 . StdEncoding . EncodeToString ( hashedToken [ : ] )
tokenID , err := am . Store . GetTokenIDByHashedToken ( encodedHashedToken )
2023-03-16 15:57:44 +01:00
if err != nil {
2023-03-29 15:21:53 +02:00
return nil , nil , nil , err
2023-03-16 15:57:44 +01:00
}
user , err := am . Store . GetUserByTokenID ( tokenID )
if err != nil {
2023-03-29 15:21:53 +02:00
return nil , nil , nil , err
2023-03-16 15:57:44 +01:00
}
account , err := am . Store . GetAccountByUser ( user . Id )
if err != nil {
2023-03-29 15:21:53 +02:00
return nil , nil , nil , err
}
pat := user . PATs [ tokenID ]
if pat == nil {
return nil , nil , nil , fmt . Errorf ( "personal access token not found" )
2023-03-16 15:57:44 +01:00
}
2023-03-29 15:21:53 +02:00
return account , user , pat , nil
2023-03-16 15:57:44 +01:00
}
2022-10-13 18:26:31 +02:00
// GetAccountFromToken returns an account associated with this token
2022-11-11 20:36:45 +01:00
func ( am * DefaultAccountManager ) GetAccountFromToken ( claims jwtclaims . AuthorizationClaims ) ( * Account , * User , error ) {
2023-02-03 21:47:20 +01:00
if claims . UserId == "" {
return nil , nil , fmt . Errorf ( "user ID is empty" )
}
2022-10-19 17:43:28 +02:00
if am . singleAccountMode && am . singleAccountModeDomain != "" {
// This section is mostly related to self-hosted installations.
// We override incoming domain claims to group users under a single account.
claims . Domain = am . singleAccountModeDomain
claims . DomainCategory = PrivateCategory
log . Infof ( "overriding JWT Domain and DomainCategory claims since single account mode is enabled" )
}
2022-10-13 18:26:31 +02:00
account , err := am . getAccountWithAuthorizationClaims ( claims )
if err != nil {
2022-11-11 20:36:45 +01:00
return nil , nil , err
}
user := account . Users [ claims . UserId ]
if user == nil {
// this is not really possible because we got an account by user ID
return nil , nil , status . Errorf ( status . NotFound , "user %s not found" , claims . UserId )
2022-10-13 18:26:31 +02:00
}
2023-04-22 12:57:51 +02:00
if ! user . IsServiceUser {
err = am . redeemInvite ( account , claims . UserId )
if err != nil {
return nil , nil , err
}
2022-03-01 15:22:18 +01:00
}
2022-11-11 20:36:45 +01:00
return account , user , nil
2022-03-01 15:22:18 +01:00
}
2022-10-13 18:26:31 +02:00
// getAccountWithAuthorizationClaims retrievs an account using JWT Claims.
2022-03-01 15:22:18 +01:00
// if domain is of the PrivateCategory category, it will evaluate
// if account is new, existing or if there is another account with the same domain
//
// Use cases:
//
// New user + New account + New domain -> create account, user role = admin (if private domain, index domain)
//
// New user + New account + Existing Private Domain -> add user to the existing account, user role = regular (not admin)
//
// New user + New account + Existing Public Domain -> create account, user role = admin
//
// Existing user + Existing account + Existing Domain -> Nothing changes (if private, index domain)
//
// Existing user + Existing account + Existing Indexed Domain -> Nothing changes
//
// Existing user + Existing account + Existing domain reclassified Domain as private -> Nothing changes (index domain)
2022-11-07 17:52:23 +01:00
func ( am * DefaultAccountManager ) getAccountWithAuthorizationClaims ( claims jwtclaims . AuthorizationClaims ) ( * Account , error ) {
2023-02-03 21:47:20 +01:00
if claims . UserId == "" {
return nil , fmt . Errorf ( "user ID is empty" )
}
2022-03-01 15:22:18 +01:00
// if Account ID is part of the claims
// it means that we've already classified the domain and user has an account
2022-09-29 10:51:18 +02:00
if claims . DomainCategory != PrivateCategory || ! isDomainValid ( claims . Domain ) {
2022-11-07 17:52:23 +01:00
return am . GetAccountByUserOrAccountID ( claims . UserId , claims . AccountId , claims . Domain )
2022-03-09 13:31:42 +01:00
} else if claims . AccountId != "" {
2022-11-07 17:52:23 +01:00
accountFromID , err := am . Store . GetAccount ( claims . AccountId )
2022-03-09 13:31:42 +01:00
if err != nil {
return nil , err
}
2022-05-09 14:30:20 +02:00
if _ , ok := accountFromID . Users [ claims . UserId ] ; ! ok {
return nil , fmt . Errorf ( "user %s is not part of the account id %s" , claims . UserId , claims . AccountId )
}
2022-03-09 13:31:42 +01:00
if accountFromID . DomainCategory == PrivateCategory || claims . DomainCategory != PrivateCategory {
return accountFromID , nil
}
2022-03-01 15:22:18 +01:00
}
2022-11-07 17:52:23 +01:00
unlock := am . Store . AcquireGlobalLock ( )
defer unlock ( )
2022-03-01 15:22:18 +01:00
// We checked if the domain has a primary account already
domainAccount , err := am . Store . GetAccountByPrivateDomain ( claims . Domain )
2022-11-11 20:36:45 +01:00
if err != nil {
// if NotFound we are good to continue, otherwise return error
e , ok := status . FromError ( err )
if ! ok || e . Type ( ) != status . NotFound {
return nil , err
}
2022-03-01 15:22:18 +01:00
}
2022-11-07 12:10:56 +01:00
account , err := am . Store . GetAccountByUser ( claims . UserId )
2022-03-01 15:22:18 +01:00
if err == nil {
err = am . handleExistingUserAccount ( account , domainAccount , claims )
if err != nil {
return nil , err
}
return account , nil
2022-11-11 20:36:45 +01:00
} else if s , ok := status . FromError ( err ) ; ok && s . Type ( ) == status . NotFound {
2022-03-01 15:22:18 +01:00
return am . handleNewUserAccount ( domainAccount , claims )
} else {
// other error
return nil , err
}
}
2022-09-29 10:51:18 +02:00
func isDomainValid ( domain string ) bool {
re := regexp . MustCompile ( ` ^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z] { 2,}$ ` )
return re . Match ( [ ] byte ( domain ) )
}
2022-05-03 16:02:51 +02:00
// AccountExists checks whether account exists (returns true) or not (returns false)
2022-11-07 17:52:23 +01:00
func ( am * DefaultAccountManager ) AccountExists ( accountID string ) ( * bool , error ) {
unlock := am . Store . AcquireAccountLock ( accountID )
defer unlock ( )
2021-08-12 12:49:10 +02:00
var res bool
2022-11-07 17:52:23 +01:00
_ , err := am . Store . GetAccount ( accountID )
2021-08-12 12:49:10 +02:00
if err != nil {
2022-11-11 20:36:45 +01:00
if s , ok := status . FromError ( err ) ; ok && s . Type ( ) == status . NotFound {
2021-08-12 12:49:10 +02:00
res = false
return & res , nil
} else {
return nil , err
}
}
res = true
return & res , nil
}
2022-11-21 11:14:42 +01:00
// GetDNSDomain returns the configured dnsDomain
func ( am * DefaultAccountManager ) GetDNSDomain ( ) string {
return am . dnsDomain
}
2022-06-06 13:45:59 +02:00
// addAllGroup to account object if it doesn't exists
2023-03-13 15:14:18 +01:00
func addAllGroup ( account * Account ) error {
2022-05-21 15:21:39 +02:00
if len ( account . Groups ) == 0 {
allGroup := & Group {
ID : xid . New ( ) . String ( ) ,
Name : "All" ,
}
for _ , peer := range account . Peers {
2023-02-03 10:33:28 +01:00
allGroup . Peers = append ( allGroup . Peers , peer . ID )
2022-05-21 15:21:39 +02:00
}
account . Groups = map [ string ] * Group { allGroup . ID : allGroup }
defaultRule := & Rule {
ID : xid . New ( ) . String ( ) ,
2022-06-14 10:32:54 +02:00
Name : DefaultRuleName ,
Description : DefaultRuleDescription ,
Disabled : false ,
2022-05-21 15:21:39 +02:00
Source : [ ] string { allGroup . ID } ,
Destination : [ ] string { allGroup . ID } ,
}
account . Rules = map [ string ] * Rule { defaultRule . ID : defaultRule }
2023-03-13 15:14:18 +01:00
// TODO: after migration we need to drop rule and create policy directly
defaultPolicy , err := RuleToPolicy ( defaultRule )
if err != nil {
return fmt . Errorf ( "convert rule to policy: %w" , err )
}
account . Policies = [ ] * Policy { defaultPolicy }
2022-05-21 15:21:39 +02:00
}
2023-03-13 15:14:18 +01:00
return nil
2022-05-21 15:21:39 +02:00
}
2021-08-12 12:49:10 +02:00
// newAccountWithId creates a new Account with a default SetupKey (doesn't store in a Store) and provided id
2022-02-11 17:18:18 +01:00
func newAccountWithId ( accountId , userId , domain string ) * Account {
2021-08-01 19:06:01 +02:00
log . Debugf ( "creating new account" )
setupKeys := make ( map [ string ] * SetupKey )
2021-09-07 18:36:46 +02:00
defaultKey := GenerateDefaultSetupKey ( )
2022-12-05 13:09:59 +01:00
oneOffKey := GenerateSetupKey ( "One-off key" , SetupKeyOneOff , DefaultSetupKeyDuration , [ ] string { } ,
SetupKeyUnlimitedUsage )
2021-09-07 18:36:46 +02:00
setupKeys [ defaultKey . Key ] = defaultKey
setupKeys [ oneOffKey . Key ] = oneOffKey
2022-01-14 14:34:27 +01:00
network := NewNetwork ( )
2021-08-01 19:06:01 +02:00
peers := make ( map [ string ] * Peer )
2021-12-27 13:17:15 +01:00
users := make ( map [ string ] * User )
2022-08-18 18:22:15 +02:00
routes := make ( map [ string ] * route . Route )
2022-09-30 13:47:11 +02:00
nameServersGroups := make ( map [ string ] * nbdns . NameServerGroup )
2022-06-09 13:14:34 +02:00
users [ userId ] = NewAdminUser ( userId )
2023-01-17 17:34:40 +01:00
dnsSettings := & DNSSettings {
DisabledManagementGroups : make ( [ ] string , 0 ) ,
}
2021-09-07 18:36:46 +02:00
log . Debugf ( "created new account %s with setup key %s" , accountId , defaultKey . Key )
2021-08-01 19:06:01 +02:00
2022-06-09 13:14:34 +02:00
acc := & Account {
2023-02-13 15:07:15 +01:00
Id : accountId ,
SetupKeys : setupKeys ,
Network : network ,
Peers : peers ,
Users : users ,
CreatedBy : userId ,
Domain : domain ,
Routes : routes ,
NameServerGroups : nameServersGroups ,
DNSSettings : dnsSettings ,
Settings : & Settings {
PeerLoginExpirationEnabled : true ,
PeerLoginExpiration : DefaultPeerLoginExpiration ,
} ,
2022-02-11 17:18:18 +01:00
}
2022-06-09 13:14:34 +02:00
2023-03-13 15:14:18 +01:00
if err := addAllGroup ( acc ) ; err != nil {
log . Errorf ( "error adding all group to account %s: %v" , acc . Id , err )
}
2022-06-09 13:14:34 +02:00
return acc
2021-08-12 12:49:10 +02:00
}
2021-08-20 22:33:43 +02:00
2022-06-14 10:32:54 +02:00
func removeFromList ( inputList [ ] string , toRemove [ ] string ) [ ] string {
toRemoveMap := make ( map [ string ] struct { } )
for _ , item := range toRemove {
toRemoveMap [ item ] = struct { } { }
}
var resultList [ ] string
for _ , item := range inputList {
_ , ok := toRemoveMap [ item ]
if ! ok {
resultList = append ( resultList , item )
}
}
return resultList
}