mirror of
https://github.com/netbirdio/netbird.git
synced 2025-06-19 08:17:01 +02:00
store hashed token base64 encoded
This commit is contained in:
parent
726ffb5740
commit
c5942e6b33
@ -3,6 +3,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
b64 "encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@ -54,7 +55,7 @@ type AccountManager interface {
|
|||||||
GetSetupKey(accountID, userID, keyID string) (*SetupKey, error)
|
GetSetupKey(accountID, userID, keyID string) (*SetupKey, error)
|
||||||
GetAccountByUserOrAccountID(userID, accountID, domain string) (*Account, error)
|
GetAccountByUserOrAccountID(userID, accountID, domain string) (*Account, error)
|
||||||
GetAccountFromToken(claims jwtclaims.AuthorizationClaims) (*Account, *User, error)
|
GetAccountFromToken(claims jwtclaims.AuthorizationClaims) (*Account, *User, error)
|
||||||
GetAccountFromPAT(pat string) (*Account, *User, error)
|
GetAccountFromPAT(pat string) (*Account, *User, *PersonalAccessToken, error)
|
||||||
IsUserAdmin(claims jwtclaims.AuthorizationClaims) (bool, error)
|
IsUserAdmin(claims jwtclaims.AuthorizationClaims) (bool, error)
|
||||||
AccountExists(accountId string) (*bool, error)
|
AccountExists(accountId string) (*bool, error)
|
||||||
GetPeerByKey(peerKey string) (*Peer, error)
|
GetPeerByKey(peerKey string) (*Peer, error)
|
||||||
@ -1120,44 +1121,55 @@ func (am *DefaultAccountManager) redeemInvite(account *Account, userID string) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountFromPAT returns Account and User associated with a personal access token
|
// GetAccountFromPAT returns Account and User associated with a personal access token
|
||||||
func (am *DefaultAccountManager) GetAccountFromPAT(token string) (*Account, *User, error) {
|
func (am *DefaultAccountManager) GetAccountFromPAT(token string) (*Account, *User, *PersonalAccessToken, error) {
|
||||||
if len(token) != PATLength {
|
if len(token) != PATLength {
|
||||||
return nil, nil, fmt.Errorf("token has wrong length")
|
return nil, nil, nil, fmt.Errorf("token has wrong length")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debugf("Token: %s", token)
|
||||||
|
|
||||||
prefix := token[:len(PATPrefix)]
|
prefix := token[:len(PATPrefix)]
|
||||||
if prefix != PATPrefix {
|
if prefix != PATPrefix {
|
||||||
return nil, nil, fmt.Errorf("token has wrong prefix")
|
return nil, nil, nil, fmt.Errorf("token has wrong prefix")
|
||||||
}
|
}
|
||||||
secret := token[len(PATPrefix) : len(PATPrefix)+PATSecretLength]
|
secret := token[len(PATPrefix) : len(PATPrefix)+PATSecretLength]
|
||||||
encodedChecksum := token[len(PATPrefix)+PATSecretLength : len(PATPrefix)+PATSecretLength+PATChecksumLength]
|
encodedChecksum := token[len(PATPrefix)+PATSecretLength : len(PATPrefix)+PATSecretLength+PATChecksumLength]
|
||||||
|
|
||||||
verificationChecksum, err := base62.Decode(encodedChecksum)
|
verificationChecksum, err := base62.Decode(encodedChecksum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("token checksum decoding failed: %w", err)
|
return nil, nil, nil, fmt.Errorf("token checksum decoding failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
secretChecksum := crc32.ChecksumIEEE([]byte(secret))
|
secretChecksum := crc32.ChecksumIEEE([]byte(secret))
|
||||||
if secretChecksum != verificationChecksum {
|
if secretChecksum != verificationChecksum {
|
||||||
return nil, nil, fmt.Errorf("token checksum does not match")
|
return nil, nil, nil, fmt.Errorf("token checksum does not match")
|
||||||
}
|
}
|
||||||
|
|
||||||
hashedToken := sha256.Sum256([]byte(token))
|
hashedToken := sha256.Sum256([]byte(token))
|
||||||
tokenID, err := am.Store.GetTokenIDByHashedToken(string(hashedToken[:]))
|
encodedHashedToken := b64.StdEncoding.EncodeToString(hashedToken[:])
|
||||||
|
tokenID, err := am.Store.GetTokenIDByHashedToken(encodedHashedToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
log.Debugf("TokenID: %s", tokenID)
|
||||||
|
|
||||||
user, err := am.Store.GetUserByTokenID(tokenID)
|
user, err := am.Store.GetUserByTokenID(tokenID)
|
||||||
|
log.Debugf("User: %v", user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
account, err := am.Store.GetAccountByUser(user.Id)
|
account, err := am.Store.GetAccountByUser(user.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
return account, user, nil
|
|
||||||
|
pat := user.PATs[tokenID]
|
||||||
|
if pat == nil {
|
||||||
|
return nil, nil, nil, fmt.Errorf("personal access token not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return account, user, pat, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountFromToken returns an account associated with this token
|
// GetAccountFromToken returns an account associated with this token
|
||||||
|
@ -2,6 +2,7 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
b64 "encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -465,12 +466,13 @@ func TestAccountManager_GetAccountFromPAT(t *testing.T) {
|
|||||||
|
|
||||||
token := "nbp_9999EUDNdkeusjentDLSJEn1902u84390W6W"
|
token := "nbp_9999EUDNdkeusjentDLSJEn1902u84390W6W"
|
||||||
hashedToken := sha256.Sum256([]byte(token))
|
hashedToken := sha256.Sum256([]byte(token))
|
||||||
|
encodedHashedToken := b64.StdEncoding.EncodeToString(hashedToken[:])
|
||||||
account.Users["someUser"] = &User{
|
account.Users["someUser"] = &User{
|
||||||
Id: "someUser",
|
Id: "someUser",
|
||||||
PATs: map[string]*PersonalAccessToken{
|
PATs: map[string]*PersonalAccessToken{
|
||||||
"pat1": {
|
"tokenId": {
|
||||||
ID: "tokenId",
|
ID: "tokenId",
|
||||||
HashedToken: string(hashedToken[:]),
|
HashedToken: encodedHashedToken,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -483,13 +485,14 @@ func TestAccountManager_GetAccountFromPAT(t *testing.T) {
|
|||||||
Store: store,
|
Store: store,
|
||||||
}
|
}
|
||||||
|
|
||||||
account, user, err := am.GetAccountFromPAT(token)
|
account, user, pat, err := am.GetAccountFromPAT(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error when getting Account from PAT: %s", err)
|
t.Fatalf("Error when getting Account from PAT: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, "account_id", account.Id)
|
assert.Equal(t, "account_id", account.Id)
|
||||||
assert.Equal(t, "someUser", user.Id)
|
assert.Equal(t, "someUser", user.Id)
|
||||||
|
assert.Equal(t, account.Users["someUser"].PATs["tokenId"], pat)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccountManager_PrivateAccount(t *testing.T) {
|
func TestAccountManager_PrivateAccount(t *testing.T) {
|
||||||
|
@ -112,7 +112,7 @@ func restore(file string) (*FileStore, error) {
|
|||||||
store.UserID2AccountID[user.Id] = accountID
|
store.UserID2AccountID[user.Id] = accountID
|
||||||
for _, pat := range user.PATs {
|
for _, pat := range user.PATs {
|
||||||
store.TokenID2UserID[pat.ID] = user.Id
|
store.TokenID2UserID[pat.ID] = user.Id
|
||||||
store.HashedPAT2TokenID[pat.HashedToken[:]] = pat.ID
|
store.HashedPAT2TokenID[pat.HashedToken] = pat.ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +268,7 @@ func (s *FileStore) SaveAccount(account *Account) error {
|
|||||||
s.UserID2AccountID[user.Id] = accountCopy.Id
|
s.UserID2AccountID[user.Id] = accountCopy.Id
|
||||||
for _, pat := range user.PATs {
|
for _, pat := range user.PATs {
|
||||||
s.TokenID2UserID[pat.ID] = user.Id
|
s.TokenID2UserID[pat.ID] = user.Id
|
||||||
s.HashedPAT2TokenID[pat.HashedToken[:]] = pat.ID
|
s.HashedPAT2TokenID[pat.HashedToken] = pat.ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,11 +349,13 @@ func (s *FileStore) GetTokenIDByHashedToken(token string) (string, error) {
|
|||||||
s.mux.Lock()
|
s.mux.Lock()
|
||||||
defer s.mux.Unlock()
|
defer s.mux.Unlock()
|
||||||
|
|
||||||
|
log.Debugf("TOken still there: %v", token)
|
||||||
|
log.Debugf("TokenID2UserId %v", s.HashedPAT2TokenID)
|
||||||
tokenID, ok := s.HashedPAT2TokenID[token]
|
tokenID, ok := s.HashedPAT2TokenID[token]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", status.Errorf(status.NotFound, "tokenID not found: provided token doesn't exists")
|
return "", status.Errorf(status.NotFound, "tokenID not found: provided token doesn't exists")
|
||||||
}
|
}
|
||||||
|
log.Debugf("TokenID for token %s is %s", token, tokenID)
|
||||||
return tokenID, nil
|
return tokenID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,12 +368,12 @@ func (s *FileStore) GetUserByTokenID(tokenID string) (*User, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, status.Errorf(status.NotFound, "user not found: provided tokenID doesn't exists")
|
return nil, status.Errorf(status.NotFound, "user not found: provided tokenID doesn't exists")
|
||||||
}
|
}
|
||||||
|
log.Debugf("UserID for tokenID %s is %s", tokenID, userID)
|
||||||
accountID, ok := s.UserID2AccountID[userID]
|
accountID, ok := s.UserID2AccountID[userID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, status.Errorf(status.NotFound, "accountID not found: provided userID doesn't exists")
|
return nil, status.Errorf(status.NotFound, "accountID not found: provided userID doesn't exists")
|
||||||
}
|
}
|
||||||
|
log.Debugf("AccountID for userID %s is %s", userID, accountID)
|
||||||
account, err := s.getAccount(accountID)
|
account, err := s.getAccount(accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -2,6 +2,7 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
b64 "encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"time"
|
"time"
|
||||||
@ -74,5 +75,6 @@ func generateNewToken() (string, string, error) {
|
|||||||
paddedChecksum := fmt.Sprintf("%06s", encodedChecksum)
|
paddedChecksum := fmt.Sprintf("%06s", encodedChecksum)
|
||||||
plainToken := PATPrefix + secret + paddedChecksum
|
plainToken := PATPrefix + secret + paddedChecksum
|
||||||
hashedToken := sha256.Sum256([]byte(plainToken))
|
hashedToken := sha256.Sum256([]byte(plainToken))
|
||||||
return string(hashedToken[:]), plainToken, nil
|
encodedHashedToken := b64.StdEncoding.EncodeToString(hashedToken[:])
|
||||||
|
return encodedHashedToken, plainToken, nil
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
b64 "encoding/base64"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -13,7 +14,8 @@ import (
|
|||||||
func TestPAT_GenerateToken_Hashing(t *testing.T) {
|
func TestPAT_GenerateToken_Hashing(t *testing.T) {
|
||||||
hashedToken, plainToken, _ := generateNewToken()
|
hashedToken, plainToken, _ := generateNewToken()
|
||||||
expectedToken := sha256.Sum256([]byte(plainToken))
|
expectedToken := sha256.Sum256([]byte(plainToken))
|
||||||
assert.Equal(t, hashedToken, string(expectedToken[:]))
|
encodedExpectedToken := b64.StdEncoding.EncodeToString(expectedToken[:])
|
||||||
|
assert.Equal(t, hashedToken, encodedExpectedToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPAT_GenerateToken_Prefix(t *testing.T) {
|
func TestPAT_GenerateToken_Prefix(t *testing.T) {
|
||||||
|
@ -37,7 +37,7 @@ func TestUser_AddPATToUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileStore := am.Store.(*FileStore)
|
fileStore := am.Store.(*FileStore)
|
||||||
tokenID := fileStore.HashedPAT2TokenID[mockToken[:]]
|
tokenID := fileStore.HashedPAT2TokenID[mockToken]
|
||||||
|
|
||||||
if tokenID == "" {
|
if tokenID == "" {
|
||||||
t.Fatal("GetTokenIDByHashedToken failed after adding PAT")
|
t.Fatal("GetTokenIDByHashedToken failed after adding PAT")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user