2023-02-15 06:21:51 +08:00
|
|
|
package logic
|
|
|
|
|
|
|
|
import (
|
2023-02-16 05:32:16 +08:00
|
|
|
b64 "encoding/base64"
|
2023-02-15 06:21:51 +08:00
|
|
|
"encoding/json"
|
2023-02-16 04:52:58 +08:00
|
|
|
"errors"
|
2023-02-15 06:21:51 +08:00
|
|
|
"fmt"
|
2024-08-15 14:29:00 +08:00
|
|
|
"sync"
|
2023-02-15 06:21:51 +08:00
|
|
|
"time"
|
|
|
|
|
2023-11-04 20:28:57 +08:00
|
|
|
"github.com/google/uuid"
|
2023-02-15 06:21:51 +08:00
|
|
|
"github.com/gravitl/netmaker/database"
|
|
|
|
"github.com/gravitl/netmaker/models"
|
2024-08-15 14:29:00 +08:00
|
|
|
"github.com/gravitl/netmaker/servercfg"
|
2023-11-04 20:28:57 +08:00
|
|
|
"golang.org/x/exp/slices"
|
2023-02-15 06:21:51 +08:00
|
|
|
)
|
|
|
|
|
2023-02-17 07:56:45 +08:00
|
|
|
// EnrollmentErrors - struct for holding EnrollmentKey error messages
|
|
|
|
var EnrollmentErrors = struct {
|
2023-02-16 05:32:16 +08:00
|
|
|
InvalidCreate error
|
|
|
|
NoKeyFound error
|
|
|
|
InvalidKey error
|
|
|
|
NoUsesRemaining error
|
|
|
|
FailedToTokenize error
|
|
|
|
FailedToDeTokenize error
|
2023-02-15 06:21:51 +08:00
|
|
|
}{
|
2024-01-03 14:53:04 +08:00
|
|
|
InvalidCreate: fmt.Errorf("failed to create enrollment key. paramters invalid"),
|
2023-02-16 05:32:16 +08:00
|
|
|
NoKeyFound: fmt.Errorf("no enrollmentkey found"),
|
|
|
|
InvalidKey: fmt.Errorf("invalid key provided"),
|
|
|
|
NoUsesRemaining: fmt.Errorf("no uses remaining"),
|
|
|
|
FailedToTokenize: fmt.Errorf("failed to tokenize"),
|
|
|
|
FailedToDeTokenize: fmt.Errorf("failed to detokenize"),
|
2023-02-15 06:21:51 +08:00
|
|
|
}
|
2024-08-15 14:29:00 +08:00
|
|
|
var (
|
|
|
|
enrollmentkeyCacheMutex = &sync.RWMutex{}
|
|
|
|
enrollmentkeyCacheMap = make(map[string]models.EnrollmentKey)
|
|
|
|
)
|
2023-02-15 06:21:51 +08:00
|
|
|
|
|
|
|
// CreateEnrollmentKey - creates a new enrollment key in db
|
2023-11-04 20:28:57 +08:00
|
|
|
func CreateEnrollmentKey(uses int, expiration time.Time, networks, tags []string, unlimited bool, relay uuid.UUID) (*models.EnrollmentKey, error) {
|
2023-02-15 06:21:51 +08:00
|
|
|
newKeyID, err := getUniqueEnrollmentID()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-11-04 20:28:57 +08:00
|
|
|
k := &models.EnrollmentKey{
|
2023-02-15 06:21:51 +08:00
|
|
|
Value: newKeyID,
|
|
|
|
Expiration: time.Time{},
|
|
|
|
UsesRemaining: 0,
|
|
|
|
Unlimited: unlimited,
|
|
|
|
Networks: []string{},
|
|
|
|
Tags: []string{},
|
2023-05-05 23:03:59 +08:00
|
|
|
Type: models.Undefined,
|
2023-11-04 20:28:57 +08:00
|
|
|
Relay: relay,
|
2023-02-15 06:21:51 +08:00
|
|
|
}
|
|
|
|
if uses > 0 {
|
|
|
|
k.UsesRemaining = uses
|
2023-05-05 23:03:59 +08:00
|
|
|
k.Type = models.Uses
|
2023-05-08 18:42:16 +08:00
|
|
|
} else if !expiration.IsZero() {
|
2023-02-15 06:21:51 +08:00
|
|
|
k.Expiration = expiration
|
2023-05-05 23:03:59 +08:00
|
|
|
k.Type = models.TimeExpiration
|
2023-05-08 18:42:16 +08:00
|
|
|
} else if k.Unlimited {
|
2023-05-05 23:03:59 +08:00
|
|
|
k.Type = models.Unlimited
|
2023-02-15 06:21:51 +08:00
|
|
|
}
|
|
|
|
if len(networks) > 0 {
|
|
|
|
k.Networks = networks
|
|
|
|
}
|
|
|
|
if len(tags) > 0 {
|
|
|
|
k.Tags = tags
|
|
|
|
}
|
2024-01-03 14:53:04 +08:00
|
|
|
if err := k.Validate(); err != nil {
|
|
|
|
return nil, err
|
2023-02-15 06:21:51 +08:00
|
|
|
}
|
2023-11-04 20:28:57 +08:00
|
|
|
if relay != uuid.Nil {
|
|
|
|
relayNode, err := GetNodeByID(relay.String())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if !slices.Contains(k.Networks, relayNode.Network) {
|
|
|
|
return nil, errors.New("relay node not in key's networks")
|
|
|
|
}
|
|
|
|
if !relayNode.IsRelay {
|
|
|
|
return nil, errors.New("relay node is not a relay")
|
|
|
|
}
|
|
|
|
}
|
2023-02-15 06:21:51 +08:00
|
|
|
if err = upsertEnrollmentKey(k); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-11-04 20:28:57 +08:00
|
|
|
return k, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateEnrollmentKey - updates an existing enrollment key's associated relay
|
|
|
|
func UpdateEnrollmentKey(keyId string, relayId uuid.UUID) (*models.EnrollmentKey, error) {
|
|
|
|
key, err := GetEnrollmentKey(keyId)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if relayId != uuid.Nil {
|
|
|
|
relayNode, err := GetNodeByID(relayId.String())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if !slices.Contains(key.Networks, relayNode.Network) {
|
|
|
|
return nil, errors.New("relay node not in key's networks")
|
|
|
|
}
|
|
|
|
if !relayNode.IsRelay {
|
|
|
|
return nil, errors.New("relay node is not a relay")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
key.Relay = relayId
|
|
|
|
|
2024-08-15 14:29:00 +08:00
|
|
|
if err = upsertEnrollmentKey(&key); err != nil {
|
2023-11-04 20:28:57 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-08-15 14:29:00 +08:00
|
|
|
return &key, nil
|
2023-02-15 06:21:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetAllEnrollmentKeys - fetches all enrollment keys from DB
|
2023-05-31 15:41:54 +08:00
|
|
|
// TODO drop double pointer
|
2024-08-15 14:29:00 +08:00
|
|
|
func GetAllEnrollmentKeys() ([]models.EnrollmentKey, error) {
|
2023-02-15 06:21:51 +08:00
|
|
|
currentKeys, err := getEnrollmentKeysMap()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-08-15 14:29:00 +08:00
|
|
|
var currentKeysList = []models.EnrollmentKey{}
|
2023-02-15 06:21:51 +08:00
|
|
|
for k := range currentKeys {
|
2023-02-17 04:41:23 +08:00
|
|
|
currentKeysList = append(currentKeysList, currentKeys[k])
|
2023-02-15 06:21:51 +08:00
|
|
|
}
|
|
|
|
return currentKeysList, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetEnrollmentKey - fetches a single enrollment key
|
|
|
|
// returns nil and error if not found
|
2024-08-15 14:29:00 +08:00
|
|
|
func GetEnrollmentKey(value string) (key models.EnrollmentKey, err error) {
|
2023-02-15 06:21:51 +08:00
|
|
|
currentKeys, err := getEnrollmentKeysMap()
|
|
|
|
if err != nil {
|
2024-08-15 14:29:00 +08:00
|
|
|
return key, err
|
2023-02-15 06:21:51 +08:00
|
|
|
}
|
|
|
|
if key, ok := currentKeys[value]; ok {
|
|
|
|
return key, nil
|
|
|
|
}
|
2024-08-15 14:29:00 +08:00
|
|
|
return key, EnrollmentErrors.NoKeyFound
|
|
|
|
}
|
|
|
|
|
|
|
|
func deleteEnrollmentkeyFromCache(key string) {
|
|
|
|
enrollmentkeyCacheMutex.Lock()
|
|
|
|
delete(enrollmentkeyCacheMap, key)
|
|
|
|
enrollmentkeyCacheMutex.Unlock()
|
2023-02-15 06:21:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteEnrollmentKey - delete's a given enrollment key by value
|
|
|
|
func DeleteEnrollmentKey(value string) error {
|
2023-02-16 04:27:26 +08:00
|
|
|
_, err := GetEnrollmentKey(value)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-08-15 14:29:00 +08:00
|
|
|
err = database.DeleteRecord(database.ENROLLMENT_KEYS_TABLE_NAME, value)
|
|
|
|
if err == nil {
|
|
|
|
if servercfg.CacheEnabled() {
|
|
|
|
deleteEnrollmentkeyFromCache(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
2023-02-15 06:21:51 +08:00
|
|
|
}
|
|
|
|
|
2023-02-16 04:52:58 +08:00
|
|
|
// TryToUseEnrollmentKey - checks first if key can be decremented
|
|
|
|
// returns true if it is decremented or isvalid
|
|
|
|
func TryToUseEnrollmentKey(k *models.EnrollmentKey) bool {
|
|
|
|
key, err := decrementEnrollmentKey(k.Value)
|
|
|
|
if err != nil {
|
2023-02-17 07:56:45 +08:00
|
|
|
if errors.Is(err, EnrollmentErrors.NoUsesRemaining) {
|
2023-02-16 04:52:58 +08:00
|
|
|
return k.IsValid()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
k.UsesRemaining = key.UsesRemaining
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-02-16 05:32:16 +08:00
|
|
|
// Tokenize - tokenizes an enrollment key to be used via registration
|
|
|
|
// and attaches it to the Token field on the struct
|
|
|
|
func Tokenize(k *models.EnrollmentKey, serverAddr string) error {
|
2023-02-16 23:56:13 +08:00
|
|
|
if len(serverAddr) == 0 || k == nil {
|
2023-02-17 07:56:45 +08:00
|
|
|
return EnrollmentErrors.FailedToTokenize
|
2023-02-16 05:32:16 +08:00
|
|
|
}
|
|
|
|
newToken := models.EnrollmentToken{
|
|
|
|
Server: serverAddr,
|
|
|
|
Value: k.Value,
|
|
|
|
}
|
|
|
|
data, err := json.Marshal(&newToken)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-02-16 23:56:13 +08:00
|
|
|
k.Token = b64.StdEncoding.EncodeToString(data)
|
2023-02-16 05:32:16 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeTokenize - detokenizes a base64 encoded string
|
|
|
|
// and finds the associated enrollment key
|
|
|
|
func DeTokenize(b64Token string) (*models.EnrollmentKey, error) {
|
|
|
|
if len(b64Token) == 0 {
|
2023-02-17 07:56:45 +08:00
|
|
|
return nil, EnrollmentErrors.FailedToDeTokenize
|
2023-02-16 05:32:16 +08:00
|
|
|
}
|
|
|
|
tokenData, err := b64.StdEncoding.DecodeString(b64Token)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var newToken models.EnrollmentToken
|
|
|
|
err = json.Unmarshal(tokenData, &newToken)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
k, err := GetEnrollmentKey(newToken.Value)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-08-15 14:29:00 +08:00
|
|
|
return &k, nil
|
2023-02-16 05:32:16 +08:00
|
|
|
}
|
|
|
|
|
2023-02-16 04:52:58 +08:00
|
|
|
// == private ==
|
|
|
|
|
|
|
|
// decrementEnrollmentKey - decrements the uses on a key if above 0 remaining
|
|
|
|
func decrementEnrollmentKey(value string) (*models.EnrollmentKey, error) {
|
2023-02-16 04:27:26 +08:00
|
|
|
k, err := GetEnrollmentKey(value)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if k.UsesRemaining == 0 {
|
2023-02-17 07:56:45 +08:00
|
|
|
return nil, EnrollmentErrors.NoUsesRemaining
|
2023-02-16 04:27:26 +08:00
|
|
|
}
|
|
|
|
k.UsesRemaining = k.UsesRemaining - 1
|
2024-08-15 14:29:00 +08:00
|
|
|
if err = upsertEnrollmentKey(&k); err != nil {
|
2023-02-16 04:27:26 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-08-15 14:29:00 +08:00
|
|
|
return &k, nil
|
2023-02-16 04:27:26 +08:00
|
|
|
}
|
|
|
|
|
2023-02-15 06:21:51 +08:00
|
|
|
func upsertEnrollmentKey(k *models.EnrollmentKey) error {
|
|
|
|
if k == nil {
|
2023-02-17 07:56:45 +08:00
|
|
|
return EnrollmentErrors.InvalidKey
|
2023-02-15 06:21:51 +08:00
|
|
|
}
|
|
|
|
data, err := json.Marshal(k)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-08-15 14:29:00 +08:00
|
|
|
err = database.Insert(k.Value, string(data), database.ENROLLMENT_KEYS_TABLE_NAME)
|
|
|
|
if err == nil {
|
|
|
|
if servercfg.CacheEnabled() {
|
|
|
|
storeEnrollmentkeyInCache(k.Value, *k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2023-02-15 06:21:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func getUniqueEnrollmentID() (string, error) {
|
|
|
|
currentKeys, err := getEnrollmentKeysMap()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2023-05-03 01:28:00 +08:00
|
|
|
newID := RandomString(models.EnrollmentKeyLength)
|
2023-02-16 04:27:26 +08:00
|
|
|
for _, ok := currentKeys[newID]; ok; {
|
2023-05-03 01:28:00 +08:00
|
|
|
newID = RandomString(models.EnrollmentKeyLength)
|
2023-02-15 06:21:51 +08:00
|
|
|
}
|
|
|
|
return newID, nil
|
|
|
|
}
|
|
|
|
|
2024-08-15 14:29:00 +08:00
|
|
|
func getEnrollmentkeysFromCache() map[string]models.EnrollmentKey {
|
|
|
|
return enrollmentkeyCacheMap
|
|
|
|
}
|
|
|
|
|
|
|
|
func storeEnrollmentkeyInCache(key string, enrollmentkey models.EnrollmentKey) {
|
|
|
|
enrollmentkeyCacheMutex.Lock()
|
|
|
|
enrollmentkeyCacheMap[key] = enrollmentkey
|
|
|
|
enrollmentkeyCacheMutex.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func getEnrollmentKeysMap() (map[string]models.EnrollmentKey, error) {
|
|
|
|
if servercfg.CacheEnabled() {
|
|
|
|
keys := getEnrollmentkeysFromCache()
|
|
|
|
if len(keys) != 0 {
|
|
|
|
return keys, nil
|
|
|
|
}
|
|
|
|
}
|
2023-02-15 06:21:51 +08:00
|
|
|
records, err := database.FetchRecords(database.ENROLLMENT_KEYS_TABLE_NAME)
|
|
|
|
if err != nil {
|
|
|
|
if !database.IsEmptyRecord(err) {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2023-02-16 04:27:26 +08:00
|
|
|
if records == nil {
|
|
|
|
records = make(map[string]string)
|
|
|
|
}
|
2024-08-15 14:29:00 +08:00
|
|
|
currentKeys := make(map[string]models.EnrollmentKey, 0)
|
2023-02-15 06:21:51 +08:00
|
|
|
if len(records) > 0 {
|
|
|
|
for k := range records {
|
|
|
|
var currentKey models.EnrollmentKey
|
|
|
|
if err = json.Unmarshal([]byte(records[k]), ¤tKey); err != nil {
|
|
|
|
continue
|
|
|
|
}
|
2024-08-15 14:29:00 +08:00
|
|
|
currentKeys[k] = currentKey
|
|
|
|
if servercfg.CacheEnabled() {
|
|
|
|
storeEnrollmentkeyInCache(currentKey.Value, currentKey)
|
|
|
|
}
|
2023-02-15 06:21:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return currentKeys, nil
|
|
|
|
}
|