package logic import ( "encoding/base64" "encoding/json" "errors" "math/rand" "time" "github.com/go-playground/validator/v10" "github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/servercfg" ) const ( charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ) // CreateAccessKey - create access key func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models.AccessKey, error) { if accesskey.Name == "" { accesskey.Name = genKeyName() } if accesskey.Value == "" { accesskey.Value = genKey() } if accesskey.Uses == 0 { accesskey.Uses = 1 } checkkeys, err := GetKeys(network.NetID) if err != nil { return models.AccessKey{}, errors.New("could not retrieve network keys") } for _, key := range checkkeys { if key.Name == accesskey.Name { return models.AccessKey{}, errors.New("duplicate AccessKey Name") } } privAddr := "" if network.IsLocal != "" { privAddr = network.LocalRange } netID := network.NetID var accessToken models.AccessToken s := servercfg.GetServerConfig() servervals := models.ServerConfig{ GRPCConnString: s.GRPCConnString, GRPCSSL: s.GRPCSSL, CheckinInterval: s.CheckinInterval, } accessToken.ServerConfig = servervals accessToken.ClientConfig.Network = netID accessToken.ClientConfig.Key = accesskey.Value accessToken.ClientConfig.LocalRange = privAddr tokenjson, err := json.Marshal(accessToken) if err != nil { return accesskey, err } accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(tokenjson)) //validate accesskey v := validator.New() err = v.Struct(accesskey) if err != nil { for _, e := range err.(validator.ValidationErrors) { logger.Log(1, "validator", e.Error()) } return models.AccessKey{}, err } network.AccessKeys = append(network.AccessKeys, accesskey) data, err := json.Marshal(&network) if err != nil { return models.AccessKey{}, err } if err = database.Insert(network.NetID, string(data), database.NETWORKS_TABLE_NAME); err != nil { return models.AccessKey{}, err } return accesskey, nil } // DeleteKey - deletes a key func DeleteKey(keyname, netname string) error { network, err := GetParentNetwork(netname) if err != nil { return err } //basically, turn the list of access keys into the list of access keys before and after the item //have not done any error handling for if there's like...1 item. I think it works? need to test. found := false var updatedKeys []models.AccessKey for _, currentkey := range network.AccessKeys { if currentkey.Name == keyname { found = true } else { updatedKeys = append(updatedKeys, currentkey) } } if !found { return errors.New("key " + keyname + " does not exist") } network.AccessKeys = updatedKeys data, err := json.Marshal(&network) if err != nil { return err } if err := database.Insert(network.NetID, string(data), database.NETWORKS_TABLE_NAME); err != nil { return err } return nil } // GetKeys - fetches keys for network func GetKeys(net string) ([]models.AccessKey, error) { record, err := database.FetchRecord(database.NETWORKS_TABLE_NAME, net) if err != nil { return []models.AccessKey{}, err } network, err := ParseNetwork(record) if err != nil { return []models.AccessKey{}, err } return network.AccessKeys, nil } // DecrimentKey - decriments key uses func DecrimentKey(networkName string, keyvalue string) { var network models.Network network, err := GetParentNetwork(networkName) if err != nil || network.IsComms == "yes" { return } for i := len(network.AccessKeys) - 1; i >= 0; i-- { currentkey := network.AccessKeys[i] if currentkey.Value == keyvalue { network.AccessKeys[i].Uses-- if network.AccessKeys[i].Uses < 1 { network.AccessKeys = append(network.AccessKeys[:i], network.AccessKeys[i+1:]...) break } } } if newNetworkData, err := json.Marshal(&network); err != nil { logger.Log(2, "failed to decrement key") return } else { database.Insert(network.NetID, string(newNetworkData), database.NETWORKS_TABLE_NAME) } } // IsKeyValid - check if key is valid func IsKeyValid(networkname string, keyvalue string) bool { network, err := GetParentNetwork(networkname) if err != nil { return false } accesskeys := network.AccessKeys if network.IsComms == "yes" { accesskeys = getAllAccessKeys() } var key models.AccessKey foundkey := false isvalid := false for i := len(accesskeys) - 1; i >= 0; i-- { currentkey := accesskeys[i] if currentkey.Value == keyvalue { key = currentkey foundkey = true } } if foundkey { if key.Uses > 0 { isvalid = true } } return isvalid } // RemoveKeySensitiveInfo - remove sensitive key info func RemoveKeySensitiveInfo(keys []models.AccessKey) []models.AccessKey { var returnKeys []models.AccessKey for _, key := range keys { key.Value = models.PLACEHOLDER_KEY_TEXT key.AccessString = models.PLACEHOLDER_TOKEN_TEXT returnKeys = append(returnKeys, key) } return returnKeys } // == private methods == func genKeyName() string { var seededRand *rand.Rand = rand.New( rand.NewSource(time.Now().UnixNano())) length := 5 b := make([]byte, length) for i := range b { b[i] = charset[seededRand.Intn(len(charset))] } return "key" + string(b) } func genKey() string { var seededRand *rand.Rand = rand.New( rand.NewSource(time.Now().UnixNano())) length := 16 b := make([]byte, length) for i := range b { b[i] = charset[seededRand.Intn(len(charset))] } return string(b) } func getAllAccessKeys() []models.AccessKey { var accesskeys = make([]models.AccessKey, 0) networks, err := GetNetworks() if err != nil { return accesskeys } for i := range networks { accesskeys = append(accesskeys, networks[i].AccessKeys...) } return accesskeys }