mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-25 17:43:38 +01:00
Refactor user and PAT handling
Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>
This commit is contained in:
parent
4d00207c3b
commit
6a456c52bf
@ -58,7 +58,7 @@ type PersonalAccessTokenGenerated struct {
|
|||||||
|
|
||||||
// CreateNewPAT will generate a new PersonalAccessToken that can be assigned to a User.
|
// CreateNewPAT will generate a new PersonalAccessToken that can be assigned to a User.
|
||||||
// Additionally, it will return the token in plain text once, to give to the user and only save a hashed version
|
// Additionally, it will return the token in plain text once, to give to the user and only save a hashed version
|
||||||
func CreateNewPAT(name string, expirationInDays int, createdBy string) (*PersonalAccessTokenGenerated, error) {
|
func CreateNewPAT(name string, expirationInDays int, targetID, createdBy string) (*PersonalAccessTokenGenerated, error) {
|
||||||
hashedToken, plainToken, err := generateNewToken()
|
hashedToken, plainToken, err := generateNewToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -67,6 +67,7 @@ func CreateNewPAT(name string, expirationInDays int, createdBy string) (*Persona
|
|||||||
return &PersonalAccessTokenGenerated{
|
return &PersonalAccessTokenGenerated{
|
||||||
PersonalAccessToken: PersonalAccessToken{
|
PersonalAccessToken: PersonalAccessToken{
|
||||||
ID: xid.New().String(),
|
ID: xid.New().String(),
|
||||||
|
UserID: targetID,
|
||||||
Name: name,
|
Name: name,
|
||||||
HashedToken: hashedToken,
|
HashedToken: hashedToken,
|
||||||
ExpirationDate: currentTime.AddDate(0, 0, expirationInDays),
|
ExpirationDate: currentTime.AddDate(0, 0, expirationInDays),
|
||||||
|
@ -401,32 +401,26 @@ func (s *SqlStore) SavePeerLocation(ctx context.Context, lockStrength LockingStr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SaveUsers saves the given list of users to the database.
|
// SaveUsers saves the given list of users to the database.
|
||||||
// It updates existing users if a conflict occurs.
|
func (s *SqlStore) SaveUsers(ctx context.Context, lockStrength LockingStrength, users []*User) error {
|
||||||
func (s *SqlStore) SaveUsers(accountID string, users map[string]*User) error {
|
if len(users) == 0 {
|
||||||
usersToSave := make([]User, 0, len(users))
|
return nil
|
||||||
for _, user := range users {
|
|
||||||
user.AccountID = accountID
|
|
||||||
for id, pat := range user.PATs {
|
|
||||||
pat.ID = id
|
|
||||||
user.PATsG = append(user.PATsG, *pat)
|
|
||||||
}
|
|
||||||
usersToSave = append(usersToSave, *user)
|
|
||||||
}
|
|
||||||
err := s.db.Session(&gorm.Session{FullSaveAssociations: true}).
|
|
||||||
Clauses(clause.OnConflict{UpdateAll: true}).
|
|
||||||
Create(&usersToSave).Error
|
|
||||||
if err != nil {
|
|
||||||
return status.Errorf(status.Internal, "failed to save users to store: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result := s.db.WithContext(ctx).Clauses(clause.Locking{Strength: string(lockStrength)}).Save(&users)
|
||||||
|
if result.Error != nil {
|
||||||
|
log.WithContext(ctx).Errorf("failed to save users to store: %s", result.Error)
|
||||||
|
return status.Errorf(status.Internal, "failed to save users to store")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveUser saves the given user to the database.
|
// SaveUser saves the given user to the database.
|
||||||
func (s *SqlStore) SaveUser(ctx context.Context, lockStrength LockingStrength, user *User) error {
|
func (s *SqlStore) SaveUser(ctx context.Context, lockStrength LockingStrength, user *User) error {
|
||||||
result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Save(user)
|
result := s.db.WithContext(ctx).Clauses(clause.Locking{Strength: string(lockStrength)}).
|
||||||
|
Select(clause.Associations).Save(user)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
return status.Errorf(status.Internal, "failed to save user to store: %v", result.Error)
|
log.WithContext(ctx).Errorf("failed to save user to store: %s", result.Error)
|
||||||
|
return status.Errorf(status.Internal, "failed to save user to store")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -513,30 +507,17 @@ func (s *SqlStore) GetTokenIDByHashedToken(ctx context.Context, hashedToken stri
|
|||||||
return token.ID, nil
|
return token.ID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SqlStore) GetUserByTokenID(ctx context.Context, tokenID string) (*User, error) {
|
func (s *SqlStore) GetUserByPATID(ctx context.Context, lockStrength LockingStrength, patID string) (*User, error) {
|
||||||
var token PersonalAccessToken
|
var user User
|
||||||
result := s.db.First(&token, idQueryCondition, tokenID)
|
result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).
|
||||||
|
Joins("JOIN personal_access_tokens ON personal_access_tokens.user_id = users.id").
|
||||||
|
Where("personal_access_tokens.id = ?", patID).First(&user)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||||
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
return nil, status.NewPATNotFoundError(patID)
|
||||||
}
|
}
|
||||||
log.WithContext(ctx).Errorf("error when getting token from the store: %s", result.Error)
|
log.WithContext(ctx).Errorf("failed to get token user from the store: %s", result.Error)
|
||||||
return nil, status.NewGetAccountFromStoreError(result.Error)
|
return nil, status.NewGetUserFromStoreError()
|
||||||
}
|
|
||||||
|
|
||||||
if token.UserID == "" {
|
|
||||||
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
var user User
|
|
||||||
result = s.db.Preload("PATsG").First(&user, idQueryCondition, token.UserID)
|
|
||||||
if result.Error != nil {
|
|
||||||
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
user.PATs = make(map[string]*PersonalAccessToken, len(user.PATsG))
|
|
||||||
for _, pat := range user.PATsG {
|
|
||||||
user.PATs[pat.ID] = pat.Copy()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &user, nil
|
return &user, nil
|
||||||
@ -556,6 +537,25 @@ func (s *SqlStore) GetUserByUserID(ctx context.Context, lockStrength LockingStre
|
|||||||
return &user, nil
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SqlStore) DeleteUser(ctx context.Context, lockStrength LockingStrength, accountID, userID string) error {
|
||||||
|
err := s.db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
result := tx.Clauses(clause.Locking{Strength: string(lockStrength)}).
|
||||||
|
Delete(&PersonalAccessToken{}, "user_id = ?", userID)
|
||||||
|
if result.Error != nil {
|
||||||
|
return result.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.Clauses(clause.Locking{Strength: string(lockStrength)}).
|
||||||
|
Delete(&User{}, accountAndIDQueryCondition, accountID, userID).Error
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.WithContext(ctx).Errorf("failed to delete user from the store: %s", err)
|
||||||
|
return status.Errorf(status.Internal, "failed to delete user from store")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SqlStore) GetAccountUsers(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*User, error) {
|
func (s *SqlStore) GetAccountUsers(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*User, error) {
|
||||||
var users []*User
|
var users []*User
|
||||||
result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Find(&users, accountIDCondition, accountID)
|
result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Find(&users, accountIDCondition, accountID)
|
||||||
@ -865,6 +865,20 @@ func (s *SqlStore) GetAccountSettings(ctx context.Context, lockStrength LockingS
|
|||||||
return accountSettings.Settings, nil
|
return accountSettings.Settings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SqlStore) GetAccountCreatedBy(ctx context.Context, lockStrength LockingStrength, accountID string) (string, error) {
|
||||||
|
var createdBy string
|
||||||
|
result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Model(&Account{}).
|
||||||
|
Select("created_by").First(&createdBy, idQueryCondition, accountID)
|
||||||
|
if result.Error != nil {
|
||||||
|
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return "", status.NewAccountNotFoundError(accountID)
|
||||||
|
}
|
||||||
|
return "", status.NewGetAccountFromStoreError(result.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return createdBy, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SaveUserLastLogin stores the last login time for a user in DB.
|
// SaveUserLastLogin stores the last login time for a user in DB.
|
||||||
func (s *SqlStore) SaveUserLastLogin(ctx context.Context, accountID, userID string, lastLogin time.Time) error {
|
func (s *SqlStore) SaveUserLastLogin(ctx context.Context, accountID, userID string, lastLogin time.Time) error {
|
||||||
var user User
|
var user User
|
||||||
@ -1685,3 +1699,94 @@ func (s *SqlStore) SaveDNSSettings(ctx context.Context, lockStrength LockingStre
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPATByHashedToken returns a PersonalAccessToken by its hashed token.
|
||||||
|
func (s *SqlStore) GetPATByHashedToken(ctx context.Context, lockStrength LockingStrength, hashedToken string) (*PersonalAccessToken, error) {
|
||||||
|
var pat PersonalAccessToken
|
||||||
|
result := s.db.WithContext(ctx).Clauses(clause.Locking{Strength: string(lockStrength)}).First(&pat, "hashed_token = ?", hashedToken)
|
||||||
|
if result.Error != nil {
|
||||||
|
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, status.NewPATNotFoundError(hashedToken)
|
||||||
|
}
|
||||||
|
log.WithContext(ctx).Errorf("failed to get pat by hash from the store: %s", result.Error)
|
||||||
|
return nil, status.Errorf(status.Internal, "failed to get pat by hash from store")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pat, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPATByID retrieves a personal access token by its ID and user ID.
|
||||||
|
func (s *SqlStore) GetPATByID(ctx context.Context, lockStrength LockingStrength, userID string, patID string) (*PersonalAccessToken, error) {
|
||||||
|
var pat PersonalAccessToken
|
||||||
|
result := s.db.WithContext(ctx).Clauses(clause.Locking{Strength: string(lockStrength)}).
|
||||||
|
First(&pat, "id = ? AND user_id = ?", patID, userID)
|
||||||
|
if err := result.Error; err != nil {
|
||||||
|
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, status.NewPATNotFoundError(patID)
|
||||||
|
}
|
||||||
|
log.WithContext(ctx).Errorf("failed to get pat from the store: %s", err)
|
||||||
|
return nil, status.Errorf(status.Internal, "failed to get pat from store")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pat, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserPATs retrieves personal access tokens for a user.
|
||||||
|
func (s *SqlStore) GetUserPATs(ctx context.Context, lockStrength LockingStrength, userID string) ([]*PersonalAccessToken, error) {
|
||||||
|
var pats []*PersonalAccessToken
|
||||||
|
result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Find(&pats, "user_id = ?", userID)
|
||||||
|
if err := result.Error; err != nil {
|
||||||
|
log.WithContext(ctx).Errorf("failed to get user pat's from the store: %s", err)
|
||||||
|
return nil, status.Errorf(status.Internal, "failed to get user pat's from store")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pats, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkPATUsed marks a personal access token as used.
|
||||||
|
func (s *SqlStore) MarkPATUsed(ctx context.Context, lockStrength LockingStrength, patID string) error {
|
||||||
|
patCopy := PersonalAccessToken{
|
||||||
|
LastUsed: time.Now().UTC(),
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldsToUpdate := []string{"last_used"}
|
||||||
|
result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Select(fieldsToUpdate).
|
||||||
|
Where(idQueryCondition, patID).Updates(&patCopy)
|
||||||
|
if result.Error != nil {
|
||||||
|
log.WithContext(ctx).Errorf("failed to mark pat as used: %s", result.Error)
|
||||||
|
return status.Errorf(status.Internal, "failed to mark pat as used")
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return status.NewPATNotFoundError(patID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SavePAT saves a personal access token to the database.
|
||||||
|
func (s *SqlStore) SavePAT(ctx context.Context, lockStrength LockingStrength, pat *PersonalAccessToken) error {
|
||||||
|
result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Save(pat)
|
||||||
|
if err := result.Error; err != nil {
|
||||||
|
log.WithContext(ctx).Errorf("failed to save pat to the store: %s", err)
|
||||||
|
return status.Errorf(status.Internal, "failed to save pat to store")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePAT deletes a personal access token from the database.
|
||||||
|
func (s *SqlStore) DeletePAT(ctx context.Context, lockStrength LockingStrength, userID, patID string) error {
|
||||||
|
result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).
|
||||||
|
Delete(&PersonalAccessToken{}, "user_id = ? AND id = ?", userID, patID)
|
||||||
|
if err := result.Error; err != nil {
|
||||||
|
log.WithContext(ctx).Errorf("failed to delete pat from the store: %s", err)
|
||||||
|
return status.Errorf(status.Internal, "failed to delete pat from store")
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return status.NewPATNotFoundError(patID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -159,3 +159,18 @@ func NewPolicyNotFoundError(policyID string) error {
|
|||||||
func NewNameServerGroupNotFoundError(nsGroupID string) error {
|
func NewNameServerGroupNotFoundError(nsGroupID string) error {
|
||||||
return Errorf(NotFound, "nameserver group: %s not found", nsGroupID)
|
return Errorf(NotFound, "nameserver group: %s not found", nsGroupID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewServiceUserRoleInvalidError creates a new Error with InvalidArgument type for creating a service user with owner role
|
||||||
|
func NewServiceUserRoleInvalidError() error {
|
||||||
|
return Errorf(InvalidArgument, "can't create a service user with owner role")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOwnerDeletePermissionError creates a new Error with PermissionDenied type for attempting
|
||||||
|
// to delete a user with the owner role.
|
||||||
|
func NewOwnerDeletePermissionError() error {
|
||||||
|
return Errorf(PermissionDenied, "can't delete a user with the owner role")
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPATNotFoundError(patID string) error {
|
||||||
|
return Errorf(NotFound, "PAT: %s not found", patID)
|
||||||
|
}
|
||||||
|
@ -57,21 +57,30 @@ type Store interface {
|
|||||||
GetAccountIDByPrivateDomain(ctx context.Context, lockStrength LockingStrength, domain string) (string, error)
|
GetAccountIDByPrivateDomain(ctx context.Context, lockStrength LockingStrength, domain string) (string, error)
|
||||||
GetAccountSettings(ctx context.Context, lockStrength LockingStrength, accountID string) (*Settings, error)
|
GetAccountSettings(ctx context.Context, lockStrength LockingStrength, accountID string) (*Settings, error)
|
||||||
GetAccountDNSSettings(ctx context.Context, lockStrength LockingStrength, accountID string) (*DNSSettings, error)
|
GetAccountDNSSettings(ctx context.Context, lockStrength LockingStrength, accountID string) (*DNSSettings, error)
|
||||||
|
GetAccountCreatedBy(ctx context.Context, lockStrength LockingStrength, accountID string) (string, error)
|
||||||
SaveAccount(ctx context.Context, account *Account) error
|
SaveAccount(ctx context.Context, account *Account) error
|
||||||
DeleteAccount(ctx context.Context, account *Account) error
|
DeleteAccount(ctx context.Context, account *Account) error
|
||||||
UpdateAccountDomainAttributes(ctx context.Context, accountID string, domain string, category string, isPrimaryDomain bool) error
|
UpdateAccountDomainAttributes(ctx context.Context, accountID string, domain string, category string, isPrimaryDomain bool) error
|
||||||
SaveDNSSettings(ctx context.Context, lockStrength LockingStrength, accountID string, settings *DNSSettings) error
|
SaveDNSSettings(ctx context.Context, lockStrength LockingStrength, accountID string, settings *DNSSettings) error
|
||||||
|
|
||||||
GetUserByTokenID(ctx context.Context, tokenID string) (*User, error)
|
GetUserByPATID(ctx context.Context, lockStrength LockingStrength, patID string) (*User, error)
|
||||||
GetUserByUserID(ctx context.Context, lockStrength LockingStrength, userID string) (*User, error)
|
GetUserByUserID(ctx context.Context, lockStrength LockingStrength, userID string) (*User, error)
|
||||||
GetAccountUsers(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*User, error)
|
GetAccountUsers(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*User, error)
|
||||||
SaveUsers(accountID string, users map[string]*User) error
|
SaveUsers(ctx context.Context, lockStrength LockingStrength, users []*User) error
|
||||||
SaveUser(ctx context.Context, lockStrength LockingStrength, user *User) error
|
SaveUser(ctx context.Context, lockStrength LockingStrength, user *User) error
|
||||||
SaveUserLastLogin(ctx context.Context, accountID, userID string, lastLogin time.Time) error
|
SaveUserLastLogin(ctx context.Context, accountID, userID string, lastLogin time.Time) error
|
||||||
|
DeleteUser(ctx context.Context, lockStrength LockingStrength, accountID, userID string) error
|
||||||
GetTokenIDByHashedToken(ctx context.Context, secret string) (string, error)
|
GetTokenIDByHashedToken(ctx context.Context, secret string) (string, error)
|
||||||
DeleteHashedPAT2TokenIDIndex(hashedToken string) error
|
DeleteHashedPAT2TokenIDIndex(hashedToken string) error
|
||||||
DeleteTokenID2UserIDIndex(tokenID string) error
|
DeleteTokenID2UserIDIndex(tokenID string) error
|
||||||
|
|
||||||
|
GetPATByID(ctx context.Context, lockStrength LockingStrength, userID, patID string) (*PersonalAccessToken, error)
|
||||||
|
GetUserPATs(ctx context.Context, lockStrength LockingStrength, userID string) ([]*PersonalAccessToken, error)
|
||||||
|
GetPATByHashedToken(ctx context.Context, lockStrength LockingStrength, hashedToken string) (*PersonalAccessToken, error)
|
||||||
|
MarkPATUsed(ctx context.Context, lockStrength LockingStrength, patID string) error
|
||||||
|
SavePAT(ctx context.Context, strength LockingStrength, pat *PersonalAccessToken) error
|
||||||
|
DeletePAT(ctx context.Context, strength LockingStrength, userID, patID string) error
|
||||||
|
|
||||||
GetAccountGroups(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*nbgroup.Group, error)
|
GetAccountGroups(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*nbgroup.Group, error)
|
||||||
GetGroupByID(ctx context.Context, lockStrength LockingStrength, groupID, accountID string) (*nbgroup.Group, error)
|
GetGroupByID(ctx context.Context, lockStrength LockingStrength, groupID, accountID string) (*nbgroup.Group, error)
|
||||||
GetGroupByName(ctx context.Context, lockStrength LockingStrength, groupName, accountID string) (*nbgroup.Group, error)
|
GetGroupByName(ctx context.Context, lockStrength LockingStrength, groupName, accountID string) (*nbgroup.Group, error)
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user