Fix nodebalancer CLI and removed autogen accesses

pull/1145/head
kompotkot 2024-12-10 14:31:21 +00:00
rodzic 2afc38f730
commit 8bf1915b68
9 zmienionych plików z 909 dodań i 540 usunięć

Wyświetl plik

@ -2,519 +2,275 @@ package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"time"
bugout "github.com/bugout-dev/bugout-go/pkg"
"github.com/google/uuid"
"github.com/urfave/cli/v2"
)
var (
// Storing CLI definitions at server startup
stateCLI StateCLI
var CommonCommands = []*cli.Command{
{
Name: "access",
Usage: "Operations with access IDs as Brood resource",
Flags: []cli.Flag{},
Subcommands: []*cli.Command{
{
Name: "add",
Usage: "Add new user's access ID",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "access-token",
Aliases: []string{"t"},
Usage: "Authorized user access token with granted privileges to create resources in Moonstream Bugout application and sharing read permissions to nodebalancer application user",
Required: true,
},
&cli.StringFlag{
Name: "access-id",
Aliases: []string{"a"},
Usage: "UUID for access identification",
Required: true,
},
&cli.StringFlag{
Name: "user-id",
Aliases: []string{"u"},
Usage: "Bugout user ID",
Required: true,
},
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Usage: "Name of the user or application to work with nodebalancer",
Required: true,
},
&cli.StringFlag{
Name: "description",
Aliases: []string{"d"},
},
&cli.BoolFlag{
Name: "blockchain-access",
Aliases: []string{"b"},
Usage: "Specify this flag to grant direct access to blockchain nodes",
},
&cli.BoolFlag{
Name: "extended-methods",
Aliases: []string{"e"},
Usage: "Specify this flag to grant execution availability to not whitelisted methods",
},
&cli.UintFlag{
Name: "period-duration",
Aliases: []string{"p"},
Usage: "Access period duration in seconds",
Value: 2592000,
},
&cli.UintFlag{
Name: "max-calls-per-period",
Aliases: []string{"m"},
Usage: "Max available calls to node during the period",
Value: 10000,
},
},
Before: func(c *cli.Context) error {
periodDurationFlag := c.Uint("period-duration")
bugoutClient bugout.BugoutClient
if periodDurationFlag < 3600 {
return fmt.Errorf("time for --period-duration should be greater then 1 hour")
}
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)
)
return nil
},
Action: func(c *cli.Context) error {
var clientErr error
bugoutClient, clientErr = CreateBugoutClient()
if clientErr != nil {
return fmt.Errorf("an error occurred during Bugout client creation, err: %v", clientErr)
}
// Command Line Interface state
type StateCLI struct {
addAccessCmd *flag.FlagSet
updateAccessCmd *flag.FlagSet
generateConfigCmd *flag.FlagSet
deleteAccessCmd *flag.FlagSet
serverCmd *flag.FlagSet
usersCmd *flag.FlagSet
versionCmd *flag.FlagSet
newAccess, newErr := AddNewAccess(c.String("access-id"), c.String("user-id"), c.String("name"), c.String("description"), c.Bool("blockchain-access"), c.Bool("extended-methods"), c.Uint("period-duration"), c.Uint("max-calls-per-period"), c.String("access-token"))
if newErr != nil {
return fmt.Errorf("failed to add new access, err: %v", newErr)
}
// Common flags
configPathFlag string
helpFlag bool
_, shareErr := ShareAccess(newAccess.ResourceID, NB_CONTROLLER_USER_ID, "user", DEFAULT_AUTOGENERATED_USER_PERMISSIONS, c.String("access-token"))
if shareErr != nil {
return fmt.Errorf("failed to share access to resource ID %s with nodebalancer application user, err: %v", newAccess.ResourceID, shareErr)
}
// Add/update user access flags
userIDFlag string
accessIDFlag string
accessNameFlag string
accessDescriptionFlag string
newAccessJson, err := json.Marshal(newAccess)
if err != nil {
return fmt.Errorf("unable to encode resource %s data interface to json, err: %v", newAccess.ResourceID, err)
}
fmt.Println(string(newAccessJson))
blockchainAccessFlag bool
extendedMethodsFlag bool
PeriodDurationFlag int64
MaxCallsPerPeriodFlag int64
// Update user access flags
PeriodStartTsFlag int64
CallsPerPeriodFlag int64
// Server flags
listeningAddrFlag string
listeningPortFlag string
enableHealthCheckFlag bool
enableDebugFlag bool
// Users list flags
limitFlag int
offsetFlag int
}
func (s *StateCLI) usage() {
fmt.Printf(`usage: nodebalancer [-h] {%[1]s,%[2]s,%[3]s,%[4]s,%[5]s,%[6]s} ...
Moonstream node balancer CLI
optional arguments:
-h, --help show this help message and exit
subcommands:
{%[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
func (s *StateCLI) checkRequirements() {
if s.helpFlag {
switch {
case s.addAccessCmd.Parsed():
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 resource\n\n")
s.deleteAccessCmd.PrintDefaults()
os.Exit(0)
case s.serverCmd.Parsed():
fmt.Printf("Start nodebalancer server\n\n")
s.serverCmd.PrintDefaults()
os.Exit(0)
case s.usersCmd.Parsed():
fmt.Printf("List user access tokens\n\n")
s.usersCmd.PrintDefaults()
os.Exit(0)
case s.versionCmd.Parsed():
fmt.Printf("Show version\n\n")
s.versionCmd.PrintDefaults()
os.Exit(0)
default:
s.usage()
os.Exit(0)
}
}
switch {
case s.addAccessCmd.Parsed():
if s.userIDFlag == "" {
fmt.Printf("User ID should be specified\n\n")
s.addAccessCmd.PrintDefaults()
os.Exit(1)
}
if s.accessIDFlag == "" {
s.accessIDFlag = uuid.New().String()
}
if s.accessNameFlag == "" {
fmt.Printf("Access name should be specified\n\n")
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")
s.deleteAccessCmd.PrintDefaults()
os.Exit(1)
}
case s.usersCmd.Parsed():
if s.offsetFlag < 0 || s.limitFlag < 0 {
fmt.Printf("Offset and limit flags should be greater then zero\n\n")
s.usersCmd.PrintDefaults()
os.Exit(1)
}
}
// Load configuration
config, err := GetConfigPath(s.configPathFlag)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if !config.ConfigExists {
if err := GenerateDefaultConfig(config); err != nil {
fmt.Println(err)
os.Exit(1)
}
} else {
log.Printf("Loaded configuration from %s", config.ConfigPath)
}
s.configPathFlag = config.ConfigPath
}
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)
s.usersCmd = flag.NewFlagSet("users", flag.ExitOnError)
s.versionCmd = flag.NewFlagSet("version", flag.ExitOnError)
// Common flag pointers
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.json)")
}
// Add, delete and list user access subcommand flag pointers
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/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")
s.serverCmd.StringVar(&s.listeningPortFlag, "port", "8544", "Server listening port")
s.serverCmd.BoolVar(&s.enableHealthCheckFlag, "healthcheck", false, "To enable healthcheck set healthcheck flag")
s.serverCmd.BoolVar(&s.enableDebugFlag, "debug", false, "To enable debug mode with extended log set debug flag")
// Users list subcommand flag pointers
s.usersCmd.IntVar(&s.limitFlag, "limit", 10, "Output result limit")
s.usersCmd.IntVar(&s.offsetFlag, "offset", 0, "Result output offset")
}
func cli() {
stateCLI.populateCLI()
if len(os.Args) < 2 {
stateCLI.usage()
os.Exit(1)
}
// Init bugout client
bc, err := CreateBugoutClient()
if err != nil {
log.Printf("An error occurred during Bugout client creation: %v", err)
os.Exit(1)
}
bugoutClient = bc
// 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()
proposedClientResourceData := ClientResourceData{
UserID: stateCLI.userIDFlag,
AccessID: stateCLI.accessIDFlag,
Name: stateCLI.accessNameFlag,
Description: stateCLI.accessDescriptionFlag,
BlockchainAccess: stateCLI.blockchainAccessFlag,
ExtendedMethods: stateCLI.extendedMethodsFlag,
PeriodDuration: stateCLI.PeriodDurationFlag,
PeriodStartTs: time.Now().Unix(),
MaxCallsPerPeriod: stateCLI.MaxCallsPerPeriodFlag,
CallsPerPeriod: 0,
Type: BUGOUT_RESOURCE_TYPE_NODEBALANCER_ACCESS,
}
_, err := bugoutClient.Brood.FindUser(
NB_CONTROLLER_TOKEN,
map[string]string{
"user_id": proposedClientResourceData.UserID,
"application_id": MOONSTREAM_APPLICATION_ID,
return nil
},
},
)
if err != nil {
fmt.Printf("User does not exists, err: %v\n", err)
os.Exit(1)
}
resource, err := bugoutClient.Brood.CreateResource(NB_CONTROLLER_TOKEN, MOONSTREAM_APPLICATION_ID, proposedClientResourceData)
if err != nil {
fmt.Printf("Unable to create user access, err: %v\n", err)
os.Exit(1)
}
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 ClientAccess
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(userAccessJson))
{
Name: "update",
Usage: "Update user's access ID",
Flags: []cli.Flag{},
Before: func(c *cli.Context) error { return nil },
Action: func(c *cli.Context) error { return nil },
},
{
Name: "delete",
Usage: "Delete user's access ID",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "access-token",
Aliases: []string{"t"},
Usage: "Authorized user access token with granted privileges to delete resources in Moonstream Bugout application",
Required: true,
},
&cli.StringFlag{
Name: "access-id",
Aliases: []string{"a"},
Usage: "UUID for access identification",
},
&cli.StringFlag{
Name: "user-id",
Aliases: []string{"u"},
Usage: "Filter by user_id",
},
},
Before: func(c *cli.Context) error {
accessIdFlag := c.String("access-id")
userIdFlag := c.String("user-id")
case "update-access":
stateCLI.updateAccessCmd.Parse(os.Args[2:])
stateCLI.checkRequirements()
if accessIdFlag == "" && userIdFlag == "" {
return fmt.Errorf("at least one of --access-id or --user-id should be set")
}
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,
MOONSTREAM_APPLICATION_ID,
queryParameters,
)
if err != nil {
fmt.Printf("Unable to get Bugout resources, err: %v\n", err)
os.Exit(1)
}
return nil
},
Action: func(c *cli.Context) error {
var clientErr error
bugoutClient, clientErr = CreateBugoutClient()
if clientErr != nil {
return fmt.Errorf("an error occurred during Bugout client creation: %v", clientErr)
}
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)
}
resources, getResErr := GetAccesses(c.String("access-id"), c.String("user-id"), c.String("access-token"))
if getResErr != nil {
return fmt.Errorf("unable to get Bugout resources, err: %v", getResErr)
}
resource := resources.Resources[0]
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)
}
if len(resources.Resources) == 0 {
fmt.Println("[]")
return nil
}
var currentClientAccess ClientAccess
currentClientAccess.ResourceID = resource.Id
err = json.Unmarshal(resourceData, &currentClientAccess.ClientResourceData)
if err != nil {
fmt.Printf("Unable to decode resource %s data json to structure, err: %v\n", resource.Id, err)
os.Exit(1)
}
var clientAccesses []ClientAccess
for _, resource := range resources.Resources {
clientAccess, parseErr := ParseResourceDataToClientAccess(resource)
if parseErr != nil {
fmt.Printf("Unable to parse resource data, err: %v", parseErr)
continue
}
// 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 != currentClientAccess.ClientResourceData.Name && stateCLI.accessNameFlag != DEFAULT_ACCESS_NAME {
update["name"] = stateCLI.accessNameFlag
}
if stateCLI.accessDescriptionFlag != currentClientAccess.ClientResourceData.Description && stateCLI.accessDescriptionFlag != DEFAULT_ACCESS_DESCRIPTION {
update["description"] = stateCLI.accessDescriptionFlag
}
if stateCLI.blockchainAccessFlag != currentClientAccess.ClientResourceData.BlockchainAccess && stateCLI.blockchainAccessFlag != DEFAULT_BLOCKCHAIN_ACCESS {
update["blockchain_access"] = stateCLI.blockchainAccessFlag
}
if stateCLI.extendedMethodsFlag != currentClientAccess.ClientResourceData.ExtendedMethods && stateCLI.extendedMethodsFlag != DEFAULT_EXTENDED_METHODS {
update["extended_methods"] = stateCLI.extendedMethodsFlag
}
if stateCLI.PeriodDurationFlag != currentClientAccess.ClientResourceData.PeriodDuration && stateCLI.PeriodDurationFlag != DEFAULT_PERIOD_DURATION {
update["period_duration"] = stateCLI.PeriodDurationFlag
}
if stateCLI.MaxCallsPerPeriodFlag != currentClientAccess.ClientResourceData.MaxCallsPerPeriod && stateCLI.MaxCallsPerPeriodFlag != DEFAULT_MAX_CALLS_PER_PERIOD {
update["max_calls_per_period"] = stateCLI.MaxCallsPerPeriodFlag
}
if stateCLI.PeriodStartTsFlag != currentClientAccess.ClientResourceData.PeriodStartTs && stateCLI.PeriodStartTsFlag != 0 {
update["period_start_ts"] = stateCLI.PeriodStartTsFlag
}
if stateCLI.CallsPerPeriodFlag != currentClientAccess.ClientResourceData.CallsPerPeriod && stateCLI.CallsPerPeriodFlag != 0 {
update["calls_per_period"] = stateCLI.CallsPerPeriodFlag
}
clientAccesses = append(clientAccesses, *clientAccess)
}
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)
}
for _, access := range clientAccesses {
fmt.Printf("Deleting resource ID %s with name %s in 3 seconds..\n", access.ResourceID, access.ClientResourceData.Name)
time.Sleep(3 * time.Second)
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 ClientAccess
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))
_, delErr := DeleteAccess(access.ResourceID, c.String("access-token"))
if delErr != nil {
fmt.Printf("Failed to delete resource with ID %s err: %v\n", access.ResourceID, delErr)
continue
}
}
case "delete-access":
stateCLI.deleteAccessCmd.Parse(os.Args[2:])
stateCLI.checkRequirements()
return nil
},
},
{
Name: "list",
Usage: "List all user access IDs",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "access-token",
Aliases: []string{"t"},
Usage: "Authorized user access token with granted privileges to get resources in Moonstream Bugout application",
Required: true,
},
&cli.StringFlag{
Name: "access-id",
Aliases: []string{"a"},
Usage: "Filter by access_id",
},
&cli.StringFlag{
Name: "user-id",
Aliases: []string{"u"},
Usage: "Filter by user_id",
},
},
Action: func(c *cli.Context) error {
var clientErr error
bugoutClient, clientErr = CreateBugoutClient()
if clientErr != nil {
return fmt.Errorf("an error occurred during Bugout client creation: %v", clientErr)
}
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,
MOONSTREAM_APPLICATION_ID,
queryParameters,
)
if err != nil {
fmt.Printf("Unable to get Bugout resources, err: %v\n", err)
os.Exit(1)
}
resources, getResErr := GetAccesses(c.String("access-id"), c.String("user-id"), c.String("access-token"))
if getResErr != nil {
return fmt.Errorf("unable to get Bugout resources, err: %v", getResErr)
}
var userAccesses []ClientAccess
for _, resource := range resources.Resources {
deletedResource, err := bugoutClient.Brood.DeleteResource(NB_CONTROLLER_TOKEN, resource.Id)
if err != nil {
fmt.Printf("Unable to delete resource %s, err: %v\n", resource.Id, err)
continue
}
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 deletedUserAccess ClientAccess
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
}
deletedUserAccess.ResourceID = deletedResource.Id
userAccesses = append(userAccesses, deletedUserAccess)
}
var clientAccesses []ClientAccess
for _, resource := range resources.Resources {
clientAccess, parseErr := ParseResourceDataToClientAccess(resource)
if parseErr != nil {
fmt.Printf("Unable to parse resource data, err: %v", parseErr)
continue
}
userAccessesJson, err := json.Marshal(userAccesses)
if err != nil {
fmt.Printf("Unable to marshal user access struct, err: %v\n", err)
os.Exit(1)
}
fmt.Println(string(userAccessesJson))
clientAccesses = append(clientAccesses, *clientAccess)
}
case "server":
stateCLI.serverCmd.Parse(os.Args[2:])
stateCLI.checkRequirements()
userAccessesJson, marErr := json.Marshal(clientAccesses)
if marErr != nil {
return fmt.Errorf("unable to marshal user accesses struct, err: %v", marErr)
}
fmt.Println(string(userAccessesJson))
CheckEnvVarSet()
return nil
},
},
},
},
{
Name: "configure",
Usage: "Generate nodebalancer configuration",
Action: func(cCtx *cli.Context) error {
Server()
return nil
},
},
{
Name: "server",
Usage: "Start nodebalancer server",
Action: func(cCtx *cli.Context) error {
case "users":
stateCLI.usersCmd.Parse(os.Args[2:])
stateCLI.checkRequirements()
return nil
},
},
{
Name: "version",
Usage: "Shows nodebalancer package version",
Action: func(cCtx *cli.Context) error {
fmt.Println(NB_VERSION)
return nil
},
},
}
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,
MOONSTREAM_APPLICATION_ID,
queryParameters,
)
if err != nil {
fmt.Printf("Unable to get Bugout resources, err: %v\n", err)
os.Exit(1)
}
var clientAccesses []ClientAccess
offset := stateCLI.offsetFlag
if stateCLI.offsetFlag > len(resources.Resources) {
offset = len(resources.Resources)
}
limit := stateCLI.offsetFlag + stateCLI.limitFlag
if limit > len(resources.Resources) {
limit = len(resources.Resources[offset:]) + offset
}
for _, resource := range resources.Resources[offset:limit] {
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)
continue
}
var clientAccess ClientAccess
clientAccess.ResourceID = resource.Id
err = json.Unmarshal(resourceData, &clientAccess.ClientResourceData)
if err != nil {
fmt.Printf("Unable to decode resource %s data json to structure, err: %v\n", resource.Id, err)
continue
}
clientAccesses = append(clientAccesses, clientAccess)
}
userAccessesJson, err := json.Marshal(clientAccesses)
if err != nil {
fmt.Printf("Unable to marshal user accesses struct, err: %v\n", err)
os.Exit(1)
}
fmt.Println(string(userAccessesJson))
case "version":
stateCLI.versionCmd.Parse(os.Args[2:])
stateCLI.checkRequirements()
fmt.Printf("v%s\n", NB_VERSION)
default:
stateCLI.usage()
os.Exit(1)
func NodebalancerAppCli() *cli.App {
return &cli.App{
Name: "nodebalancer",
Version: fmt.Sprintf("v%s", NB_VERSION),
Usage: "Web3 node balancer",
EnableBashCompletion: true,
Commands: CommonCommands,
}
}

Wyświetl plik

@ -0,0 +1,517 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"time"
"github.com/google/uuid"
)
var (
// Storing CLI definitions at server startup
stateCLI StateCLI
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
usersCmd *flag.FlagSet
versionCmd *flag.FlagSet
// Common flags
configPathFlag string
helpFlag bool
// Add/update user access flags
userIDFlag string
accessIDFlag string
accessNameFlag string
accessDescriptionFlag string
blockchainAccessFlag bool
extendedMethodsFlag bool
PeriodDurationFlag int64
MaxCallsPerPeriodFlag int64
// Update user access flags
PeriodStartTsFlag int64
CallsPerPeriodFlag int64
// Server flags
listeningAddrFlag string
listeningPortFlag string
enableHealthCheckFlag bool
enableDebugFlag bool
// Users list flags
limitFlag int
offsetFlag int
}
func (s *StateCLI) usage() {
fmt.Printf(`usage: nodebalancer [-h] {%[1]s,%[2]s,%[3]s,%[4]s,%[5]s,%[6]s} ...
Moonstream node balancer CLI
optional arguments:
-h, --help show this help message and exit
subcommands:
{%[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
func (s *StateCLI) checkRequirements() {
if s.helpFlag {
switch {
case s.addAccessCmd.Parsed():
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 resource\n\n")
s.deleteAccessCmd.PrintDefaults()
os.Exit(0)
case s.serverCmd.Parsed():
fmt.Printf("Start nodebalancer server\n\n")
s.serverCmd.PrintDefaults()
os.Exit(0)
case s.usersCmd.Parsed():
fmt.Printf("List user access tokens\n\n")
s.usersCmd.PrintDefaults()
os.Exit(0)
case s.versionCmd.Parsed():
fmt.Printf("Show version\n\n")
s.versionCmd.PrintDefaults()
os.Exit(0)
default:
s.usage()
os.Exit(0)
}
}
switch {
case s.addAccessCmd.Parsed():
if s.userIDFlag == "" {
fmt.Printf("User ID should be specified\n\n")
s.addAccessCmd.PrintDefaults()
os.Exit(1)
}
if s.accessIDFlag == "" {
s.accessIDFlag = uuid.New().String()
}
if s.accessNameFlag == "" {
fmt.Printf("Access name should be specified\n\n")
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")
s.deleteAccessCmd.PrintDefaults()
os.Exit(1)
}
case s.usersCmd.Parsed():
if s.offsetFlag < 0 || s.limitFlag < 0 {
fmt.Printf("Offset and limit flags should be greater then zero\n\n")
s.usersCmd.PrintDefaults()
os.Exit(1)
}
}
// Load configuration
config, err := GetConfigPath(s.configPathFlag)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if !config.ConfigExists {
if err := GenerateDefaultConfig(config); err != nil {
fmt.Println(err)
os.Exit(1)
}
} else {
log.Printf("Loaded configuration from %s", config.ConfigPath)
}
s.configPathFlag = config.ConfigPath
}
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)
s.usersCmd = flag.NewFlagSet("users", flag.ExitOnError)
s.versionCmd = flag.NewFlagSet("version", flag.ExitOnError)
// Common flag pointers
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.json)")
}
// Add, delete and list user access subcommand flag pointers
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/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")
s.serverCmd.StringVar(&s.listeningPortFlag, "port", "8544", "Server listening port")
s.serverCmd.BoolVar(&s.enableHealthCheckFlag, "healthcheck", false, "To enable healthcheck set healthcheck flag")
s.serverCmd.BoolVar(&s.enableDebugFlag, "debug", false, "To enable debug mode with extended log set debug flag")
// Users list subcommand flag pointers
s.usersCmd.IntVar(&s.limitFlag, "limit", 10, "Output result limit")
s.usersCmd.IntVar(&s.offsetFlag, "offset", 0, "Result output offset")
}
func NodebalancerAppCli_OLD() {
stateCLI.populateCLI()
if len(os.Args) < 2 {
stateCLI.usage()
os.Exit(1)
}
// Init bugout client
bc, err := CreateBugoutClient()
if err != nil {
log.Printf("An error occurred during Bugout client creation: %v", err)
os.Exit(1)
}
bugoutClient = bc
// 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()
proposedClientResourceData := ClientResourceData{
UserID: stateCLI.userIDFlag,
AccessID: stateCLI.accessIDFlag,
Name: stateCLI.accessNameFlag,
Description: stateCLI.accessDescriptionFlag,
BlockchainAccess: stateCLI.blockchainAccessFlag,
ExtendedMethods: stateCLI.extendedMethodsFlag,
PeriodDuration: stateCLI.PeriodDurationFlag,
PeriodStartTs: time.Now().Unix(),
MaxCallsPerPeriod: stateCLI.MaxCallsPerPeriodFlag,
CallsPerPeriod: 0,
Type: BUGOUT_RESOURCE_TYPE_NODEBALANCER_ACCESS,
}
_, err := bugoutClient.Brood.FindUser(
NB_CONTROLLER_TOKEN,
map[string]string{
"user_id": proposedClientResourceData.UserID,
"application_id": MOONSTREAM_APPLICATION_ID,
},
)
if err != nil {
fmt.Printf("User does not exists, err: %v\n", err)
os.Exit(1)
}
resource, err := bugoutClient.Brood.CreateResource(NB_CONTROLLER_TOKEN, MOONSTREAM_APPLICATION_ID, proposedClientResourceData)
if err != nil {
fmt.Printf("Unable to create user access, err: %v\n", err)
os.Exit(1)
}
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 ClientAccess
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(userAccessJson))
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,
MOONSTREAM_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]
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 currentClientAccess ClientAccess
currentClientAccess.ResourceID = resource.Id
err = json.Unmarshal(resourceData, &currentClientAccess.ClientResourceData)
if err != nil {
fmt.Printf("Unable to decode resource %s data json to structure, err: %v\n", resource.Id, err)
os.Exit(1)
}
// 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 != currentClientAccess.ClientResourceData.Name && stateCLI.accessNameFlag != DEFAULT_ACCESS_NAME {
update["name"] = stateCLI.accessNameFlag
}
if stateCLI.accessDescriptionFlag != currentClientAccess.ClientResourceData.Description && stateCLI.accessDescriptionFlag != DEFAULT_ACCESS_DESCRIPTION {
update["description"] = stateCLI.accessDescriptionFlag
}
if stateCLI.blockchainAccessFlag != currentClientAccess.ClientResourceData.BlockchainAccess && stateCLI.blockchainAccessFlag != DEFAULT_BLOCKCHAIN_ACCESS {
update["blockchain_access"] = stateCLI.blockchainAccessFlag
}
if stateCLI.extendedMethodsFlag != currentClientAccess.ClientResourceData.ExtendedMethods && stateCLI.extendedMethodsFlag != DEFAULT_EXTENDED_METHODS {
update["extended_methods"] = stateCLI.extendedMethodsFlag
}
if stateCLI.PeriodDurationFlag != currentClientAccess.ClientResourceData.PeriodDuration && stateCLI.PeriodDurationFlag != DEFAULT_PERIOD_DURATION {
update["period_duration"] = stateCLI.PeriodDurationFlag
}
if stateCLI.MaxCallsPerPeriodFlag != currentClientAccess.ClientResourceData.MaxCallsPerPeriod && stateCLI.MaxCallsPerPeriodFlag != DEFAULT_MAX_CALLS_PER_PERIOD {
update["max_calls_per_period"] = stateCLI.MaxCallsPerPeriodFlag
}
if stateCLI.PeriodStartTsFlag != currentClientAccess.ClientResourceData.PeriodStartTs && stateCLI.PeriodStartTsFlag != 0 {
update["period_start_ts"] = stateCLI.PeriodStartTsFlag
}
if stateCLI.CallsPerPeriodFlag != currentClientAccess.ClientResourceData.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 ClientAccess
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()
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,
MOONSTREAM_APPLICATION_ID,
queryParameters,
)
if err != nil {
fmt.Printf("Unable to get Bugout resources, err: %v\n", err)
os.Exit(1)
}
var userAccesses []ClientAccess
for _, resource := range resources.Resources {
deletedResource, err := bugoutClient.Brood.DeleteResource(NB_CONTROLLER_TOKEN, resource.Id)
if err != nil {
fmt.Printf("Unable to delete resource %s, err: %v\n", resource.Id, err)
continue
}
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 deletedUserAccess ClientAccess
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
}
deletedUserAccess.ResourceID = deletedResource.Id
userAccesses = append(userAccesses, deletedUserAccess)
}
userAccessesJson, err := json.Marshal(userAccesses)
if err != nil {
fmt.Printf("Unable to marshal user access struct, err: %v\n", err)
os.Exit(1)
}
fmt.Println(string(userAccessesJson))
case "server":
stateCLI.serverCmd.Parse(os.Args[2:])
stateCLI.checkRequirements()
CheckEnvVarSet()
Server()
case "users":
stateCLI.usersCmd.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,
MOONSTREAM_APPLICATION_ID,
queryParameters,
)
if err != nil {
fmt.Printf("Unable to get Bugout resources, err: %v\n", err)
os.Exit(1)
}
var clientAccesses []ClientAccess
offset := stateCLI.offsetFlag
if stateCLI.offsetFlag > len(resources.Resources) {
offset = len(resources.Resources)
}
limit := stateCLI.offsetFlag + stateCLI.limitFlag
if limit > len(resources.Resources) {
limit = len(resources.Resources[offset:]) + offset
}
for _, resource := range resources.Resources[offset:limit] {
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)
continue
}
var clientAccess ClientAccess
clientAccess.ResourceID = resource.Id
err = json.Unmarshal(resourceData, &clientAccess.ClientResourceData)
if err != nil {
fmt.Printf("Unable to decode resource %s data json to structure, err: %v\n", resource.Id, err)
continue
}
clientAccesses = append(clientAccesses, clientAccess)
}
userAccessesJson, err := json.Marshal(clientAccesses)
if err != nil {
fmt.Printf("Unable to marshal user accesses struct, err: %v\n", err)
os.Exit(1)
}
fmt.Println(string(userAccessesJson))
case "version":
stateCLI.versionCmd.Parse(os.Args[2:])
stateCLI.checkRequirements()
fmt.Printf("v%s\n", NB_VERSION)
default:
stateCLI.usage()
os.Exit(1)
}
}

