mirror of
https://github.com/netbirdio/netbird.git
synced 2025-03-27 08:07:51 +01:00
storing and retrieving PATs
This commit is contained in:
parent
6143b819c5
commit
b8cab2882b
@ -2,7 +2,9 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/netip"
|
||||
@ -12,17 +14,19 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"codeberg.org/ac/base62"
|
||||
"github.com/eko/gocache/v3/cache"
|
||||
cacheStore "github.com/eko/gocache/v3/store"
|
||||
gocache "github.com/patrickmn/go-cache"
|
||||
"github.com/rs/xid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/idp"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
gocache "github.com/patrickmn/go-cache"
|
||||
"github.com/rs/xid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -50,6 +54,7 @@ type AccountManager interface {
|
||||
GetSetupKey(accountID, userID, keyID string) (*SetupKey, error)
|
||||
GetAccountByUserOrAccountID(userID, accountID, domain string) (*Account, error)
|
||||
GetAccountFromToken(claims jwtclaims.AuthorizationClaims) (*Account, *User, error)
|
||||
GetAccountFromPAT(pat string) (*Account, *User, error)
|
||||
IsUserAdmin(claims jwtclaims.AuthorizationClaims) (bool, error)
|
||||
AccountExists(accountId string) (*bool, error)
|
||||
GetPeerByKey(peerKey string) (*Peer, error)
|
||||
@ -1112,6 +1117,47 @@ func (am *DefaultAccountManager) redeemInvite(account *Account, userID string) e
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAccountFromPAT returns Account and User associated with a personal access token
|
||||
func (am *DefaultAccountManager) GetAccountFromPAT(token string) (*Account, *User, error) {
|
||||
if len(token) != PATLength {
|
||||
return nil, nil, fmt.Errorf("token invalid")
|
||||
}
|
||||
|
||||
prefix := token[:len(PATPrefix)]
|
||||
if prefix != PATPrefix {
|
||||
return nil, nil, fmt.Errorf("token invalid")
|
||||
}
|
||||
secret := token[len(PATPrefix):len(PATPrefix)]
|
||||
encodedChecksum := token[34:40]
|
||||
|
||||
verificationChecksum, err := base62.Decode(encodedChecksum)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("token invalid")
|
||||
}
|
||||
|
||||
secretChecksum := crc32.ChecksumIEEE([]byte(secret))
|
||||
if secretChecksum != verificationChecksum {
|
||||
return nil, nil, fmt.Errorf("token invalid")
|
||||
}
|
||||
|
||||
hashedToken := sha256.Sum256([]byte(token))
|
||||
tokenID, err := am.Store.GetTokenIDByHashedToken(string(hashedToken[:]))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("token invalid")
|
||||
}
|
||||
|
||||
user, err := am.Store.GetUserByTokenID(tokenID)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("token invalid")
|
||||
}
|
||||
|
||||
account, err := am.Store.GetAccountByUser(user.Id)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("token invalid")
|
||||
}
|
||||
return account, user, nil
|
||||
}
|
||||
|
||||
// GetAccountFromToken returns an account associated with this token
|
||||
func (am *DefaultAccountManager) GetAccountFromToken(claims jwtclaims.AuthorizationClaims) (*Account, *User, error) {
|
||||
if claims.UserId == "" {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
@ -458,6 +459,61 @@ func TestDefaultAccountManager_GetAccountFromToken(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccountManager_GetAccountFromPAT(t *testing.T) {
|
||||
store := newStore(t)
|
||||
account := newAccountWithId("account_id", "testuser", "")
|
||||
account.Peers["testpeer"] = &Peer{
|
||||
Key: "peerkey",
|
||||
SetupKey: "peerkeysetupkey",
|
||||
IP: net.IP{127, 0, 0, 1},
|
||||
Meta: PeerSystemMeta{},
|
||||
Name: "peer name",
|
||||
Status: &PeerStatus{Connected: true, LastSeen: time.Now()},
|
||||
}
|
||||
|
||||
token := "nbp_9999EUDNdkeusjentDLSJEn1902u84390W6W"
|
||||
hashedToken := sha256.Sum256([]byte(token))
|
||||
pat := PersonalAccessToken{
|
||||
ID: "tokenId",
|
||||
Description: "some Description",
|
||||
HashedToken: string(hashedToken[:]),
|
||||
ExpirationDate: time.Time{},
|
||||
CreatedBy: "testuser",
|
||||
CreatedAt: time.Time{},
|
||||
LastUsed: time.Time{},
|
||||
}
|
||||
account.Users["someUser"] = &User{
|
||||
Id: "someUser",
|
||||
Role: "",
|
||||
AutoGroups: nil,
|
||||
PATs: []PersonalAccessToken{pat},
|
||||
}
|
||||
store.SaveAccount(account)
|
||||
|
||||
am := DefaultAccountManager{
|
||||
Store: store,
|
||||
cacheMux: sync.Mutex{},
|
||||
cacheLoading: nil,
|
||||
peersUpdateManager: nil,
|
||||
idpManager: nil,
|
||||
cacheManager: nil,
|
||||
ctx: nil,
|
||||
eventStore: nil,
|
||||
singleAccountMode: false,
|
||||
singleAccountModeDomain: "",
|
||||
dnsDomain: "",
|
||||
peerLoginExpiry: nil,
|
||||
}
|
||||
|
||||
account, user, err := am.GetAccountFromPAT(token)
|
||||
if err != nil {
|
||||
t.Fatalf("Error when getting Account from PAT: %s", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "account_id", account.Id)
|
||||
assert.Equal(t, "someUser", user.Id)
|
||||
}
|
||||
|
||||
func TestAccountManager_PrivateAccount(t *testing.T) {
|
||||
manager, err := createManager(t)
|
||||
if err != nil {
|
||||
|
@ -7,10 +7,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/rs/xid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
|
||||
"github.com/netbirdio/netbird/util"
|
||||
)
|
||||
|
||||
@ -25,6 +26,8 @@ type FileStore struct {
|
||||
PeerID2AccountID map[string]string `json:"-"`
|
||||
UserID2AccountID map[string]string `json:"-"`
|
||||
PrivateDomain2AccountID map[string]string `json:"-"`
|
||||
HashedPAT2TokenID map[string]string `json:"-"`
|
||||
TokenID2UserID map[string]string `json:"-"`
|
||||
InstallationID string
|
||||
|
||||
// mutex to synchronise Store read/write operations
|
||||
@ -57,6 +60,8 @@ func restore(file string) (*FileStore, error) {
|
||||
UserID2AccountID: make(map[string]string),
|
||||
PrivateDomain2AccountID: make(map[string]string),
|
||||
PeerID2AccountID: make(map[string]string),
|
||||
HashedPAT2TokenID: make(map[string]string),
|
||||
TokenID2UserID: make(map[string]string),
|
||||
storeFile: file,
|
||||
}
|
||||
|
||||
@ -80,6 +85,8 @@ func restore(file string) (*FileStore, error) {
|
||||
store.UserID2AccountID = make(map[string]string)
|
||||
store.PrivateDomain2AccountID = make(map[string]string)
|
||||
store.PeerID2AccountID = make(map[string]string)
|
||||
store.HashedPAT2TokenID = make(map[string]string)
|
||||
store.TokenID2UserID = make(map[string]string)
|
||||
|
||||
for accountID, account := range store.Accounts {
|
||||
if account.Settings == nil {
|
||||
@ -103,9 +110,10 @@ func restore(file string) (*FileStore, error) {
|
||||
}
|
||||
for _, user := range account.Users {
|
||||
store.UserID2AccountID[user.Id] = accountID
|
||||
}
|
||||
for _, user := range account.Users {
|
||||
store.UserID2AccountID[user.Id] = accountID
|
||||
for _, pat := range user.PATs {
|
||||
store.TokenID2UserID[pat.ID] = user.Id
|
||||
store.HashedPAT2TokenID[string(pat.HashedToken[:])] = pat.ID
|
||||
}
|
||||
}
|
||||
|
||||
if account.Domain != "" && account.DomainCategory == PrivateCategory &&
|
||||
@ -258,6 +266,10 @@ func (s *FileStore) SaveAccount(account *Account) error {
|
||||
|
||||
for _, user := range accountCopy.Users {
|
||||
s.UserID2AccountID[user.Id] = accountCopy.Id
|
||||
for _, pat := range user.PATs {
|
||||
s.TokenID2UserID[pat.ID] = user.Id
|
||||
s.HashedPAT2TokenID[string(pat.HashedToken[:])] = pat.ID
|
||||
}
|
||||
}
|
||||
|
||||
if accountCopy.DomainCategory == PrivateCategory && accountCopy.IsDomainPrimaryAccount {
|
||||
@ -312,6 +324,42 @@ func (s *FileStore) GetAccountBySetupKey(setupKey string) (*Account, error) {
|
||||
return account.Copy(), nil
|
||||
}
|
||||
|
||||
// GetTokenIDByHashedToken returns the id of a personal access token by its hashed secret
|
||||
func (s *FileStore) GetTokenIDByHashedToken(token string) (string, error) {
|
||||
s.mux.Lock()
|
||||
defer s.mux.Unlock()
|
||||
|
||||
tokenID, tokenIDFound := s.HashedPAT2TokenID[token]
|
||||
if !tokenIDFound {
|
||||
return "", status.Errorf(status.NotFound, "tokenID not found: provided token doesn't exists")
|
||||
}
|
||||
|
||||
return tokenID, nil
|
||||
}
|
||||
|
||||
// GetUserByTokenID returns a User object a tokenID belongs to
|
||||
func (s *FileStore) GetUserByTokenID(tokenID string) (*User, error) {
|
||||
s.mux.Lock()
|
||||
defer s.mux.Unlock()
|
||||
|
||||
userID, userIDFound := s.TokenID2UserID[tokenID]
|
||||
if !userIDFound {
|
||||
return nil, status.Errorf(status.NotFound, "user not found: provided tokenID doesn't exists")
|
||||
}
|
||||
|
||||
accountID, accountIDFound := s.UserID2AccountID[userID]
|
||||
if !accountIDFound {
|
||||
return nil, status.Errorf(status.NotFound, "accountID not found: provided userID doesn't exists")
|
||||
}
|
||||
|
||||
account, err := s.getAccount(accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return account.Users[userID].Copy(), nil
|
||||
}
|
||||
|
||||
// GetAllAccounts returns all accounts
|
||||
func (s *FileStore) GetAllAccounts() (all []*Account) {
|
||||
s.mux.Lock()
|
||||
|
@ -1,6 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@ -71,6 +72,14 @@ func TestNewStore(t *testing.T) {
|
||||
if store.UserID2AccountID == nil || len(store.UserID2AccountID) != 0 {
|
||||
t.Errorf("expected to create a new empty UserID2AccountID map when creating a new FileStore")
|
||||
}
|
||||
|
||||
if store.HashedPAT2TokenID == nil || len(store.HashedPAT2TokenID) != 0 {
|
||||
t.Errorf("expected to create a new empty HashedPAT2TokenID map when creating a new FileStore")
|
||||
}
|
||||
|
||||
if store.TokenID2UserID == nil || len(store.TokenID2UserID) != 0 {
|
||||
t.Errorf("expected to create a new empty TokenID2UserID map when creating a new FileStore")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveAccount(t *testing.T) {
|
||||
@ -239,11 +248,17 @@ func TestRestore(t *testing.T) {
|
||||
|
||||
require.NotNil(t, account.SetupKeys["A2C8E62B-38F5-4553-B31E-DD66C696CEBB"], "failed to restore a FileStore file - missing Account SetupKey A2C8E62B-38F5-4553-B31E-DD66C696CEBB")
|
||||
|
||||
require.Len(t, account.Users["f4f6d672-63fb-11ec-90d6-0242ac120003"].PATs, 1, "failed to restore a FileStore wrong PATs length")
|
||||
|
||||
require.Len(t, store.UserID2AccountID, 2, "failed to restore a FileStore wrong UserID2AccountID mapping length")
|
||||
|
||||
require.Len(t, store.SetupKeyID2AccountID, 1, "failed to restore a FileStore wrong SetupKeyID2AccountID mapping length")
|
||||
|
||||
require.Len(t, store.PrivateDomain2AccountID, 1, "failed to restore a FileStore wrong PrivateDomain2AccountID mapping length")
|
||||
|
||||
require.Len(t, store.HashedPAT2TokenID, 1, "failed to restore a FileStore wrong HashedPAT2TokenID mapping length")
|
||||
|
||||
require.Len(t, store.TokenID2UserID, 1, "failed to restore a FileStore wrong TokenID2UserID mapping length")
|
||||
}
|
||||
|
||||
func TestRestorePolicies_Migration(t *testing.T) {
|
||||
@ -348,6 +363,110 @@ func TestFileStore_GetAccount(t *testing.T) {
|
||||
assert.Len(t, account.NameServerGroups, len(expected.NameServerGroups))
|
||||
}
|
||||
|
||||
func TestFileStore_GetTokenIDByHashedToken(t *testing.T) {
|
||||
storeDir := t.TempDir()
|
||||
storeFile := filepath.Join(storeDir, "store.json")
|
||||
err := util.CopyFileContents("testdata/store.json", storeFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
accounts := &accounts{}
|
||||
_, err = util.ReadJson(storeFile, accounts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
store, err := NewFileStore(storeDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hashedToken := accounts.Accounts["bf1c8084-ba50-4ce7-9439-34653001fc3b"].Users["f4f6d672-63fb-11ec-90d6-0242ac120003"].PATs[0].HashedToken
|
||||
tokenID, err := store.GetTokenIDByHashedToken(hashedToken)
|
||||
|
||||
expectedTokenID := accounts.Accounts["bf1c8084-ba50-4ce7-9439-34653001fc3b"].Users["f4f6d672-63fb-11ec-90d6-0242ac120003"].PATs[0].ID
|
||||
assert.Equal(t, expectedTokenID, tokenID)
|
||||
}
|
||||
|
||||
func TestFileStore_GetTokenIDByHashedToken_Failure(t *testing.T) {
|
||||
storeDir := t.TempDir()
|
||||
storeFile := filepath.Join(storeDir, "store.json")
|
||||
err := util.CopyFileContents("testdata/store.json", storeFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
accounts := &accounts{}
|
||||
_, err = util.ReadJson(storeFile, accounts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
store, err := NewFileStore(storeDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
wrongToken := sha256.Sum256([]byte("someNotValidTokenThatFails1234"))
|
||||
_, err = store.GetTokenIDByHashedToken(string(wrongToken[:]))
|
||||
|
||||
assert.Error(t, err, "GetTokenIDByHashedToken should throw error if token invalid")
|
||||
}
|
||||
|
||||
func TestFileStore_GetUserByTokenID(t *testing.T) {
|
||||
storeDir := t.TempDir()
|
||||
storeFile := filepath.Join(storeDir, "store.json")
|
||||
err := util.CopyFileContents("testdata/store.json", storeFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
accounts := &accounts{}
|
||||
_, err = util.ReadJson(storeFile, accounts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
store, err := NewFileStore(storeDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tokenId := accounts.Accounts["bf1c8084-ba50-4ce7-9439-34653001fc3b"].Users["f4f6d672-63fb-11ec-90d6-0242ac120003"].PATs[0].ID
|
||||
user, err := store.GetUserByTokenID(tokenId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "f4f6d672-63fb-11ec-90d6-0242ac120003", user.Id)
|
||||
}
|
||||
|
||||
func TestFileStore_GetUserByTokenID_Failure(t *testing.T) {
|
||||
storeDir := t.TempDir()
|
||||
storeFile := filepath.Join(storeDir, "store.json")
|
||||
err := util.CopyFileContents("testdata/store.json", storeFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
accounts := &accounts{}
|
||||
_, err = util.ReadJson(storeFile, accounts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
store, err := NewFileStore(storeDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
wrongTokenID := "someNonExistingTokenID"
|
||||
_, err = store.GetUserByTokenID(wrongTokenID)
|
||||
|
||||
assert.Error(t, err, "GetUserByTokenID should throw error if tokenID invalid")
|
||||
}
|
||||
|
||||
func TestFileStore_SavePeerStatus(t *testing.T) {
|
||||
storeDir := t.TempDir()
|
||||
|
||||
|
@ -15,6 +15,7 @@ const (
|
||||
// PATPrefix is the globally used, 4 char prefix for personal access tokens
|
||||
PATPrefix = "nbp_"
|
||||
secretLength = 30
|
||||
PATLength = 40
|
||||
)
|
||||
|
||||
// PersonalAccessToken holds all information about a PAT including a hashed version of it for verification
|
||||
|
@ -6,8 +6,10 @@ type Store interface {
|
||||
GetAccountByUser(userID string) (*Account, error)
|
||||
GetAccountByPeerPubKey(peerKey string) (*Account, error)
|
||||
GetAccountByPeerID(peerID string) (*Account, error)
|
||||
GetAccountBySetupKey(setupKey string) (*Account, error) //todo use key hash later
|
||||
GetAccountBySetupKey(setupKey string) (*Account, error) // todo use key hash later
|
||||
GetAccountByPrivateDomain(domain string) (*Account, error)
|
||||
GetTokenIDByHashedToken(secret string) (string, error)
|
||||
GetUserByTokenID(tokenID string) (*User, error)
|
||||
SaveAccount(account *Account) error
|
||||
GetInstallationID() string
|
||||
SaveInstallationID(ID string) error
|
||||
|
13
management/server/testdata/store.json
vendored
13
management/server/testdata/store.json
vendored
@ -33,7 +33,18 @@
|
||||
},
|
||||
"f4f6d672-63fb-11ec-90d6-0242ac120003": {
|
||||
"Id": "f4f6d672-63fb-11ec-90d6-0242ac120003",
|
||||
"Role": "user"
|
||||
"Role": "user",
|
||||
"PATs":[
|
||||
{
|
||||
"ID":"9dj38s35-63fb-11ec-90d6-0242ac120003",
|
||||
"Description":"some Description",
|
||||
"HashedToken":"SoMeHaShEdToKeN",
|
||||
"ExpirationDate":"2023-02-27T00:00:00Z",
|
||||
"CreatedBy":"user",
|
||||
"CreatedAt":"2023-01-01T00:00:00Z",
|
||||
"LastUsed":"2023-02-01T00:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -189,6 +189,30 @@ func (am *DefaultAccountManager) CreateUser(accountID, userID string, invite *Us
|
||||
|
||||
}
|
||||
|
||||
// AddPATToUser takes the userID and the accountID the user belongs to and assigns a provided PersonalAccessToken to that user
|
||||
func (am *DefaultAccountManager) AddPATToUser(accountID string, userID string, pat PersonalAccessToken) error {
|
||||
unlock := am.Store.AcquireAccountLock(accountID)
|
||||
defer unlock()
|
||||
|
||||
account, err := am.Store.GetAccount(accountID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user := account.Users[userID]
|
||||
if user == nil {
|
||||
return status.Errorf(status.NotFound, "user not found")
|
||||
}
|
||||
|
||||
user.PATs = append(user.PATs, pat)
|
||||
|
||||
if err = am.Store.SaveAccount(account); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveUser saves updates a given user. If the user doesn't exit it will throw status.NotFound error.
|
||||
// Only User.AutoGroups field is allowed to be updated for now.
|
||||
func (am *DefaultAccountManager) SaveUser(accountID, userID string, update *User) (*UserInfo, error) {
|
||||
|
67
management/server/user_test.go
Normal file
67
management/server/user_test.go
Normal file
@ -0,0 +1,67 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUser_AddPATToUser(t *testing.T) {
|
||||
store := newStore(t)
|
||||
account := newAccountWithId("account_id", "testuser", "")
|
||||
account.Peers["testpeer"] = &Peer{
|
||||
Key: "peerkey",
|
||||
SetupKey: "peerkeysetupkey",
|
||||
IP: net.IP{127, 0, 0, 1},
|
||||
Meta: PeerSystemMeta{},
|
||||
Name: "peer name",
|
||||
Status: &PeerStatus{Connected: true, LastSeen: time.Now()},
|
||||
}
|
||||
store.SaveAccount(account)
|
||||
|
||||
am := DefaultAccountManager{
|
||||
Store: store,
|
||||
cacheMux: sync.Mutex{},
|
||||
cacheLoading: nil,
|
||||
peersUpdateManager: nil,
|
||||
idpManager: nil,
|
||||
cacheManager: nil,
|
||||
ctx: nil,
|
||||
eventStore: nil,
|
||||
singleAccountMode: false,
|
||||
singleAccountModeDomain: "",
|
||||
dnsDomain: "",
|
||||
peerLoginExpiry: nil,
|
||||
}
|
||||
|
||||
token := "someToken"
|
||||
pat := PersonalAccessToken{
|
||||
ID: "tokenId",
|
||||
Description: "some Description",
|
||||
HashedToken: token,
|
||||
ExpirationDate: time.Time{},
|
||||
CreatedBy: "testuser",
|
||||
CreatedAt: time.Time{},
|
||||
LastUsed: time.Time{},
|
||||
}
|
||||
|
||||
am.AddPATToUser("account_id", "testuser", pat)
|
||||
|
||||
fileStore := am.Store.(*FileStore)
|
||||
tokenId := fileStore.HashedPAT2TokenID[string(token[:])]
|
||||
|
||||
if tokenId == "" {
|
||||
t.Fatal("GetTokenIDByHashedToken failed after adding PAT")
|
||||
}
|
||||
|
||||
assert.Equal(t, "tokenId", tokenId)
|
||||
|
||||
userId := fileStore.TokenID2UserID[tokenId]
|
||||
if userId == "" {
|
||||
t.Fatal("GetUserByTokenId failed after adding PAT")
|
||||
}
|
||||
assert.Equal(t, "testuser", userId)
|
||||
}
|
Loading…
Reference in New Issue
Block a user