netbird/management/server/personal_access_token.go

97 lines
2.8 KiB
Go
Raw Permalink Normal View History

package server
import (
"crypto/sha256"
2023-03-29 15:21:53 +02:00
b64 "encoding/base64"
"fmt"
"hash/crc32"
"time"
2023-03-08 11:36:03 +01:00
b "github.com/hashicorp/go-secure-stdlib/base62"
2023-03-02 16:19:31 +01:00
"github.com/rs/xid"
2023-05-16 12:57:56 +02:00
"github.com/netbirdio/netbird/base62"
)
2023-03-08 11:54:10 +01:00
const (
2023-03-08 12:09:22 +01:00
// PATPrefix is the globally used, 4 char prefix for personal access tokens
2023-03-20 11:44:12 +01:00
PATPrefix = "nbp_"
// PATSecretLength number of characters used for the secret inside the token
PATSecretLength = 30
// PATChecksumLength number of characters used for the encoded checksum of the secret inside the token
2023-03-16 16:59:32 +01:00
PATChecksumLength = 6
2023-03-20 11:44:12 +01:00
// PATLength total number of characters used for the token
PATLength = 40
2023-03-08 11:54:10 +01:00
)
2023-03-06 14:46:04 +01:00
// PersonalAccessToken holds all information about a PAT including a hashed version of it for verification
type PersonalAccessToken struct {
ID string `gorm:"primaryKey"`
// User is a reference to Account that this object belongs
UserID string `gorm:"index"`
Name string
2023-03-08 11:30:09 +01:00
HashedToken string
ExpirationDate time.Time
// scope could be added in future
CreatedBy string
CreatedAt time.Time
LastUsed time.Time
}
func (t *PersonalAccessToken) Copy() *PersonalAccessToken {
return &PersonalAccessToken{
ID: t.ID,
Name: t.Name,
HashedToken: t.HashedToken,
ExpirationDate: t.ExpirationDate,
CreatedBy: t.CreatedBy,
CreatedAt: t.CreatedAt,
LastUsed: t.LastUsed,
}
}
// PersonalAccessTokenGenerated holds the new PersonalAccessToken and the plain text version of it
type PersonalAccessTokenGenerated struct {
PlainToken string
PersonalAccessToken
}
2023-03-06 14:46:04 +01:00
// 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
func CreateNewPAT(name string, expirationInDays int, targetID, createdBy string) (*PersonalAccessTokenGenerated, error) {
2023-03-08 11:36:03 +01:00
hashedToken, plainToken, err := generateNewToken()
if err != nil {
return nil, err
2023-03-08 11:36:03 +01:00
}
currentTime := time.Now()
return &PersonalAccessTokenGenerated{
PersonalAccessToken: PersonalAccessToken{
ID: xid.New().String(),
UserID: targetID,
Name: name,
HashedToken: hashedToken,
ExpirationDate: currentTime.AddDate(0, 0, expirationInDays),
CreatedBy: createdBy,
CreatedAt: currentTime,
2023-03-29 17:46:09 +02:00
LastUsed: time.Time{},
},
PlainToken: plainToken,
}, nil
}
2023-03-08 11:36:03 +01:00
func generateNewToken() (string, string, error) {
2023-03-20 11:44:12 +01:00
secret, err := b.Random(PATSecretLength)
2023-03-08 11:36:03 +01:00
if err != nil {
return "", "", err
}
2023-03-06 13:51:32 +01:00
checksum := crc32.ChecksumIEEE([]byte(secret))
2023-05-16 12:57:56 +02:00
encodedChecksum := base62.Encode(checksum)
paddedChecksum := fmt.Sprintf("%06s", encodedChecksum)
2023-03-08 11:54:10 +01:00
plainToken := PATPrefix + secret + paddedChecksum
hashedToken := sha256.Sum256([]byte(plainToken))
2023-03-29 15:21:53 +02:00
encodedHashedToken := b64.StdEncoding.EncodeToString(hashedToken[:])
return encodedHashedToken, plainToken, nil
}