Wyświetl plik

@ -1,10 +1,14 @@
package main
import (
"encoding/json"
"fmt"
"log"
"reflect"
"sync"
"time"
"github.com/bugout-dev/bugout-go/pkg/brood"
)
var (
@ -188,3 +192,109 @@ func (cpool *ClientPool) CleanInactiveClientNodes() int {
return cnt
}
// Creates new Bugout resource according to nodebalancer type to grant user or application access to call JSON RPC nodes
func AddNewAccess(accessId, userId, name, description string, blockchainAccess, extendedMethods bool, periodDuration, maxCallsPerPeriod uint, accessToken string) (*ClientAccess, error) {
_, findErr := bugoutClient.Brood.FindUser(
accessToken,
map[string]string{
"user_id": userId,
"application_id": MOONSTREAM_APPLICATION_ID,
},
)
if findErr != nil {
return nil, fmt.Errorf("user does not exists, err: %v", findErr)
}
proposedClientResourceData := ClientResourceData{
AccessID: accessId,
UserID: userId,
Name: name,
Description: description,
BlockchainAccess: blockchainAccess,
ExtendedMethods: extendedMethods,
PeriodDuration: int64(periodDuration),
PeriodStartTs: int64(time.Now().Unix()),
MaxCallsPerPeriod: int64(maxCallsPerPeriod),
CallsPerPeriod: 0,
Type: BUGOUT_RESOURCE_TYPE_NODEBALANCER_ACCESS,
}
resource, err := bugoutClient.Brood.CreateResource(accessToken, MOONSTREAM_APPLICATION_ID, proposedClientResourceData)
if err != nil {
return nil, fmt.Errorf("unable to create user access, err: %v", err)
}
resourceData, err := json.Marshal(resource.ResourceData)
if err != nil {
return nil, fmt.Errorf("unable to encode resource %s data interface to json, err: %v", resource.Id, err)
}
var newUserAccess ClientAccess
err = json.Unmarshal(resourceData, &newUserAccess)
if err != nil {
return nil, fmt.Errorf("unable to decode resource %s data json to structure, err: %v", resource.Id, err)
}
newUserAccess.ResourceID = resource.Id
return &newUserAccess, nil
}
func ShareAccess(resourceId, userId, holderType string, permissions []string, accessToken string) (*brood.ResourceHolders, error) {
resourceHolderPermissions, holdErr := bugoutClient.Brood.AddResourceHolderPermissions(
accessToken, resourceId, brood.ResourceHolder{
Id: userId,
HolderType: holderType,
Permissions: permissions,
},
)
if holdErr != nil {
return nil, fmt.Errorf("unable to grant permissions to user with ID %s at resource with ID %s, err: %v", resourceId, userId, holdErr)
}
return &resourceHolderPermissions, nil
}
func GetAccesses(accessId, userId, accessToken string) (*brood.Resources, error) {
queryParameters := map[string]string{
"type": BUGOUT_RESOURCE_TYPE_NODEBALANCER_ACCESS,
}
if userId != "" {
queryParameters["user_id"] = userId
}
if accessId != "" {
queryParameters["access_id"] = accessId
}
resources, getResErr := bugoutClient.Brood.GetResources(accessToken, MOONSTREAM_APPLICATION_ID, queryParameters)
if getResErr != nil {
return nil, fmt.Errorf("unable to get Bugout resources, err: %v", getResErr)
}
return &resources, nil
}
func ParseResourceDataToClientAccess(resource brood.Resource) (*ClientAccess, error) {
resourceData, marErr := json.Marshal(resource.ResourceData)
if marErr != nil {
return nil, fmt.Errorf("unable to encode resource %s data interface to json, err: %v", resource.Id, marErr)
}
var clientAccess ClientAccess
clientAccess.ResourceID = resource.Id
unmarErr := json.Unmarshal(resourceData, &clientAccess.ClientResourceData)
if unmarErr != nil {
return nil, fmt.Errorf("unable to decode resource %s data json to structure, err: %v", resource.Id, unmarErr)
}
return &clientAccess, nil
}
func DeleteAccess(resourceId, accessToken string) (*brood.Resource, error) {
del, delErr := bugoutClient.Brood.DeleteResource(accessToken, resourceId)
if delErr != nil {
return nil, fmt.Errorf("unable to delete resource, err: %v", delErr)
}
return &del, nil
}

Wyświetl plik

@ -23,15 +23,18 @@ var (
supportedBlockchains map[string]bool
bugoutClient *bugout.BugoutClient
// Bugout client
// TODO(kompotkot): Find out why it cuts out the port
BUGOUT_BROOD_URL = "https://auth.bugout.dev"
// BUGOUT_BROOD_URL = os.Getenv("BUGOUT_BROOD_URL")
// BUGOUT_BROOD_URL = "https://auth.bugout.dev"
BUGOUT_BROOD_URL = os.Getenv("BUGOUT_BROOD_URL")
NB_BUGOUT_TIMEOUT_SECONDS_RAW = os.Getenv("NB_BUGOUT_TIMEOUT_SECONDS")
// Bugout and application configuration
BUGOUT_AUTH_CALL_TIMEOUT = time.Second * 5
MOONSTREAM_APPLICATION_ID = os.Getenv("MOONSTREAM_APPLICATION_ID")
NB_CONTROLLER_USER_ID = os.Getenv("NB_CONTROLLER_USER_ID")
NB_CONTROLLER_TOKEN = os.Getenv("NB_CONTROLLER_TOKEN")
NB_CONTROLLER_ACCESS_ID = os.Getenv("NB_CONTROLLER_ACCESS_ID")
MOONSTREAM_CORS_ALLOWED_ORIGINS = os.Getenv("MOONSTREAM_CORS_ALLOWED_ORIGINS")
@ -64,15 +67,15 @@ var (
DEFAULT_AUTOGENERATED_MAX_CALLS_PER_PERIOD = int64(1000)
)
func CreateBugoutClient() (bugout.BugoutClient, error) {
func CreateBugoutClient() (*bugout.BugoutClient, error) {
bugoutTimeoutSeconds, err := strconv.Atoi(NB_BUGOUT_TIMEOUT_SECONDS_RAW)
if err != nil {
return bugout.BugoutClient{}, fmt.Errorf("unable to parse environment variable as integer: %v", err)
return nil, fmt.Errorf("unable to parse environment variable as integer: %v", err)
}
NB_BUGOUT_TIMEOUT_SECONDS := time.Duration(bugoutTimeoutSeconds) * time.Second
broodClient := bugout.ClientBrood(BUGOUT_BROOD_URL, NB_BUGOUT_TIMEOUT_SECONDS)
return broodClient, nil
bugoutClient := bugout.ClientBrood(BUGOUT_BROOD_URL, NB_BUGOUT_TIMEOUT_SECONDS)
return &bugoutClient, nil
}
func CheckEnvVarSet() {

Wyświetl plik

@ -1,5 +1,13 @@
package main
import (
"log"
"os"
)
func main() {
cli()
app := NodebalancerAppCli()
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}

Wyświetl plik

@ -234,52 +234,9 @@ func fetchClientAccessFromResources(accessID, authorizationToken string, tsNow i
}
if len(resources.Resources) == 0 {
if authorizationToken != "" {
// Generate new autogenerated access resource with default parameters and grant user permissions to work with it
user, err := bugoutClient.Brood.GetUser(authorizationToken)
if err != nil {
log.Printf("Unable to get user, err: %v", err)
return nil, fmt.Errorf("unable to find user with provided authorization token")
}
newResource, err := bugoutClient.Brood.CreateResource(
NB_CONTROLLER_TOKEN, MOONSTREAM_APPLICATION_ID, ClientResourceData{
UserID: user.Id,
AccessID: uuid.New().String(),
Name: user.Username,
Description: "Autogenerated access ID",
BlockchainAccess: true,
ExtendedMethods: false,
PeriodDuration: DEFAULT_AUTOGENERATED_PERIOD_DURATION,
PeriodStartTs: tsNow,
MaxCallsPerPeriod: DEFAULT_AUTOGENERATED_MAX_CALLS_PER_PERIOD,
CallsPerPeriod: 0,
Type: BUGOUT_RESOURCE_TYPE_NODEBALANCER_ACCESS,
},
)
if err != nil {
log.Printf("Unable to create resource with autogenerated access for user with ID %s, err: %v", user.Id, err)
return nil, fmt.Errorf("unable to create resource with autogenerated access for user")
}
resourceHolderPermissions, err := bugoutClient.Brood.AddResourceHolderPermissions(
NB_CONTROLLER_TOKEN, newResource.Id, brood.ResourceHolder{
Id: user.Id,
HolderType: "user",
Permissions: DEFAULT_AUTOGENERATED_USER_PERMISSIONS,
},
)
if err != nil {
log.Printf("Unable to grant permissions to user with ID %s at resource with ID %s, err: %v", newResource.Id, user.Id, err)
return nil, fmt.Errorf("unable to create resource with autogenerated access for user")
}
log.Printf("Created new resource with ID %s with autogenerated access for user with ID %s", resourceHolderPermissions.ResourceId, user.Id)
resources.Resources = append(resources.Resources, newResource)
} else {
return nil, fmt.Errorf("there are no provided access identifier")
}
// Generate new autogenerated access resource with default parameters and grant user permissions to work with it
// TODO(kompotkot): Not working because of permissions models change at Brood layer
return nil, fmt.Errorf("there are no provided access identifier")
} else if len(resources.Resources) > 1 {
// TODO(kompotkot): Write support of multiple resources, be careful, because NB_CONTROLLER has several resources
return nil, fmt.Errorf("there are no provided access identifier")

Wyświetl plik

@ -6,4 +6,11 @@ require (
github.com/bugout-dev/bugout-go v0.4.3
github.com/bugout-dev/humbug/go v0.0.0-20211206230955-57607cd2d205
github.com/google/uuid v1.3.0
github.com/urfave/cli/v2 v2.27.5
)
require (
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
)

Wyświetl plik

@ -12,6 +12,7 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@ -35,6 +36,8 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
@ -142,6 +145,8 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@ -163,7 +168,11 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@ -282,6 +291,7 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

Wyświetl plik

@ -3,8 +3,9 @@ export BUGOUT_BROOD_URL="https://auth.bugout.dev"
export NB_BUGOUT_TIMEOUT_SECONDS=15
export MOONSTREAM_APPLICATION_ID="<application_id_to_controll_access>"
export MOONSTREAM_CORS_ALLOWED_ORIGINS="http://localhost:3000,https://moonstream.to,https://portal.moonstream.to"
export NB_CONTROLLER_TOKEN="<token_of_controller_user>"
export NB_CONTROLLER_ACCESS_ID="<controller_access_id_for_internal_usage>"
export NB_CONTROLLER_USER_ID="<bugout_id_of_nodebalancer_user>"
export NB_CONTROLLER_TOKEN="<token_of_nodebalancer_user>"
export NB_CONTROLLER_ACCESS_ID="<nodebalancer_access_id_for_internal_usage>"
# Error humbug reporter
export HUMBUG_REPORTER_NODE_BALANCER_TOKEN="<bugout_humbug_token_for_crash_reports>"