From 8a4ecafe8ffcd6c677edc1ce1171bfb05d3a7a0d Mon Sep 17 00:00:00 2001 From: Javier Marcos <1271349+javuto@users.noreply.github.com> Date: Tue, 8 Nov 2022 09:57:17 +0100 Subject: [PATCH] Login in osctrl-api --- .gitignore | 3 ++ api/handlers-login.go | 82 +++++++++++++++++++++++++++++++++ api/main.go | 5 ++ cli/api-login.go | 32 +++++++++++++ cli/api.go | 22 ++++++++- cli/main.go | 105 ++++++++++++++++++++++++++++++++++++------ cli/node.go | 28 +++++------ cli/permission.go | 24 +++++----- cli/query.go | 30 ++++++------ cli/settings.go | 6 +-- cli/user.go | 40 ++++++++-------- go.mod | 1 + go.sum | 2 + types/types.go | 11 +++++ 14 files changed, 313 insertions(+), 78 deletions(-) create mode 100644 api/handlers-login.go create mode 100644 cli/api-login.go diff --git a/.gitignore b/.gitignore index 6990c7b3..c4c773ba 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,9 @@ notes.txt # Tests coverage.out +# Configuration +osctrl-api.json + # Go Workspace go.work go.work.sum diff --git a/api/handlers-login.go b/api/handlers-login.go new file mode 100644 index 00000000..ef0345a1 --- /dev/null +++ b/api/handlers-login.go @@ -0,0 +1,82 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + + "github.com/gorilla/mux" + "github.com/jmpsec/osctrl/settings" + "github.com/jmpsec/osctrl/types" + "github.com/jmpsec/osctrl/users" + "github.com/jmpsec/osctrl/utils" +) + +const ( + metricAPILoginReq = "login-req" + metricAPILoginErr = "login-err" + metricAPILoginOK = "login-ok" +) + +// POST Handler for API login request +func apiLoginHandler(w http.ResponseWriter, r *http.Request) { + incMetric(metricAPILoginReq) + utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAPI), false) + vars := mux.Vars(r) + // Extract environment + envVar, ok := vars["env"] + if !ok { + apiErrorResponse(w, "error with environment", http.StatusInternalServerError, nil) + incMetric(metricAPILoginErr) + return + } + // Get environment + env, err := envs.Get(envVar) + if err != nil { + apiErrorResponse(w, "error getting environment", http.StatusInternalServerError, nil) + incMetric(metricAPILoginErr) + return + } + var l types.ApiLoginRequest + // Parse request JSON body + if err := json.NewDecoder(r.Body).Decode(&l); err != nil { + apiErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err) + incMetric(metricAPILoginErr) + return + } + // Check credentials + access, user := apiUsers.CheckLoginCredentials(l.Username, l.Password) + if !access { + apiErrorResponse(w, "invalid credentials", http.StatusForbidden, err) + incMetric(metricAPILoginErr) + return + } + // Check if user has access to this environment + if !apiUsers.CheckPermissions(l.Username, users.AdminLevel, env.UUID) { + apiErrorResponse(w, "no access", http.StatusForbidden, fmt.Errorf("attempt to use API by user %s", l.Username)) + incMetric(metricAPILoginErr) + return + } + // Do we have a token already? + if user.APIToken == "" { + token, exp, err := apiUsers.CreateToken(l.Username) + if err != nil { + apiErrorResponse(w, "error creating token", http.StatusInternalServerError, err) + incMetric(metricAPILoginErr) + return + } + if err = apiUsers.UpdateToken(l.Username, token, exp); err != nil { + apiErrorResponse(w, "error updating token", http.StatusInternalServerError, err) + incMetric(metricAPILoginErr) + return + } + user.APIToken = token + } + // Serialize and serve JSON + if settingsmgr.DebugService(settings.ServiceAPI) { + log.Printf("DebugService: Returning token for %s", user.Username) + } + utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, types.ApiLoginResponse{Token: user.APIToken}) + incMetric(metricAPILoginOK) +} diff --git a/api/main.go b/api/main.go index 1e61f713..e08598f3 100644 --- a/api/main.go +++ b/api/main.go @@ -64,6 +64,8 @@ const ( apiPrefixPath = "/api" // API version path apiVersionPath = "/v1" + // API login path + apiLoginPath = "/login" // API nodes path apiNodesPath = "/nodes" // API queries path @@ -481,6 +483,9 @@ func osctrlAPIService() { // API: forbidden routerAPI.HandleFunc(forbiddenPath, forbiddenHTTPHandler).Methods("GET") + // ///////////////////////// UNAUTHENTICATED + routerAPI.Handle(_apiPath(apiLoginPath)+"/{env}", handlerAuthCheck(http.HandlerFunc(apiLoginHandler))).Methods("POST") + routerAPI.Handle(_apiPath(apiLoginPath)+"/{env}/", handlerAuthCheck(http.HandlerFunc(apiLoginHandler))).Methods("POST") // ///////////////////////// AUTHENTICATED // API: nodes by environment routerAPI.Handle(_apiPath(apiNodesPath)+"/{env}/node/{node}", handlerAuthCheck(http.HandlerFunc(apiNodeHandler))).Methods("GET") diff --git a/cli/api-login.go b/cli/api-login.go new file mode 100644 index 00000000..4e8ae969 --- /dev/null +++ b/cli/api-login.go @@ -0,0 +1,32 @@ +package main + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/jmpsec/osctrl/types" +) + +// PostLogin to login into API to retrieve a token +func (api *OsctrlAPI) PostLogin(env, username, password string) (types.ApiLoginResponse, error) { + var res types.ApiLoginResponse + l := types.ApiLoginRequest{ + Username: username, + Password: password, + } + jsonMessage, err := json.Marshal(l) + if err != nil { + return res, fmt.Errorf("error marshaling data %s", err) + } + jsonParam := strings.NewReader(string(jsonMessage)) + reqURL := fmt.Sprintf("%s%s%s/%s", api.Configuration.URL, APIPath, APILogin, env) + rawRes, err := api.PostGeneric(reqURL, jsonParam) + if err != nil { + return res, fmt.Errorf("error api request - %v - %s", err, string(rawRes)) + } + if err := json.Unmarshal(rawRes, &res); err != nil { + return res, fmt.Errorf("can not parse body - %v", err) + } + return res, nil +} diff --git a/cli/api.go b/cli/api.go index 6851445e..86485818 100644 --- a/cli/api.go +++ b/cli/api.go @@ -3,6 +3,7 @@ package main import ( "crypto/tls" "crypto/x509" + "encoding/json" "fmt" "io" "io/ioutil" @@ -25,6 +26,8 @@ const ( APICarves = "/carves" // APIUsers APIUSers = "/users" + // APILogin + APILogin = "/login" // JSONApplication for Content-Type headers JSONApplication = "application/json" // JSONApplicationUTF8 for Content-Type headers, UTF charset @@ -52,7 +55,7 @@ type OsctrlAPI struct { Headers map[string]string } -// loadAPIConfiguration to load the DB configuration file and assign to variables +// loadAPIConfiguration to load the API configuration file and assign to variables func loadAPIConfiguration(file string) (JSONConfigurationAPI, error) { var config JSONConfigurationAPI // Load file and read config @@ -72,6 +75,23 @@ func loadAPIConfiguration(file string) (JSONConfigurationAPI, error) { return config, nil } +// writeAPIConfiguration to write the API configuration file and update values +func writeAPIConfiguration(file string, apiConf JSONConfigurationAPI) error { + if apiConf.URL == "" || apiConf.Token == "" { + return fmt.Errorf("invalid JSON values") + } + fileData := make(map[string]JSONConfigurationAPI) + fileData[projectName] = apiConf + confByte, err := json.MarshalIndent(fileData, "", " ") + if err != nil { + return fmt.Errorf("error serializing data %s", err) + } + if err := ioutil.WriteFile(file, confByte, 0644); err != nil { + return fmt.Errorf("error writing to file %s", err) + } + return nil +} + // CreateAPI to initialize the API client and handlers func CreateAPI(config JSONConfigurationAPI, insecure bool) *OsctrlAPI { var a *OsctrlAPI diff --git a/cli/main.go b/cli/main.go index 078abd83..68d2599c 100644 --- a/cli/main.go +++ b/cli/main.go @@ -15,6 +15,7 @@ import ( "github.com/jmpsec/osctrl/types" "github.com/jmpsec/osctrl/users" "github.com/jmpsec/osctrl/version" + "golang.org/x/term" "github.com/urfave/cli/v2" ) @@ -30,6 +31,8 @@ const ( appUsage string = "CLI for " + projectName // Application description appDescription string = appUsage + ", a fast and efficient osquery management" + // JSON file with API token + defaultApiConfigFile = projectName + "-api.json" ) const ( @@ -61,13 +64,14 @@ var ( // Variables for flags var ( - dbFlag bool - apiFlag bool - formatFlag string - silentFlag bool - insecureFlag bool - dbConfigFile string - apiConfigFile string + dbFlag bool + apiFlag bool + formatFlag string + silentFlag bool + insecureFlag bool + writeApiFileFlag bool + dbConfigFile string + apiConfigFile string ) // Initialization code @@ -93,7 +97,7 @@ func init() { &cli.StringFlag{ Name: "api-file", Aliases: []string{"A"}, - Value: "", + Value: defaultApiConfigFile, Usage: "Load API JSON configuration from `FILE`", EnvVars: []string{"API_CONFIG_FILE"}, Destination: &apiConfigFile, @@ -1442,6 +1446,29 @@ func init() { Usage: "Checks API token", Action: checkAPI, }, + { + Name: "login", + Usage: "Login into API and generate JSON config file with token", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "username", + Aliases: []string{"u"}, + Usage: "User to be used in login", + }, + &cli.StringFlag{ + Name: "environment", + Aliases: []string{"e"}, + Usage: "Environment to be used in login", + }, + &cli.BoolFlag{ + Name: "write-api-file", + Aliases: []string{"w"}, + Destination: &writeApiFileFlag, + Usage: "Write API configuration to JSON file", + }, + }, + Action: loginAPI, + }, } // Initialize formats values formats = make(map[string]bool) @@ -1467,6 +1494,9 @@ func checkDB(c *cli.Context) error { if err := db.Check(); err != nil { return err } + if !silentFlag { + fmt.Println("✅ DB check successful") + } // Should be good return nil } @@ -1483,6 +1513,55 @@ func checkAPI(c *cli.Context) error { // Initialize API osctrlAPI = CreateAPI(apiConfig, insecureFlag) } + if !silentFlag { + fmt.Println("✅ API check successful") + } + // Should be good + return nil +} + +// Action for the API login +func loginAPI(c *cli.Context) error { + // API URL can is needed + if apiConfig.URL == "" { + fmt.Println("❌ API URL is required") + os.Exit(1) + } + // Initialize API + osctrlAPI = CreateAPI(apiConfig, insecureFlag) + // We need credentials + username := c.String("username") + if username == "" { + fmt.Println("❌ username is required") + os.Exit(1) + } + env := c.String("environment") + if env == "" { + fmt.Println("❌ environment is required") + os.Exit(1) + } + fmt.Printf("\n -> Please introduce your password: ") + passwordByte, err := term.ReadPassword(int(os.Stdin.Fd())) + if err != nil { + return fmt.Errorf("error reading password %s", err) + } + fmt.Println() + apiResponse, err := osctrlAPI.PostLogin(env, username, string(passwordByte)) + if err != nil { + return fmt.Errorf("error in login %s", err) + } + apiConfig.Token = apiResponse.Token + if !silentFlag { + fmt.Printf("\n✅ API Login successful: %s\n", apiResponse.Token) + } + if writeApiFileFlag { + if err := writeAPIConfiguration(apiConfigFile, apiConfig); err != nil { + return fmt.Errorf("error writing to file %s, %s", apiConfigFile, err) + } + if !silentFlag { + fmt.Printf("\n✅ API config file written: %s\n", apiConfigFile) + } + } // Should be good return nil } @@ -1492,7 +1571,7 @@ func cliWrapper(action func(*cli.Context) error) func(*cli.Context) error { return func(c *cli.Context) error { // Verify if format is correct if !formats[formatFlag] { - return fmt.Errorf("❌ invalid format %s", formatFlag) + return fmt.Errorf("invalid format %s", formatFlag) } // DB connection will be used if dbFlag { @@ -1500,12 +1579,12 @@ func cliWrapper(action func(*cli.Context) error) func(*cli.Context) error { if dbConfigFile != "" { db, err = backend.CreateDBManagerFile(dbConfigFile) if err != nil { - return fmt.Errorf("❌ CreateDBManagerFile - %v", err) + return fmt.Errorf("CreateDBManagerFile - %v", err) } } else { db, err = backend.CreateDBManager(dbConfig) if err != nil { - return fmt.Errorf("❌ CreateDBManager - %v", err) + return fmt.Errorf("CreateDBManager - %v", err) } } // Initialize users @@ -1529,7 +1608,7 @@ func cliWrapper(action func(*cli.Context) error) func(*cli.Context) error { if apiConfigFile != "" { apiConfig, err = loadAPIConfiguration(apiConfigFile) if err != nil { - return fmt.Errorf("❌ loadAPIConfiguration - %v", err) + return fmt.Errorf("loadAPIConfiguration - %v", err) } } // Initialize API @@ -1565,6 +1644,6 @@ func main() { app.Commands = commands app.Action = cliAction if err := app.Run(os.Args); err != nil { - log.Fatalf("❌ Failed to execute %v", err) + log.Fatalf("❌ Failed to execute - %v", err) } } diff --git a/cli/node.go b/cli/node.go index 3f12008d..491f6bb3 100644 --- a/cli/node.go +++ b/cli/node.go @@ -61,12 +61,12 @@ func listNodes(c *cli.Context) error { if dbFlag { nds, err = nodesmgr.Gets(target, settingsmgr.InactiveHours()) if err != nil { - return fmt.Errorf("❌ error getting nodes - %s", err) + return fmt.Errorf("error getting nodes - %s", err) } } else if apiFlag { nds, err = osctrlAPI.GetNodes(env, target) if err != nil { - return fmt.Errorf("❌ error getting nodes - %s", err) + return fmt.Errorf("error getting nodes - %s", err) } } header := []string{ @@ -83,14 +83,14 @@ func listNodes(c *cli.Context) error { if formatFlag == jsonFormat { jsonRaw, err := json.Marshal(nds) if err != nil { - return fmt.Errorf("❌ error marshaling - %s", err) + return fmt.Errorf("error marshaling - %s", err) } fmt.Println(string(jsonRaw)) } else if formatFlag == csvFormat { data := nodesToData(nds, header) w := csv.NewWriter(os.Stdout) if err := w.WriteAll(data); err != nil { - return fmt.Errorf("❌ error writting csv - %s", err) + return fmt.Errorf("error writting csv - %s", err) } } else if formatFlag == prettyFormat { table := tablewriter.NewWriter(os.Stdout) @@ -121,11 +121,11 @@ func deleteNode(c *cli.Context) error { } if dbFlag { if err := nodesmgr.ArchiveDeleteByUUID(uuid); err != nil { - return fmt.Errorf("❌ error deleting - %s", err) + return fmt.Errorf("error deleting - %s", err) } } else if apiFlag { if err := osctrlAPI.DeleteNode(env, uuid); err != nil { - return fmt.Errorf("❌ error deleting node - %s", err) + return fmt.Errorf("error deleting node - %s", err) } } if !silentFlag { @@ -154,20 +154,20 @@ func tagNode(c *cli.Context) error { if dbFlag { e, err := envs.Get(env) if err != nil { - return fmt.Errorf("❌ error env get - %s", err) + return fmt.Errorf("error env get - %s", err) } n, err := nodesmgr.GetByUUIDEnv(uuid, e.ID) if err != nil { - return fmt.Errorf("❌ error get uuid - %s", err) + return fmt.Errorf("error get uuid - %s", err) } if tagsmgr.Exists(tag) { if err := tagsmgr.TagNode(tag, n, appName, false); err != nil { - return fmt.Errorf("❌ error tagging - %s", err) + return fmt.Errorf("error tagging - %s", err) } } } else if apiFlag { if err := osctrlAPI.TagNode(env, uuid, tag); err != nil { - return fmt.Errorf("❌ error tagging node - %s", err) + return fmt.Errorf("error tagging node - %s", err) } } if !silentFlag { @@ -192,12 +192,12 @@ func showNode(c *cli.Context) error { if dbFlag { node, err = nodesmgr.GetByUUID(uuid) if err != nil { - return fmt.Errorf("❌ error getting node - %s", err) + return fmt.Errorf("error getting node - %s", err) } } else if apiFlag { node, err = osctrlAPI.GetNode(env, uuid) if err != nil { - return fmt.Errorf("❌ error getting node - %s", err) + return fmt.Errorf("error getting node - %s", err) } } header := []string{ @@ -214,14 +214,14 @@ func showNode(c *cli.Context) error { if formatFlag == jsonFormat { jsonRaw, err := json.Marshal(node) if err != nil { - return fmt.Errorf("❌ error marshaling - %s", err) + return fmt.Errorf("error marshaling - %s", err) } fmt.Println(string(jsonRaw)) } else if formatFlag == csvFormat { data := nodeToData(node, nil) w := csv.NewWriter(os.Stdout) if err := w.WriteAll(data); err != nil { - return fmt.Errorf("❌ error writting csv - %s", err) + return fmt.Errorf("error writting csv - %s", err) } } else if formatFlag == prettyFormat { table := tablewriter.NewWriter(os.Stdout) diff --git a/cli/permission.go b/cli/permission.go index 88162062..87bdfaa6 100644 --- a/cli/permission.go +++ b/cli/permission.go @@ -66,7 +66,7 @@ func changePermissions(c *cli.Context) error { if dbFlag { env, err := envs.Get(envName) if err != nil { - return fmt.Errorf("❌ error getting environment - %s", err) + return fmt.Errorf("error getting environment - %s", err) } // If admin, then all permissions follow if admin { @@ -77,22 +77,22 @@ func changePermissions(c *cli.Context) error { // Reset permissions to regular user access if user { if err := adminUsers.SetEnvUser(username, env.UUID, user); err != nil { - return fmt.Errorf("❌ error setting user - %s", err) + return fmt.Errorf("error setting user - %s", err) } } if admin { if err := adminUsers.SetEnvAdmin(username, env.UUID, admin); err != nil { - return fmt.Errorf("❌ error setting admin - %s", err) + return fmt.Errorf("error setting admin - %s", err) } } if carve { if err := adminUsers.SetEnvCarve(username, env.UUID, carve); err != nil { - return fmt.Errorf("❌ error setting carve - %s", err) + return fmt.Errorf("error setting carve - %s", err) } } if query { if err := adminUsers.SetEnvQuery(username, env.UUID, query); err != nil { - return fmt.Errorf("❌ error setting query - %s", err) + return fmt.Errorf("error setting query - %s", err) } } } else if apiFlag { @@ -120,12 +120,12 @@ func showPermissions(c *cli.Context) error { if dbFlag { env, err := envs.Get(envName) if err != nil { - return fmt.Errorf("❌ error env get - %s", err) + return fmt.Errorf("error env get - %s", err) } // Show is just display user existing permissions and return userAccess, err = adminUsers.GetEnvAccess(username, env.UUID) if err != nil { - return fmt.Errorf("❌ error getting access - %s", err) + return fmt.Errorf("error getting access - %s", err) } } else if apiFlag { } @@ -140,14 +140,14 @@ func showPermissions(c *cli.Context) error { if formatFlag == jsonFormat { jsonRaw, err := json.Marshal(userAccess) if err != nil { - return fmt.Errorf("❌ error serializing - %s", err) + return fmt.Errorf("error serializing - %s", err) } fmt.Println(string(jsonRaw)) } else if formatFlag == csvFormat { data := accessToData(userAccess, envName, header) w := csv.NewWriter(os.Stdout) if err := w.WriteAll(data); err != nil { - return fmt.Errorf("❌ error WriteAll - %s", err) + return fmt.Errorf("error WriteAll - %s", err) } } else if formatFlag == prettyFormat { table := tablewriter.NewWriter(os.Stdout) @@ -214,7 +214,7 @@ func allPermissions(c *cli.Context) error { // Show is just display user existing permissions and return existingAccess, err = adminUsers.GetAccess(username) if err != nil { - return fmt.Errorf("❌ error getting access - %s", err) + return fmt.Errorf("error getting access - %s", err) } } else if apiFlag { } @@ -229,14 +229,14 @@ func allPermissions(c *cli.Context) error { if formatFlag == jsonFormat { jsonRaw, err := json.Marshal(existingAccess) if err != nil { - return fmt.Errorf("❌ error serializing - %s", err) + return fmt.Errorf("error serializing - %s", err) } fmt.Println(string(jsonRaw)) } else if formatFlag == csvFormat { data := permissionsToData(existingAccess, header) w := csv.NewWriter(os.Stdout) if err := w.WriteAll(data); err != nil { - return fmt.Errorf("❌ error WriteAll - %s", err) + return fmt.Errorf("error WriteAll - %s", err) } } else if formatFlag == prettyFormat { table := tablewriter.NewWriter(os.Stdout) diff --git a/cli/query.go b/cli/query.go index 852f7317..3594dc65 100644 --- a/cli/query.go +++ b/cli/query.go @@ -73,16 +73,16 @@ func listQueries(c *cli.Context) error { if dbFlag { e, err := envs.Get(env) if err != nil { - return fmt.Errorf("❌ error env get - %s", err) + return fmt.Errorf("error env get - %s", err) } qs, err = queriesmgr.GetQueries(target, e.ID) if err != nil { - return fmt.Errorf("❌ error get queries - %s", err) + return fmt.Errorf("error get queries - %s", err) } } else if apiFlag { qs, err = osctrlAPI.GetQueries(env) if err != nil { - return fmt.Errorf("❌ error get queries - %s", err) + return fmt.Errorf("error get queries - %s", err) } } header := []string{ @@ -101,14 +101,14 @@ func listQueries(c *cli.Context) error { if formatFlag == jsonFormat { jsonRaw, err := json.Marshal(qs) if err != nil { - return fmt.Errorf("❌ error json marshal - %s", err) + return fmt.Errorf("error json marshal - %s", err) } fmt.Println(string(jsonRaw)) } else if formatFlag == csvFormat { data := queriesToData(qs, header) w := csv.NewWriter(os.Stdout) if err := w.WriteAll(data); err != nil { - return fmt.Errorf("❌ error csv writeall - %s", err) + return fmt.Errorf("error csv writeall - %s", err) } } else if formatFlag == prettyFormat { table := tablewriter.NewWriter(os.Stdout) @@ -140,14 +140,14 @@ func completeQuery(c *cli.Context) error { if dbFlag { e, err := envs.Get(env) if err != nil { - return fmt.Errorf("❌ error env get - %s", err) + return fmt.Errorf("error env get - %s", err) } if err := queriesmgr.Complete(name, e.ID); err != nil { - return fmt.Errorf("❌ error query complete - %s", err) + return fmt.Errorf("error query complete - %s", err) } } else if apiFlag { if err := osctrlAPI.CompleteQuery(env, name); err != nil { - return fmt.Errorf("❌ error complete query - %s", err) + return fmt.Errorf("error complete query - %s", err) } } if !silentFlag { @@ -174,11 +174,11 @@ func deleteQuery(c *cli.Context) error { return err } if err := queriesmgr.Delete(name, e.ID); err != nil { - return fmt.Errorf("❌ error %s", err) + return fmt.Errorf("error %s", err) } } else if apiFlag { if err := osctrlAPI.DeleteQuery(env, name); err != nil { - return fmt.Errorf("❌ error %s", err) + return fmt.Errorf("error %s", err) } } if !silentFlag { @@ -208,7 +208,7 @@ func runQuery(c *cli.Context) error { if dbFlag { e, err := envs.Get(env) if err != nil { - return fmt.Errorf("❌ error env get - %s", err) + return fmt.Errorf("error env get - %s", err) } queryName := queries.GenQueryName() newQuery := queries.DistributedQuery{ @@ -225,15 +225,15 @@ func runQuery(c *cli.Context) error { EnvironmentID: e.ID, } if err := queriesmgr.Create(newQuery); err != nil { - return fmt.Errorf("❌ error query create - %s", err) + return fmt.Errorf("error query create - %s", err) } if (uuid != "") && nodesmgr.CheckByUUID(uuid) { if err := queriesmgr.CreateTarget(queryName, queries.QueryTargetUUID, uuid); err != nil { - return fmt.Errorf("❌ error create target - %s", err) + return fmt.Errorf("error create target - %s", err) } } if err := queriesmgr.SetExpected(queryName, 1, e.ID); err != nil { - return fmt.Errorf("❌ error set expected - %s", err) + return fmt.Errorf("error set expected - %s", err) } if !silentFlag { fmt.Printf("✅ query %s created successfully", queryName) @@ -242,7 +242,7 @@ func runQuery(c *cli.Context) error { } else if apiFlag { q, err := osctrlAPI.RunQuery(env, uuid, query, hidden) if err != nil { - return fmt.Errorf("❌ error run query - %s", err) + return fmt.Errorf("error run query - %s", err) } if !silentFlag { fmt.Printf("✅ query %s created successfully", q.Name) diff --git a/cli/settings.go b/cli/settings.go index 3982d4cf..fd1b14b6 100644 --- a/cli/settings.go +++ b/cli/settings.go @@ -103,13 +103,13 @@ func updateSetting(c *cli.Context) error { err = settingsmgr.SetString(c.String("string"), service, name, false) } if err != nil { - return fmt.Errorf("❌ error set type - %s", err) + return fmt.Errorf("error set type - %s", err) } if info != "" { err = settingsmgr.SetInfo(info, service, name) } if err != nil { - return fmt.Errorf("❌ error set info - %s", err) + return fmt.Errorf("error set info - %s", err) } if !silentFlag { fmt.Println("✅ setting deleted successfully") @@ -130,7 +130,7 @@ func deleteSetting(c *cli.Context) error { os.Exit(1) } if err := settingsmgr.DeleteValue(service, name); err != nil { - return fmt.Errorf("❌ error get queries - %s", err) + return fmt.Errorf("error get queries - %s", err) } if !silentFlag { fmt.Println("✅ setting deleted successfully") diff --git a/cli/user.go b/cli/user.go index bcaea6f3..75dea802 100644 --- a/cli/user.go +++ b/cli/user.go @@ -59,7 +59,7 @@ func addUser(c *cli.Context) error { } env, err := envs.Get(defaultEnv) if err != nil { - return fmt.Errorf("❌ error getting environment - %s", err) + return fmt.Errorf("error getting environment - %s", err) } password := c.String("password") email := c.String("email") @@ -67,17 +67,17 @@ func addUser(c *cli.Context) error { admin := c.Bool("admin") user, err := adminUsers.New(username, password, email, fullname, env.UUID, admin) if err != nil { - return fmt.Errorf("❌ error with new user - %s", err) + return fmt.Errorf("error with new user - %s", err) } // Create user if err := adminUsers.Create(user); err != nil { - return fmt.Errorf("❌ error creating user - %s", err) + return fmt.Errorf("error creating user - %s", err) } // Assign permissions to user access := adminUsers.GenEnvUserAccess([]string{env.UUID}, true, (admin == true), (admin == true), (admin == true)) perms := adminUsers.GenPermissions(username, appName, access) if err := adminUsers.CreatePermissions(perms); err != nil { - return fmt.Errorf("❌ error creating permissions - %s", err) + return fmt.Errorf("error creating permissions - %s", err) } if !silentFlag { fmt.Printf("✅ created user %s successfully", username) @@ -95,37 +95,37 @@ func editUser(c *cli.Context) error { password := c.String("password") if password != "" { if err := adminUsers.ChangePassword(username, password); err != nil { - return fmt.Errorf("❌ error changing password - %s", err) + return fmt.Errorf("error changing password - %s", err) } } email := c.String("email") if email != "" { if err := adminUsers.ChangeEmail(username, email); err != nil { - return fmt.Errorf("❌ error changing email - %s", err) + return fmt.Errorf("error changing email - %s", err) } } fullname := c.String("fullname") if fullname != "" { if err := adminUsers.ChangeFullname(username, fullname); err != nil { - return fmt.Errorf("❌ error changing name - %s", err) + return fmt.Errorf("error changing name - %s", err) } } admin := c.Bool("admin") if admin { if err := adminUsers.ChangeAdmin(username, admin); err != nil { - return fmt.Errorf("❌ error changing admin - %s", err) + return fmt.Errorf("error changing admin - %s", err) } } notAdmin := c.Bool("non-admin") if notAdmin { if err := adminUsers.ChangeAdmin(username, false); err != nil { - return fmt.Errorf("❌ error changing non-admin - %s", err) + return fmt.Errorf("error changing non-admin - %s", err) } } defaultEnv := c.String("environment") if defaultEnv != "" { if err := adminUsers.ChangeDefaultEnv(username, defaultEnv); err != nil { - return fmt.Errorf("❌ error changing environment - %s", err) + return fmt.Errorf("error changing environment - %s", err) } } if !silentFlag { @@ -143,11 +143,11 @@ func deleteUser(c *cli.Context) error { } if dbFlag { if err := adminUsers.Delete(username); err != nil { - return fmt.Errorf("❌ error deleting - %s", err) + return fmt.Errorf("error deleting - %s", err) } } else if apiFlag { if err := osctrlAPI.DeleteUser(username); err != nil { - return fmt.Errorf("❌ error deleting user - %s", err) + return fmt.Errorf("error deleting user - %s", err) } } if !silentFlag { @@ -162,12 +162,12 @@ func listUsers(c *cli.Context) error { if dbFlag { usrs, err = adminUsers.All() if err != nil { - return fmt.Errorf("❌ error getting users - %s", err) + return fmt.Errorf("error getting users - %s", err) } } else if apiFlag { usrs, err = osctrlAPI.GetUsers() if err != nil { - return fmt.Errorf("❌ error getting users - %s", err) + return fmt.Errorf("error getting users - %s", err) } } header := []string{ @@ -182,14 +182,14 @@ func listUsers(c *cli.Context) error { if formatFlag == jsonFormat { jsonRaw, err := json.Marshal(usrs) if err != nil { - return fmt.Errorf("❌ error serializing - %s", err) + return fmt.Errorf("error serializing - %s", err) } fmt.Println(string(jsonRaw)) } else if formatFlag == csvFormat { data := usersToData(usrs, header) w := csv.NewWriter(os.Stdout) if err := w.WriteAll(data); err != nil { - return fmt.Errorf("❌ error WriteAll - %s", err) + return fmt.Errorf("error WriteAll - %s", err) } } else if formatFlag == prettyFormat { table := tablewriter.NewWriter(os.Stdout) @@ -218,12 +218,12 @@ func showUser(c *cli.Context) error { if dbFlag { usr, err = adminUsers.Get(username) if err != nil { - return fmt.Errorf("❌ error getting user - %s", err) + return fmt.Errorf("error getting user - %s", err) } } else if apiFlag { usr, err = osctrlAPI.GetUser(username) if err != nil { - return fmt.Errorf("❌ error getting user - %s", err) + return fmt.Errorf("error getting user - %s", err) } } header := []string{ @@ -238,14 +238,14 @@ func showUser(c *cli.Context) error { if formatFlag == jsonFormat { jsonRaw, err := json.Marshal(usr) if err != nil { - return fmt.Errorf("❌ error serializing - %s", err) + return fmt.Errorf("error serializing - %s", err) } fmt.Println(string(jsonRaw)) } else if formatFlag == csvFormat { data := userToData(usr, nil) w := csv.NewWriter(os.Stdout) if err := w.WriteAll(data); err != nil { - return fmt.Errorf("❌ error WriteAll - %s", err) + return fmt.Errorf("error WriteAll - %s", err) } } else if formatFlag == prettyFormat { table := tablewriter.NewWriter(os.Stdout) diff --git a/go.mod b/go.mod index 0ab18c0a..9f152c07 100644 --- a/go.mod +++ b/go.mod @@ -130,6 +130,7 @@ require ( github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + golang.org/x/term v0.1.0 golang.org/x/text v0.3.7 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index f081930f..162401f7 100644 --- a/go.sum +++ b/go.sum @@ -876,6 +876,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/types/types.go b/types/types.go index e295b81d..55b19649 100644 --- a/types/types.go +++ b/types/types.go @@ -113,6 +113,12 @@ type ApiNodeGenericRequest struct { UUID string `json:"uuid"` } +// ApiLoginRequest to receive login requests +type ApiLoginRequest struct { + Username string `json:"username"` + Password string `json:"password"` +} + // ApiErrorResponse to be returned to API requests with the error message type ApiErrorResponse struct { Error string `json:"error"` @@ -127,3 +133,8 @@ type ApiQueriesResponse struct { type ApiGenericResponse struct { Message string `json:"message"` } + +// ApiLoginResponse to be returned to API login requests with the generated token +type ApiLoginResponse struct { + Token string `json:"token"` +}