package server import ( "github.com/rs/xid" log "github.com/sirupsen/logrus" "github.com/wiretrustee/wiretrustee/management/server/idp" "github.com/wiretrustee/wiretrustee/util" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "sync" ) type AccountManager struct { Store Store // mutex to synchronise account operations (e.g. generating Peer IP address inside the Network) mux sync.Mutex peersUpdateManager *PeersUpdateManager idpManager idp.Manager } // Account represents a unique account of the system type Account struct { Id string // User.Id it was created by CreatedBy string SetupKeys map[string]*SetupKey Network *Network Peers map[string]*Peer Users map[string]*User } // NewAccount creates a new Account with a generated ID and generated default setup keys func NewAccount(userId string) *Account { accountId := xid.New().String() return newAccountWithId(accountId, userId) } 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() } return &Account{ Id: a.Id, CreatedBy: a.CreatedBy, SetupKeys: setupKeys, Network: a.Network.Copy(), Peers: peers, Users: users, } } // NewManager creates a new AccountManager with a provided Store func NewManager(store Store, peersUpdateManager *PeersUpdateManager, idpManager idp.Manager) *AccountManager { return &AccountManager{ Store: store, mux: sync.Mutex{}, peersUpdateManager: peersUpdateManager, idpManager: idpManager, } } //AddSetupKey generates a new setup key with a given name and type, and adds it to the specified account func (am *AccountManager) AddSetupKey(accountId string, keyName string, keyType SetupKeyType, expiresIn *util.Duration) (*SetupKey, error) { am.mux.Lock() defer am.mux.Unlock() keyDuration := DefaultSetupKeyDuration if expiresIn != nil { keyDuration = expiresIn.Duration } account, err := am.Store.GetAccount(accountId) if err != nil { return nil, status.Errorf(codes.NotFound, "account not found") } setupKey := GenerateSetupKey(keyName, keyType, keyDuration) account.SetupKeys[setupKey.Key] = setupKey err = am.Store.SaveAccount(account) if err != nil { return nil, status.Errorf(codes.Internal, "failed adding account key") } return setupKey, nil } //RevokeSetupKey marks SetupKey as revoked - becomes not valid anymore func (am *AccountManager) RevokeSetupKey(accountId string, keyId string) (*SetupKey, error) { am.mux.Lock() defer am.mux.Unlock() account, err := am.Store.GetAccount(accountId) if err != nil { return nil, status.Errorf(codes.NotFound, "account not found") } setupKey := getAccountSetupKeyById(account, keyId) if setupKey == nil { return nil, status.Errorf(codes.NotFound, "unknown setupKey %s", keyId) } keyCopy := setupKey.Copy() keyCopy.Revoked = true account.SetupKeys[keyCopy.Key] = keyCopy err = am.Store.SaveAccount(account) if err != nil { return nil, status.Errorf(codes.Internal, "failed adding account key") } return keyCopy, nil } //RenameSetupKey renames existing setup key of the specified account. func (am *AccountManager) RenameSetupKey(accountId string, keyId string, newName string) (*SetupKey, error) { am.mux.Lock() defer am.mux.Unlock() account, err := am.Store.GetAccount(accountId) if err != nil { return nil, status.Errorf(codes.NotFound, "account not found") } setupKey := getAccountSetupKeyById(account, keyId) if setupKey == nil { return nil, status.Errorf(codes.NotFound, "unknown setupKey %s", keyId) } keyCopy := setupKey.Copy() keyCopy.Name = newName account.SetupKeys[keyCopy.Key] = keyCopy err = am.Store.SaveAccount(account) if err != nil { return nil, status.Errorf(codes.Internal, "failed adding account key") } return keyCopy, nil } //GetAccount returns an existing account or error (NotFound) if doesn't exist func (am *AccountManager) GetAccount(accountId string) (*Account, error) { am.mux.Lock() defer am.mux.Unlock() account, err := am.Store.GetAccount(accountId) if err != nil { return nil, status.Errorf(codes.NotFound, "account not found") } return account, nil } //GetAccountByUserOrAccountId look for an account by user or account Id, if no account is provided and // user id doesn't have an account associated with it, one account is created func (am *AccountManager) GetAccountByUserOrAccountId(userId, accountId string) (*Account, error) { if accountId != "" { return am.GetAccount(accountId) } else if userId != "" { account, err := am.GetOrCreateAccountByUser(userId) if err != nil { return nil, status.Errorf(codes.NotFound, "account not found using user id: %s", userId) } // update idp manager app metadata if am.idpManager != nil { err = am.idpManager.UpdateUserAppMetadata(userId, idp.AppMetadata{WTAccountId: account.Id}) if err != nil { return nil, status.Errorf(codes.Internal, "updating user's app metadata failed with: %v", err) } } return account, nil } return nil, status.Errorf(codes.NotFound, "no valid user or account Id provided") } //AccountExists checks whether account exists (returns true) or not (returns false) func (am *AccountManager) AccountExists(accountId string) (*bool, error) { am.mux.Lock() defer am.mux.Unlock() var res bool _, err := am.Store.GetAccount(accountId) if err != nil { if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound { res = false return &res, nil } else { return nil, err } } res = true return &res, nil } // AddAccount generates a new Account with a provided accountId and userId, saves to the Store func (am *AccountManager) AddAccount(accountId string, userId string) (*Account, error) { am.mux.Lock() defer am.mux.Unlock() return am.createAccount(accountId, userId) } func (am *AccountManager) createAccount(accountId string, userId string) (*Account, error) { account := newAccountWithId(accountId, userId) err := am.Store.SaveAccount(account) if err != nil { return nil, status.Errorf(codes.Internal, "failed creating account") } return account, nil } // newAccountWithId creates a new Account with a default SetupKey (doesn't store in a Store) and provided id func newAccountWithId(accountId string, userId string) *Account { log.Debugf("creating new account") setupKeys := make(map[string]*SetupKey) defaultKey := GenerateDefaultSetupKey() oneOffKey := GenerateSetupKey("One-off key", SetupKeyOneOff, DefaultSetupKeyDuration) setupKeys[defaultKey.Key] = defaultKey setupKeys[oneOffKey.Key] = oneOffKey network := NewNetwork() peers := make(map[string]*Peer) users := make(map[string]*User) log.Debugf("created new account %s with setup key %s", accountId, defaultKey.Key) return &Account{Id: accountId, SetupKeys: setupKeys, Network: network, Peers: peers, Users: users, CreatedBy: userId} } func getAccountSetupKeyById(acc *Account, keyId string) *SetupKey { for _, k := range acc.SetupKeys { if keyId == k.Id { return k } } return nil } func getAccountSetupKeyByKey(acc *Account, key string) *SetupKey { for _, k := range acc.SetupKeys { if key == k.Key { return k } } return nil }