|
|
|
@ -6,6 +6,7 @@ import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"log"
|
|
|
|
|
"os"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
bugout "github.com/bugout-dev/bugout-go/pkg"
|
|
|
|
|
"github.com/google/uuid"
|
|
|
|
@ -16,11 +17,19 @@ var (
|
|
|
|
|
stateCLI StateCLI
|
|
|
|
|
|
|
|
|
|
bugoutClient bugout.BugoutClient
|
|
|
|
|
|
|
|
|
|
DEFAULT_ACCESS_NAME = ""
|
|
|
|
|
DEFAULT_ACCESS_DESCRIPTION = ""
|
|
|
|
|
DEFAULT_BLOCKCHAIN_ACCESS = true
|
|
|
|
|
DEFAULT_EXTENDED_METHODS = true
|
|
|
|
|
DEFAULT_PERIOD_DURATION = int64(86400) // 1 day
|
|
|
|
|
DEFAULT_MAX_CALLS_PER_PERIOD = int64(10000)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Command Line Interface state
|
|
|
|
|
type StateCLI struct {
|
|
|
|
|
addAccessCmd *flag.FlagSet
|
|
|
|
|
updateAccessCmd *flag.FlagSet
|
|
|
|
|
generateConfigCmd *flag.FlagSet
|
|
|
|
|
deleteAccessCmd *flag.FlagSet
|
|
|
|
|
serverCmd *flag.FlagSet
|
|
|
|
@ -31,13 +40,21 @@ type StateCLI struct {
|
|
|
|
|
configPathFlag string
|
|
|
|
|
helpFlag bool
|
|
|
|
|
|
|
|
|
|
// Add user access flags
|
|
|
|
|
// Add/update user access flags
|
|
|
|
|
userIDFlag string
|
|
|
|
|
accessIDFlag string
|
|
|
|
|
accessNameFlag string
|
|
|
|
|
accessDescriptionFlag string
|
|
|
|
|
blockchainAccessFlag bool
|
|
|
|
|
extendedMethodsFlag bool
|
|
|
|
|
|
|
|
|
|
blockchainAccessFlag bool
|
|
|
|
|
extendedMethodsFlag bool
|
|
|
|
|
|
|
|
|
|
PeriodDurationFlag int64
|
|
|
|
|
MaxCallsPerPeriodFlag int64
|
|
|
|
|
|
|
|
|
|
// Update user access flags
|
|
|
|
|
PeriodStartTsFlag int64
|
|
|
|
|
CallsPerPeriodFlag int64
|
|
|
|
|
|
|
|
|
|
// Server flags
|
|
|
|
|
listeningAddrFlag string
|
|
|
|
@ -58,8 +75,8 @@ optional arguments:
|
|
|
|
|
-h, --help show this help message and exit
|
|
|
|
|
|
|
|
|
|
subcommands:
|
|
|
|
|
{%[1]s,%[2]s,%[3]s,%[4]s,%[5]s,%[6]s}
|
|
|
|
|
`, s.addAccessCmd.Name(), s.generateConfigCmd.Name(), s.deleteAccessCmd.Name(), s.serverCmd.Name(), s.usersCmd.Name(), s.versionCmd.Name())
|
|
|
|
|
{%[1]s,%[2]s,%[3]s,%[4]s,%[5]s,%[6]s,%[7]s}
|
|
|
|
|
`, s.addAccessCmd.Name(), s.updateAccessCmd.Name(), s.generateConfigCmd.Name(), s.deleteAccessCmd.Name(), s.serverCmd.Name(), s.usersCmd.Name(), s.versionCmd.Name())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if required flags are set
|
|
|
|
@ -67,15 +84,19 @@ func (s *StateCLI) checkRequirements() {
|
|
|
|
|
if s.helpFlag {
|
|
|
|
|
switch {
|
|
|
|
|
case s.addAccessCmd.Parsed():
|
|
|
|
|
fmt.Printf("Add new user access token\n\n")
|
|
|
|
|
fmt.Printf("Add new user access resource\n\n")
|
|
|
|
|
s.addAccessCmd.PrintDefaults()
|
|
|
|
|
os.Exit(0)
|
|
|
|
|
case s.updateAccessCmd.Parsed():
|
|
|
|
|
fmt.Printf("Update user access resource\n\n")
|
|
|
|
|
s.updateAccessCmd.PrintDefaults()
|
|
|
|
|
os.Exit(0)
|
|
|
|
|
case s.generateConfigCmd.Parsed():
|
|
|
|
|
fmt.Printf("Generate new configuration\n\n")
|
|
|
|
|
s.generateConfigCmd.PrintDefaults()
|
|
|
|
|
os.Exit(0)
|
|
|
|
|
case s.deleteAccessCmd.Parsed():
|
|
|
|
|
fmt.Printf("Delete user access token\n\n")
|
|
|
|
|
fmt.Printf("Delete user access resource\n\n")
|
|
|
|
|
s.deleteAccessCmd.PrintDefaults()
|
|
|
|
|
os.Exit(0)
|
|
|
|
|
case s.serverCmd.Parsed():
|
|
|
|
@ -111,6 +132,12 @@ func (s *StateCLI) checkRequirements() {
|
|
|
|
|
s.addAccessCmd.PrintDefaults()
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
case s.updateAccessCmd.Parsed():
|
|
|
|
|
if s.userIDFlag == "" && s.accessIDFlag == "" {
|
|
|
|
|
fmt.Printf("User ID or access ID should be specified\n\n")
|
|
|
|
|
s.updateAccessCmd.PrintDefaults()
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
case s.deleteAccessCmd.Parsed():
|
|
|
|
|
if s.userIDFlag == "" && s.accessIDFlag == "" {
|
|
|
|
|
fmt.Printf("User or access ID flag should be specified\n\n")
|
|
|
|
@ -146,6 +173,7 @@ func (s *StateCLI) checkRequirements() {
|
|
|
|
|
func (s *StateCLI) populateCLI() {
|
|
|
|
|
// Subcommands setup
|
|
|
|
|
s.addAccessCmd = flag.NewFlagSet("add-access", flag.ExitOnError)
|
|
|
|
|
s.updateAccessCmd = flag.NewFlagSet("update-access", flag.ExitOnError)
|
|
|
|
|
s.generateConfigCmd = flag.NewFlagSet("generate-config", flag.ExitOnError)
|
|
|
|
|
s.deleteAccessCmd = flag.NewFlagSet("delete-access", flag.ExitOnError)
|
|
|
|
|
s.serverCmd = flag.NewFlagSet("server", flag.ExitOnError)
|
|
|
|
@ -153,22 +181,29 @@ func (s *StateCLI) populateCLI() {
|
|
|
|
|
s.versionCmd = flag.NewFlagSet("version", flag.ExitOnError)
|
|
|
|
|
|
|
|
|
|
// Common flag pointers
|
|
|
|
|
for _, fs := range []*flag.FlagSet{s.addAccessCmd, s.generateConfigCmd, s.deleteAccessCmd, s.serverCmd, s.usersCmd, s.versionCmd} {
|
|
|
|
|
for _, fs := range []*flag.FlagSet{s.addAccessCmd, s.updateAccessCmd, s.generateConfigCmd, s.deleteAccessCmd, s.serverCmd, s.usersCmd, s.versionCmd} {
|
|
|
|
|
fs.BoolVar(&s.helpFlag, "help", false, "Show help message")
|
|
|
|
|
fs.StringVar(&s.configPathFlag, "config", "", "Path to configuration file (default: ~/.nodebalancer/config.txt)")
|
|
|
|
|
fs.StringVar(&s.configPathFlag, "config", "", "Path to configuration file (default: ~/.nodebalancer/config.json)")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add, delete and list user access subcommand flag pointers
|
|
|
|
|
for _, fs := range []*flag.FlagSet{s.addAccessCmd, s.deleteAccessCmd, s.usersCmd} {
|
|
|
|
|
for _, fs := range []*flag.FlagSet{s.addAccessCmd, s.updateAccessCmd, s.deleteAccessCmd, s.usersCmd} {
|
|
|
|
|
fs.StringVar(&s.userIDFlag, "user-id", "", "Bugout user ID")
|
|
|
|
|
fs.StringVar(&s.accessIDFlag, "access-id", "", "UUID for access identification")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add user access subcommand flag pointers
|
|
|
|
|
s.addAccessCmd.StringVar(&s.accessNameFlag, "name", "", "Name of access")
|
|
|
|
|
s.addAccessCmd.StringVar(&s.accessDescriptionFlag, "description", "", "Description of access")
|
|
|
|
|
s.addAccessCmd.BoolVar(&s.blockchainAccessFlag, "blockchain-access", false, "Provide if allow direct access to blockchain nodes")
|
|
|
|
|
s.addAccessCmd.BoolVar(&s.extendedMethodsFlag, "extended-methods", false, "Provide to be able to execute not whitelisted methods")
|
|
|
|
|
// Add/update user access subcommand flag pointers
|
|
|
|
|
for _, fs := range []*flag.FlagSet{s.addAccessCmd, s.updateAccessCmd} {
|
|
|
|
|
fs.StringVar(&s.accessNameFlag, "name", DEFAULT_ACCESS_NAME, fmt.Sprintf("Name of access (default: %s)", DEFAULT_ACCESS_NAME))
|
|
|
|
|
fs.StringVar(&s.accessDescriptionFlag, "description", DEFAULT_ACCESS_DESCRIPTION, fmt.Sprintf("Description of access (default: %s)", DEFAULT_ACCESS_DESCRIPTION))
|
|
|
|
|
fs.BoolVar(&s.blockchainAccessFlag, "blockchain-access", DEFAULT_BLOCKCHAIN_ACCESS, fmt.Sprintf("Specify this flag to grant direct access to blockchain nodes (default: %t)", DEFAULT_BLOCKCHAIN_ACCESS))
|
|
|
|
|
fs.BoolVar(&s.extendedMethodsFlag, "extended-methods", DEFAULT_EXTENDED_METHODS, fmt.Sprintf("Specify this flag to grant execution availability to not whitelisted methods (default: %t)", DEFAULT_EXTENDED_METHODS))
|
|
|
|
|
fs.Int64Var(&s.PeriodDurationFlag, "period-duration", DEFAULT_PERIOD_DURATION, fmt.Sprintf("Access period duration in seconds (default: %d)", DEFAULT_PERIOD_DURATION))
|
|
|
|
|
fs.Int64Var(&s.MaxCallsPerPeriodFlag, "max-calls-per-period", DEFAULT_MAX_CALLS_PER_PERIOD, fmt.Sprintf("Max available calls to node during the period (default: %d)", DEFAULT_MAX_CALLS_PER_PERIOD))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.updateAccessCmd.Int64Var(&s.PeriodStartTsFlag, "period-start-ts", 0, "When period starts in unix timestamp format (default: now)")
|
|
|
|
|
s.updateAccessCmd.Int64Var(&s.CallsPerPeriodFlag, "calls-per-period", 0, "Current number of calls to node during the period (default: 0)")
|
|
|
|
|
|
|
|
|
|
// Server subcommand flag pointers
|
|
|
|
|
s.serverCmd.StringVar(&s.listeningAddrFlag, "host", "127.0.0.1", "Server listening address")
|
|
|
|
@ -198,6 +233,10 @@ func cli() {
|
|
|
|
|
|
|
|
|
|
// Parse subcommands and appropriate FlagSet
|
|
|
|
|
switch os.Args[1] {
|
|
|
|
|
case "generate-config":
|
|
|
|
|
stateCLI.generateConfigCmd.Parse(os.Args[2:])
|
|
|
|
|
stateCLI.checkRequirements()
|
|
|
|
|
|
|
|
|
|
case "add-access":
|
|
|
|
|
stateCLI.addAccessCmd.Parse(os.Args[2:])
|
|
|
|
|
stateCLI.checkRequirements()
|
|
|
|
@ -209,6 +248,11 @@ func cli() {
|
|
|
|
|
Description: stateCLI.accessDescriptionFlag,
|
|
|
|
|
BlockchainAccess: stateCLI.blockchainAccessFlag,
|
|
|
|
|
ExtendedMethods: stateCLI.extendedMethodsFlag,
|
|
|
|
|
|
|
|
|
|
PeriodDuration: stateCLI.PeriodDurationFlag,
|
|
|
|
|
PeriodStartTs: time.Now().Unix(),
|
|
|
|
|
MaxCallsPerPeriod: stateCLI.MaxCallsPerPeriodFlag,
|
|
|
|
|
CallsPerPeriod: 0,
|
|
|
|
|
}
|
|
|
|
|
_, err := bugoutClient.Brood.FindUser(
|
|
|
|
|
NB_CONTROLLER_TOKEN,
|
|
|
|
@ -226,17 +270,128 @@ func cli() {
|
|
|
|
|
fmt.Printf("Unable to create user access, err: %v\n", err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
resource_data, err := json.Marshal(resource.ResourceData)
|
|
|
|
|
resourceData, err := json.Marshal(resource.ResourceData)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("Unable to encode resource %s data interface to json, err: %v\n", resource.Id, err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
var newUserAccess ClientResourceData
|
|
|
|
|
err = json.Unmarshal(resourceData, &newUserAccess)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("Unable to decode resource %s data json to structure, err: %v\n", resource.Id, err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
newUserAccess.ResourceID = resource.Id
|
|
|
|
|
userAccessJson, err := json.Marshal(newUserAccess)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("Unable to encode resource %s data interface to json, err: %v", resource.Id, err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
fmt.Println(string(resource_data))
|
|
|
|
|
fmt.Println(string(userAccessJson))
|
|
|
|
|
|
|
|
|
|
case "generate-config":
|
|
|
|
|
stateCLI.generateConfigCmd.Parse(os.Args[2:])
|
|
|
|
|
case "update-access":
|
|
|
|
|
stateCLI.updateAccessCmd.Parse(os.Args[2:])
|
|
|
|
|
stateCLI.checkRequirements()
|
|
|
|
|
|
|
|
|
|
queryParameters := make(map[string]string)
|
|
|
|
|
if stateCLI.userIDFlag != "" {
|
|
|
|
|
queryParameters["user_id"] = stateCLI.userIDFlag
|
|
|
|
|
}
|
|
|
|
|
if stateCLI.accessIDFlag != "" {
|
|
|
|
|
queryParameters["access_id"] = stateCLI.accessIDFlag
|
|
|
|
|
}
|
|
|
|
|
resources, err := bugoutClient.Brood.GetResources(
|
|
|
|
|
NB_CONTROLLER_TOKEN,
|
|
|
|
|
NB_APPLICATION_ID,
|
|
|
|
|
queryParameters,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("Unable to get Bugout resources, err: %v\n", err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resourcesLen := len(resources.Resources)
|
|
|
|
|
if resourcesLen == 0 {
|
|
|
|
|
fmt.Printf("There are no access resource with provided user-id %s or access-id %s\n", stateCLI.userIDFlag, stateCLI.accessIDFlag)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
if resourcesLen > 1 {
|
|
|
|
|
fmt.Printf("There are several %d access resources with provided user-id %s or access-id %s\n", resourcesLen, stateCLI.userIDFlag, stateCLI.accessIDFlag)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resource := resources.Resources[0]
|
|
|
|
|
resource_data, err := json.Marshal(resource.ResourceData)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("Unable to encode resource %s data interface to json, err: %v\n", resource.Id, err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
var currentUserAccess ClientResourceData
|
|
|
|
|
err = json.Unmarshal(resource_data, ¤tUserAccess)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("Unable to decode resource %s data json to structure, err: %v\n", resource.Id, err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
currentUserAccess.ResourceID = resource.Id
|
|
|
|
|
|
|
|
|
|
// TODO(kompotkot): Since we are using bool flags I moved with ugly solution.
|
|
|
|
|
// Let's find better one when have free time or will re-write flag Set.
|
|
|
|
|
update := make(map[string]interface{})
|
|
|
|
|
if stateCLI.accessNameFlag != currentUserAccess.Name && stateCLI.accessNameFlag != DEFAULT_ACCESS_NAME {
|
|
|
|
|
update["name"] = stateCLI.accessNameFlag
|
|
|
|
|
}
|
|
|
|
|
if stateCLI.accessDescriptionFlag != currentUserAccess.Description && stateCLI.accessDescriptionFlag != DEFAULT_ACCESS_DESCRIPTION {
|
|
|
|
|
update["description"] = stateCLI.accessDescriptionFlag
|
|
|
|
|
}
|
|
|
|
|
if stateCLI.blockchainAccessFlag != currentUserAccess.BlockchainAccess && stateCLI.blockchainAccessFlag != DEFAULT_BLOCKCHAIN_ACCESS {
|
|
|
|
|
update["blockchain_access"] = stateCLI.blockchainAccessFlag
|
|
|
|
|
}
|
|
|
|
|
if stateCLI.extendedMethodsFlag != currentUserAccess.ExtendedMethods && stateCLI.extendedMethodsFlag != DEFAULT_EXTENDED_METHODS {
|
|
|
|
|
update["extended_methods"] = stateCLI.extendedMethodsFlag
|
|
|
|
|
}
|
|
|
|
|
if stateCLI.PeriodDurationFlag != currentUserAccess.PeriodDuration && stateCLI.PeriodDurationFlag != DEFAULT_PERIOD_DURATION {
|
|
|
|
|
update["period_duration"] = stateCLI.PeriodDurationFlag
|
|
|
|
|
}
|
|
|
|
|
if stateCLI.MaxCallsPerPeriodFlag != currentUserAccess.MaxCallsPerPeriod && stateCLI.MaxCallsPerPeriodFlag != DEFAULT_MAX_CALLS_PER_PERIOD {
|
|
|
|
|
update["max_calls_per_period"] = stateCLI.MaxCallsPerPeriodFlag
|
|
|
|
|
}
|
|
|
|
|
if stateCLI.PeriodStartTsFlag != currentUserAccess.PeriodStartTs && stateCLI.PeriodStartTsFlag != 0 {
|
|
|
|
|
update["period_start_ts"] = stateCLI.PeriodStartTsFlag
|
|
|
|
|
}
|
|
|
|
|
if stateCLI.CallsPerPeriodFlag != currentUserAccess.CallsPerPeriod && stateCLI.CallsPerPeriodFlag != 0 {
|
|
|
|
|
update["calls_per_period"] = stateCLI.CallsPerPeriodFlag
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updatedResource, err := bugoutClient.Brood.UpdateResource(
|
|
|
|
|
NB_CONTROLLER_TOKEN,
|
|
|
|
|
resource.Id,
|
|
|
|
|
update,
|
|
|
|
|
[]string{},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("Unable to update Bugout resource, err: %v\n", err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updatedResourceData, err := json.Marshal(updatedResource.ResourceData)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("Unable to encode resource %s data interface to json, err: %v\n", resource.Id, err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
var updatedUserAccess ClientResourceData
|
|
|
|
|
err = json.Unmarshal(updatedResourceData, &updatedUserAccess)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("Unable to decode resource %s data json to structure, err: %v\n", resource.Id, err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
updatedUserAccess.ResourceID = updatedResource.Id
|
|
|
|
|
userAccessJson, err := json.Marshal(updatedUserAccess)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("Unable to marshal user access struct, err: %v\n", err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
fmt.Println(string(userAccessJson))
|
|
|
|
|
|
|
|
|
|
case "delete-access":
|
|
|
|
|
stateCLI.deleteAccessCmd.Parse(os.Args[2:])
|
|
|
|
|
stateCLI.checkRequirements()
|
|
|
|
@ -265,18 +420,19 @@ func cli() {
|
|
|
|
|
fmt.Printf("Unable to delete resource %s, err: %v\n", resource.Id, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
resource_data, err := json.Marshal(deletedResource.ResourceData)
|
|
|
|
|
deletedResourceData, err := json.Marshal(deletedResource.ResourceData)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("Unable to encode resource %s data interface to json, err: %v\n", resource.Id, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
var userAccess ClientResourceData
|
|
|
|
|
err = json.Unmarshal(resource_data, &userAccess)
|
|
|
|
|
var deletedUserAccess ClientResourceData
|
|
|
|
|
err = json.Unmarshal(deletedResourceData, &deletedUserAccess)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("Unable to decode resource %s data json to structure, err: %v\n", resource.Id, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
userAccesses = append(userAccesses, userAccess)
|
|
|
|
|
deletedUserAccess.ResourceID = deletedResource.Id
|
|
|
|
|
userAccesses = append(userAccesses, deletedUserAccess)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
userAccessesJson, err := json.Marshal(userAccesses)
|
|
|
|
|