store hashed token base64 encoded

This commit is contained in:
Pascal Fischer 2023-03-29 15:21:53 +02:00
parent 726ffb5740
commit c5942e6b33
6 changed files with 43 additions and 22 deletions

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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
} }

View File

@ -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) {

View File

@ -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")