diff --git a/cmd/polaris/auth.go b/cmd/polaris/auth.go index dc0008ee..86eb8eb0 100644 --- a/cmd/polaris/auth.go +++ b/cmd/polaris/auth.go @@ -6,7 +6,11 @@ import ( "github.com/spf13/cobra" ) +var insightsURL string + func init() { + loginCmd.Flags().StringVar(&insightsURL, "insights-host", "https://insights.fairwinds.com", "insights host URL") + rootCmd.AddCommand(authCmd) authCmd.AddCommand(loginCmd) authCmd.AddCommand(logoutCmd) @@ -25,7 +29,7 @@ var loginCmd = &cobra.Command{ Short: "Authenticate polaris with Fairwinds Insights.", Long: `Authenticate polaris with Fairwinds Insights.`, Run: func(cmd *cobra.Command, args []string) { - err := auth.HandleLogin() + err := auth.HandleLogin(insightsURL) if err != nil { logrus.Fatal(err) } diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index bf27bef0..bacb2152 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -8,8 +8,6 @@ import ( "gopkg.in/yaml.v3" ) -const insightsURL = "http://localhost:3001" // TODO: make it configurable - var userHomeDir string var polarisHostsFilepath string diff --git a/pkg/auth/login.go b/pkg/auth/login.go index 4964d3ae..762cd4d2 100644 --- a/pkg/auth/login.go +++ b/pkg/auth/login.go @@ -10,6 +10,7 @@ import ( "os" "path/filepath" "strings" + "time" "github.com/AlecAivazis/survey/v2" "github.com/gorilla/mux" @@ -40,7 +41,7 @@ type Host struct { var paramsOrErrorChan = make(chan paramsOrError) -func HandleLogin() error { +func HandleLogin(insightsURL string) error { if _, err := os.Stat(polarisHostsFilepath); err == nil { content, err := readPolarisHostsFile() if err != nil { @@ -88,7 +89,7 @@ func HandleLogin() error { var router *mux.Router go func() { router = mux.NewRouter() - router.HandleFunc("/auth/login/callback", callbackHandler(localServerPort)) + router.HandleFunc("/auth/login/callback", callbackHandler(insightsURL, localServerPort)) if err := http.Serve(listener, router); err != nil { paramsOrErrorChan <- paramsOrError{err: fmt.Errorf("starting the local http server: %w", err)} } @@ -106,13 +107,14 @@ func HandleLogin() error { token = paramOrError.token } else { var answer string - err := survey.AskOne(&survey.Password{Message: "Paste your authentication token:"}, &answer, survey.WithValidator(validateToken)) + var bot Bot + err := survey.AskOne(&survey.Password{Message: "Paste your authentication token:"}, &answer, survey.WithValidator(validateToken(insightsURL, &bot))) if err != nil { return fmt.Errorf("asking how to authenticate: %w", err) } token = answer - user = "admin" // TODO: Vitor - fetch name from bots endpoint - organization = "acme-co" // TODO: Vitor - fetch organization from bots endpoint + user = bot.Name + organization = bot.Organization } polarisCfgDir := filepath.Join(userHomeDir, ".config", "polaris") @@ -187,7 +189,7 @@ func fetchAuthToken(insightsURL, organization, code string) (string, error) { return token, nil } -func callbackHandler(localServerPort int) func(w http.ResponseWriter, r *http.Request) { +func callbackHandler(insightsURL string, localServerPort int) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { // checks for error in the params errMsg := r.URL.Query().Get("error") @@ -227,16 +229,50 @@ func callbackHandler(localServerPort int) func(w http.ResponseWriter, r *http.Re } } -func validateToken(args any) error { - token, ok := args.(string) - if !ok { - return errors.New("casting token to string") +func validateToken(insightsURL string, bot *Bot) func(args any) error { + return func(args any) error { + token, ok := args.(string) + if !ok { + return errors.New("casting token to string") + } + if len(strings.TrimSpace(token)) <= 0 { + return errors.New("token is required") + } + return fetchOrganizationBot(insightsURL, token, bot) + } +} + +type Bot struct { + ID int + Organization string + Name string + Role string + AuthToken string + CreatedAt time.Time +} + +func fetchOrganizationBot(insightsURL, authToken string, bot *Bot) error { + authTokenURL := fmt.Sprintf("%s/v0/bots/from-request", insightsURL) + r, err := http.NewRequest("GET", authTokenURL, nil) + if err != nil { + return err + } + r.Header.Add("Content-Type", "application/json") + r.Header.Add("Authorization", "Bearer "+authToken) + + res, err := http.DefaultClient.Do(r) + if err != nil { + return err + } + defer res.Body.Close() + + if res.StatusCode < 200 || res.StatusCode >= 400 { + return fmt.Errorf("expected 200 (OK) - received %d", res.StatusCode) } - if len(strings.TrimSpace(token)) <= 0 { - return errors.New("token is required") + err = json.NewDecoder(res.Body).Decode(bot) + if err != nil { + return err } - - // TODO: Vitor - validate token against the API? return nil }