From 90edaaf56a60005609e1725116b4b0f13857a02b Mon Sep 17 00:00:00 2001 From: shreddedbacon Date: Thu, 28 Dec 2023 08:28:51 +1100 Subject: [PATCH] feat: support keycloak based logins in the cli --- cmd/config.go | 99 +++++++++++++++++++++++------- cmd/deploy.go | 8 +-- cmd/deploytarget.go | 13 ++-- cmd/deploytargetconfig.go | 9 +-- cmd/environment.go | 9 +-- cmd/get.go | 6 +- cmd/groups.go | 7 ++- cmd/import.go | 4 +- cmd/list.go | 24 ++++---- cmd/login.go | 47 +++++++++----- cmd/notificationsemail.go | 15 ++--- cmd/notificationsrocketchat.go | 15 ++--- cmd/notificationsslack.go | 15 ++--- cmd/notificationsteams.go | 15 ++--- cmd/notificationswebhook.go | 15 ++--- cmd/organization.go | 7 ++- cmd/project.go | 17 ++--- cmd/retrieve.go | 2 +- cmd/root.go | 2 +- cmd/shared.go | 3 +- cmd/ssh.go | 2 +- cmd/tasks.go | 6 +- cmd/users.go | 10 +-- cmd/variables.go | 4 +- cmd/whoami.go | 2 +- docs/commands/lagoon_config_add.md | 29 ++++++--- go.mod | 13 ++-- go.sum | 34 +++++++--- internal/lagoon/config.go | 25 +++++--- pkg/graphql/main.go | 10 ++- pkg/graphql/main_test.go | 9 ++- pkg/lagoon/ssh/main.go | 2 +- 32 files changed, 305 insertions(+), 173 deletions(-) diff --git a/cmd/config.go b/cmd/config.go index 975d3cae..194cfa18 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -16,20 +16,21 @@ import ( "github.com/uselagoon/lagoon-cli/internal/lagoon" "github.com/uselagoon/lagoon-cli/internal/lagoon/client" "github.com/uselagoon/lagoon-cli/pkg/output" + "golang.org/x/oauth2" "gopkg.in/yaml.v3" ) // LagoonConfigFlags . type LagoonConfigFlags struct { - Lagoon string `json:"lagoon,omitempty"` - Hostname string `json:"hostname,omitempty"` - Port string `json:"port,omitempty"` - GraphQL string `json:"graphql,omitempty"` - Token string `json:"token,omitempty"` - UI string `json:"ui,omitempty"` - Kibana string `json:"kibana,omitempty"` - SSHKey string `json:"sshkey,omitempty"` - SSHPortal bool `json:"sshportal,omitempty"` + Lagoon string `json:"lagoon,omitempty"` + Hostname string `json:"hostname,omitempty"` + Port string `json:"port,omitempty"` + GraphQL string `json:"graphql,omitempty"` + Token string `json:"token,omitempty"` + Grant *oauth2.Token `json:"grant,omitempty"` + UI string `json:"ui,omitempty"` + Kibana string `json:"kibana,omitempty"` + SSHKey string `json:"sshkey,omitempty"` } func parseLagoonConfig(flags pflag.FlagSet) LagoonConfigFlags { @@ -112,6 +113,7 @@ var configLagoonsCmd = &cobra.Command{ if fullConfigList { mapData = append(mapData, returnNonEmptyString(lc.UI)) mapData = append(mapData, returnNonEmptyString(lc.Kibana)) + mapData = append(mapData, returnNonEmptyString(lc.KeycloakURL)) } mapData = append(mapData, returnNonEmptyString(lc.SSHKey)) data = append(data, mapData) @@ -129,6 +131,7 @@ var configLagoonsCmd = &cobra.Command{ if fullConfigList { tableHeader = append(tableHeader, "UI-URL") tableHeader = append(tableHeader, "Kibana-URL") + tableHeader = append(tableHeader, "Auth-URL") } tableHeader = append(tableHeader, "SSH-Key") output.RenderOutput(output.Table{ @@ -148,6 +151,18 @@ var configAddCmd = &cobra.Command{ if lagoonConfig.Lagoon == "" { return fmt.Errorf("Missing arguments: Lagoon name is not defined") } + sshToken, err := cmd.Flags().GetBool("ssh-token") + if err != nil { + return err + } + keycloakURL, err := cmd.Flags().GetString("keycloak-url") + if err != nil { + return err + } + keycloakIDP, err := cmd.Flags().GetString("keycloak-idp") + if err != nil { + return err + } if lagoonConfig.Hostname != "" && lagoonConfig.Port != "" && lagoonConfig.GraphQL != "" { lc := lagoonCLIConfig.Lagoons[lagoonConfig.Lagoon] @@ -160,8 +175,19 @@ var configAddCmd = &cobra.Command{ if lagoonConfig.Kibana != "" { lc.Kibana = lagoonConfig.Kibana } + lc.Grant = &oauth2.Token{} //set up an empty grant if lagoonConfig.Token != "" { - lc.Token = lagoonConfig.Token + // set the token into the grant, this is mainly for legacy based token backwards compatability + // tokens added this way will be changed by the ssh or keycloak token generation process if they are not a legacy token + lc.Grant.AccessToken = lagoonConfig.Token + } + lc.SSHToken = sshToken + if keycloakURL != "" { + // trim any trailing slashes from the keycloak url + lc.KeycloakURL = strings.TrimRight(keycloakURL, "/") + } + if keycloakIDP != "" { + lc.KeycloakIDP = keycloakIDP } if lagoonConfig.SSHKey != "" { lc.SSHKey = lagoonConfig.SSHKey @@ -178,6 +204,7 @@ var configAddCmd = &cobra.Command{ "SSH-Port", "UI-URL", "Kibana-URL", + "Keycloak-URL", "SSH-Key", }, Data: []output.Data{ @@ -188,6 +215,7 @@ var configAddCmd = &cobra.Command{ lagoonConfig.Port, lagoonConfig.UI, lagoonConfig.Kibana, + keycloakURL, lagoonConfig.SSHKey, }, }, @@ -269,7 +297,7 @@ var configLagoonVersionCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -301,21 +329,32 @@ func init() { configCmd.AddCommand(configLagoonsCmd) configCmd.AddCommand(configLagoonVersionCmd) configAddCmd.Flags().StringVarP(&lagoonHostname, "hostname", "H", "", - "Lagoon SSH hostname") + "Lagoon token endpoint hostname (eg, token.amazeeio.cloud)") configAddCmd.Flags().StringVarP(&lagoonPort, "port", "P", "", - "Lagoon SSH port") + "Lagoon token endpoint port (22)") configAddCmd.Flags().StringVarP(&lagoonGraphQL, "graphql", "g", "", - "Lagoon GraphQL endpoint") + "Lagoon GraphQL endpoint (eg, https://api.amazeeio.cloud/graphql)") configAddCmd.Flags().StringVarP(&lagoonToken, "token", "t", "", "Lagoon GraphQL token") configAddCmd.Flags().StringVarP(&lagoonUI, "ui", "u", "", - "Lagoon UI location (https://dashboard.amazeeio.cloud)") + "Optional: Lagoon UI location (eg, https://dashboard.amazeeio.cloud)") configAddCmd.PersistentFlags().BoolVarP(&createConfig, "create-config", "", false, "Create the config file if it is non existent (to be used with --config-file)") configAddCmd.Flags().StringVarP(&lagoonKibana, "kibana", "k", "", - "Lagoon Kibana URL (https://logs.amazeeio.cloud)") + "Optional: Lagoon Kibana URL (eg, https://logs.amazeeio.cloud)") + configAddCmd.Flags().StringP("keycloak-url", "K", "", `Lagoon Keycloak URL (eg, https://keycloak.amazeeio.cloud). + Setting this will use keycloak for authentication instead of SSH based tokens. + Set 'ssh-token=true' to override. + Note: SSH keys are still required for SSH access.`) + configAddCmd.Flags().String("keycloak-idp", "", `Optional: Lagoon Keycloak Identity Provider name. + Set this to the name of the separate Identity Provider within keycloak if you use one. + You may need to check with your Lagoon administrator if you use another SSO provider`) configAddCmd.Flags().StringVarP(&lagoonSSHKey, "ssh-key", "", "", "SSH Key to use for this cluster for generating tokens") + configAddCmd.Flags().Bool("ssh-token", true, `Set this context to only use ssh based tokens + Set this to only use SSH based tokens if you're using the CLI in CI jobs or other automated processes + where logging in via keycloak is not possible. + This is enabled by default, it will be disabled by default in a future release.`) configLagoonsCmd.Flags().BoolVarP(&fullConfigList, "show-full", "", false, "Show full config output when listing Lagoon configurations") configFeatureSwitch.Flags().StringVarP(&updateCheck, "disable-update-check", "", "", @@ -332,12 +371,14 @@ func readLagoonConfig(lc *lagoon.Config, file string) error { // configuration to point to the amazeeio lagoon instance if yesNo(fmt.Sprintf("Config file '%s' does not exist, do you want to create it with defaults?", file)) { l := lagoon.Context{ - GraphQL: "https://api.lagoon.amazeeio.cloud/graphql", - HostName: "ssh.lagoon.amazeeio.cloud", - Token: "", - Port: "32222", - UI: "https://dashboard.amazeeio.cloud", - Kibana: "https://logs.amazeeio.cloud/", + GraphQL: "https://api.amazeeio.cloud/graphql", + HostName: "token.amazeeio.cloud", + Grant: &oauth2.Token{}, // set an empty oauth token + Port: "22", + UI: "https://dashboard.amazeeio.cloud", + Kibana: "https://logs.amazeeio.cloud/", + KeycloakURL: "https://keycloak.amazeeio.cloud/", + SSHToken: true, //@TODO: retain ssh token generation by default, eventually change this to false so that token generation is opt-in } lc.Lagoons = map[string]lagoon.Context{} lc.Lagoons["amazeeio"] = l @@ -354,6 +395,20 @@ func readLagoonConfig(lc *lagoon.Config, file string) error { if l.GraphQL == "" || l.HostName == "" || l.Port == "" { return fmt.Errorf("configured lagoon %s is missing required configuration for graphql, hostname, or port", ln) } + if l.Token != "" { + // if there isn't already a grant in the config + if lc.Lagoons[ln].Grant == nil { + // create one by just setting the token to be the grants accesstoken. This allows legacy tokens still still function + grant := &oauth2.Token{ + AccessToken: l.Token, + } + d := lc.Lagoons[ln] + d.Grant = grant + // retain the `token` field for now for backwards compatability with older cli versions + // d.Token = "" + lc.Lagoons[ln] = d + } + } } return nil diff --git a/cmd/deploy.go b/cmd/deploy.go index 25e6b172..3e261a00 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -50,7 +50,7 @@ use 'lagoon deploy latest' instead`, current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -104,7 +104,7 @@ var deployPromoteCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -150,7 +150,7 @@ This environment should already exist in lagoon. It is analogous with the 'Deplo current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -219,7 +219,7 @@ This pullrequest may not already exist as an environment in lagoon.`, current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) diff --git a/cmd/deploytarget.go b/cmd/deploytarget.go index e1025b33..094426ee 100644 --- a/cmd/deploytarget.go +++ b/cmd/deploytarget.go @@ -3,6 +3,8 @@ package cmd import ( "context" "fmt" + "strconv" + "github.com/spf13/cobra" "github.com/uselagoon/lagoon-cli/internal/lagoon" "github.com/uselagoon/lagoon-cli/internal/lagoon/client" @@ -11,7 +13,6 @@ import ( l "github.com/uselagoon/machinery/api/lagoon" lclient "github.com/uselagoon/machinery/api/lagoon/client" s "github.com/uselagoon/machinery/api/schema" - "strconv" ) var addDeployTargetCmd = &cobra.Command{ @@ -100,7 +101,7 @@ var addDeployTargetCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -212,7 +213,7 @@ var updateDeployTargetCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug, @@ -297,7 +298,7 @@ var deleteDeployTargetCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug, @@ -349,7 +350,7 @@ var addDeployTargetToOrganizationCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -406,7 +407,7 @@ var RemoveDeployTargetFromOrganizationCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, diff --git a/cmd/deploytargetconfig.go b/cmd/deploytargetconfig.go index ad0dfcf8..ffea78c9 100644 --- a/cmd/deploytargetconfig.go +++ b/cmd/deploytargetconfig.go @@ -3,6 +3,7 @@ package cmd import ( "context" "fmt" + l "github.com/uselagoon/machinery/api/lagoon" lclient "github.com/uselagoon/machinery/api/lagoon/client" s "github.com/uselagoon/machinery/api/schema" @@ -57,7 +58,7 @@ var addDeployTargetConfigCmd = &cobra.Command{ return fmt.Errorf("Missing arguments: branches is a required flag") } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -153,7 +154,7 @@ var updateDeployTargetConfigCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -229,7 +230,7 @@ var deleteDeployTargetConfigCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -265,7 +266,7 @@ var listDeployTargetConfigsCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) diff --git a/cmd/environment.go b/cmd/environment.go index 75bc481a..4c4976af 100644 --- a/cmd/environment.go +++ b/cmd/environment.go @@ -3,10 +3,11 @@ package cmd import ( "context" "fmt" - s "github.com/uselagoon/machinery/api/schema" "os" "strings" + s "github.com/uselagoon/machinery/api/schema" + "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/uselagoon/lagoon-cli/internal/lagoon" @@ -100,7 +101,7 @@ var updateEnvironmentCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -182,7 +183,7 @@ var listBackupsCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -249,7 +250,7 @@ This returns a direct URL to the backup, this is a signed download link with a l current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) diff --git a/cmd/get.go b/cmd/get.go index acf45c71..51bdc75d 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -61,7 +61,7 @@ var getProjectCmd = &cobra.Command{ return nil } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -144,7 +144,7 @@ This returns information about a deployment, the logs of this build can also be return err } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -275,7 +275,7 @@ var getOrganizationCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, diff --git a/cmd/groups.go b/cmd/groups.go index 1dedda77..0fc7bfa3 100644 --- a/cmd/groups.go +++ b/cmd/groups.go @@ -4,11 +4,12 @@ import ( "context" "encoding/json" "fmt" + "os" + "strings" + l "github.com/uselagoon/machinery/api/lagoon" lclient "github.com/uselagoon/machinery/api/lagoon/client" s "github.com/uselagoon/machinery/api/schema" - "os" - "strings" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -262,7 +263,7 @@ var addGroupToOrganizationCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, diff --git a/cmd/import.go b/cmd/import.go index 1a142b6a..675f7ed9 100644 --- a/cmd/import.go +++ b/cmd/import.go @@ -52,7 +52,7 @@ You can get it to continue anyway with --keep-going. To disable any prompts, use } lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -115,7 +115,7 @@ You must specify to export a specific project by using the '-p ' f } lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) diff --git a/cmd/list.go b/cmd/list.go index 3df7a7e3..c269213c 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -82,7 +82,7 @@ var listDeployTargetsCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -204,7 +204,7 @@ var listEnvironmentsCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -264,7 +264,7 @@ var listVariablesCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -387,7 +387,7 @@ Without a group name, this query may time out in large Lagoon installs.`, return err } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -453,7 +453,7 @@ This query can take a long time to run if there are a lot of users.`, return err } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -505,7 +505,7 @@ var listUsersGroupsCmd = &cobra.Command{ return fmt.Errorf("Missing arguments: email address is not defined") } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -599,7 +599,7 @@ var listProjectGroupsCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -663,7 +663,7 @@ var listOrganizationProjectsCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -712,7 +712,7 @@ var listOrganizationGroupsCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -768,7 +768,7 @@ var listOrganizationDeployTargetsCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -819,7 +819,7 @@ var ListOrganizationUsersCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -864,7 +864,7 @@ var listOrganizationsCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, diff --git a/cmd/login.go b/cmd/login.go index 4a8c7ae2..36171e00 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -1,16 +1,18 @@ package cmd import ( + "encoding/json" "fmt" "net" "os" "path/filepath" - "strings" "github.com/spf13/cobra" + auth "github.com/uselagoon/machinery/utils/auth" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" - "golang.org/x/crypto/ssh/terminal" + "golang.org/x/oauth2" + terminal "golang.org/x/term" ) var loginCmd = &cobra.Command{ @@ -64,22 +66,32 @@ func publicKey(path string, skipAgent bool) (ssh.AuthMethod, func() error) { } func loginToken() error { - out, err := retrieveTokenViaSsh() - if err != nil { - return err - } - lc := lagoonCLIConfig.Lagoons[lagoonCLIConfig.Current] - lc.Token = out + if lc.KeycloakURL == "" || lc.SSHToken { + // if no keycloak url is found in the config, perform a token request via ssh + // or the ssh-token override is set to enforce tokens via ssh (accounts in CI jobs) + out, err := retrieveTokenViaSsh() + if err != nil { + return err + } + *lc.Grant = *out + } else { + // otherwise get a token via keycloak + token := &oauth2.Token{} + if lc.Grant != nil { + *token = *lc.Grant + } + _ = auth.TokenRequest(lc.KeycloakURL, "lagoon", lc.KeycloakIDP, token) + *lc.Grant = *token + } lagoonCLIConfig.Lagoons[lagoonCLIConfig.Current] = lc - if err = writeLagoonConfig(&lagoonCLIConfig, filepath.Join(configFilePath, configName+configExtension)); err != nil { + if err := writeLagoonConfig(&lagoonCLIConfig, filepath.Join(configFilePath, configName+configExtension)); err != nil { return fmt.Errorf("couldn't write config: %v", err) } - return nil } -func retrieveTokenViaSsh() (string, error) { +func retrieveTokenViaSsh() (*oauth2.Token, error) { skipAgent := false privateKey := fmt.Sprintf("%s/.ssh/id_rsa", userPath) // if the user has a key defined in their lagoon cli config, use it @@ -107,18 +119,21 @@ func retrieveTokenViaSsh() (string, error) { lagoonCLIConfig.Lagoons[lagoonCLIConfig.Current].Port) conn, err := ssh.Dial("tcp", sshHost, config) if err != nil { - return "", fmt.Errorf("couldn't connect to %s: %v", sshHost, err) + return nil, fmt.Errorf("couldn't connect to %s: %v", sshHost, err) } defer conn.Close() session, err := conn.NewSession() if err != nil { - return "", fmt.Errorf("couldn't open session: %v", err) + return nil, fmt.Errorf("couldn't open session: %v", err) } - out, err := session.CombinedOutput("token") + // use grant to get a full oauth token + out, err := session.CombinedOutput("grant") if err != nil { - return "", fmt.Errorf("couldn't get token: %v", err) + return nil, fmt.Errorf("couldn't get token: %v", err) } - return strings.TrimSpace(string(out)), err + token := &oauth2.Token{} + json.Unmarshal(out, token) + return token, err } diff --git a/cmd/notificationsemail.go b/cmd/notificationsemail.go index ac3f96f2..fd0326f9 100644 --- a/cmd/notificationsemail.go +++ b/cmd/notificationsemail.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/spf13/cobra" "github.com/uselagoon/lagoon-cli/internal/lagoon" "github.com/uselagoon/lagoon-cli/internal/lagoon/client" @@ -48,7 +49,7 @@ It does not configure a project to send notifications to email though, you need } if yesNo(fmt.Sprintf("You are attempting to create an email notification '%s' with email address '%s', are you sure?", name, email)) { current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -120,7 +121,7 @@ This command is used to add an existing email notification in Lagoon to a projec current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -160,7 +161,7 @@ var listProjectEmailsCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -201,7 +202,7 @@ var listAllEmailsCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -257,7 +258,7 @@ var deleteProjectEmailNotificationCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -302,7 +303,7 @@ var deleteEmailNotificationCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -359,7 +360,7 @@ var updateEmailNotificationCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) diff --git a/cmd/notificationsrocketchat.go b/cmd/notificationsrocketchat.go index c74b76fd..b5251f36 100644 --- a/cmd/notificationsrocketchat.go +++ b/cmd/notificationsrocketchat.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + l "github.com/uselagoon/machinery/api/lagoon" lclient "github.com/uselagoon/machinery/api/lagoon/client" s "github.com/uselagoon/machinery/api/schema" @@ -53,7 +54,7 @@ It does not configure a project to send notifications to RocketChat though, you } if yesNo(fmt.Sprintf("You are attempting to create an RocketChat notification '%s' with webhook '%s' channel '%s', are you sure?", name, webhook, channel)) { current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -128,7 +129,7 @@ This command is used to add an existing RocketChat notification in Lagoon to a p current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -168,7 +169,7 @@ var listProjectRocketChatsCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -211,7 +212,7 @@ var listAllRocketChatsCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -269,7 +270,7 @@ var deleteProjectRocketChatNotificationCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -314,7 +315,7 @@ var deleteRocketChatNotificationCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -376,7 +377,7 @@ var updateRocketChatNotificationCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) diff --git a/cmd/notificationsslack.go b/cmd/notificationsslack.go index 4d4074e0..7140ae81 100644 --- a/cmd/notificationsslack.go +++ b/cmd/notificationsslack.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + l "github.com/uselagoon/machinery/api/lagoon" lclient "github.com/uselagoon/machinery/api/lagoon/client" s "github.com/uselagoon/machinery/api/schema" @@ -53,7 +54,7 @@ It does not configure a project to send notifications to Slack though, you need } if yesNo(fmt.Sprintf("You are attempting to create an Slack notification '%s' with webhook '%s' channel '%s', are you sure?", name, webhook, channel)) { current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -128,7 +129,7 @@ This command is used to add an existing Slack notification in Lagoon to a projec current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -168,7 +169,7 @@ var listProjectSlacksCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -211,7 +212,7 @@ var listAllSlacksCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -269,7 +270,7 @@ var deleteProjectSlackNotificationCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -314,7 +315,7 @@ var deleteSlackNotificationCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -376,7 +377,7 @@ var updateSlackNotificationCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) diff --git a/cmd/notificationsteams.go b/cmd/notificationsteams.go index bac5212c..c79a0b0e 100644 --- a/cmd/notificationsteams.go +++ b/cmd/notificationsteams.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + l "github.com/uselagoon/machinery/api/lagoon" lclient "github.com/uselagoon/machinery/api/lagoon/client" s "github.com/uselagoon/machinery/api/schema" @@ -49,7 +50,7 @@ It does not configure a project to send notifications to Microsoft Teams though, } if yesNo(fmt.Sprintf("You are attempting to create a Microsoft Teams notification '%s' with webhook url '%s', are you sure?", name, webhook)) { current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -121,7 +122,7 @@ This command is used to add an existing Microsoft Teams notification in Lagoon t current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -161,7 +162,7 @@ var listProjectMicrosoftTeamsCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -202,7 +203,7 @@ var listAllMicrosoftTeamsCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -258,7 +259,7 @@ var deleteProjectMicrosoftTeamsNotificationCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -303,7 +304,7 @@ var deleteMicrosoftTeamsNotificationCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -360,7 +361,7 @@ var updateMicrosoftTeamsNotificationCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) diff --git a/cmd/notificationswebhook.go b/cmd/notificationswebhook.go index 35021065..dd3ba83b 100644 --- a/cmd/notificationswebhook.go +++ b/cmd/notificationswebhook.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + l "github.com/uselagoon/machinery/api/lagoon" lclient "github.com/uselagoon/machinery/api/lagoon/client" s "github.com/uselagoon/machinery/api/schema" @@ -49,7 +50,7 @@ It does not configure a project to send notifications to webhook though, you nee } if yesNo(fmt.Sprintf("You are attempting to create a webhook notification '%s' with webhook url '%s', are you sure?", name, webhook)) { current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -121,7 +122,7 @@ This command is used to add an existing webhook notification in Lagoon to a proj current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -161,7 +162,7 @@ var listProjectWebhooksCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -202,7 +203,7 @@ var listAllWebhooksCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -258,7 +259,7 @@ var deleteProjectWebhookNotificationCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -303,7 +304,7 @@ var deleteWebhookNotificationCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -360,7 +361,7 @@ var updateWebhookNotificationCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) diff --git a/cmd/organization.go b/cmd/organization.go index 4ca36b13..49529fe7 100644 --- a/cmd/organization.go +++ b/cmd/organization.go @@ -3,6 +3,7 @@ package cmd import ( "context" "fmt" + "github.com/spf13/cobra" "github.com/uselagoon/lagoon-cli/pkg/output" l "github.com/uselagoon/machinery/api/lagoon" @@ -59,7 +60,7 @@ var addOrgCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -112,7 +113,7 @@ var deleteOrgCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -185,7 +186,7 @@ var updateOrganizationCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, diff --git a/cmd/project.go b/cmd/project.go index 8a41a5a8..48831edb 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -4,11 +4,12 @@ import ( "context" "encoding/json" "fmt" + "os" + "strconv" + l "github.com/uselagoon/machinery/api/lagoon" lclient "github.com/uselagoon/machinery/api/lagoon/client" s "github.com/uselagoon/machinery/api/schema" - "os" - "strconv" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -159,7 +160,7 @@ var listProjectByMetadata = &cobra.Command{ return fmt.Errorf("Missing arguments: key is not defined") } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -222,7 +223,7 @@ var getProjectMetadata = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -279,7 +280,7 @@ var updateProjectMetadata = &cobra.Command{ } if yesNo(fmt.Sprintf("You are attempting to update key '%s' for project '%s' metadata, are you sure?", key, cmdProjectName)) { current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -334,7 +335,7 @@ var deleteProjectMetadataByKey = &cobra.Command{ } if yesNo(fmt.Sprintf("You are attempting to delete key '%s' from project '%s' metadata, are you sure?", key, cmdProjectName)) { current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -484,7 +485,7 @@ var addProjectToOrganizationCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -558,7 +559,7 @@ var RemoveProjectFromOrganizationCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, diff --git a/cmd/retrieve.go b/cmd/retrieve.go index d39e1d89..c7feba36 100644 --- a/cmd/retrieve.go +++ b/cmd/retrieve.go @@ -46,7 +46,7 @@ You can check the status of the backup using the list backups or get backup comm current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) diff --git a/cmd/root.go b/cmd/root.go index a98b4109..6abd11b6 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -397,7 +397,7 @@ func versionCheck(lagoon string) error { if lagoonCLIConfig.Lagoons[lagoon].Version == "" { lc := client.New( lagoonCLIConfig.Lagoons[lagoon].GraphQL, - lagoonCLIConfig.Lagoons[lagoon].Token, + lagoonCLIConfig.Lagoons[lagoon].Grant.AccessToken, lagoonCLIConfig.Lagoons[lagoon].Version, lagoonCLIVersion, debugEnable) diff --git a/cmd/shared.go b/cmd/shared.go index 7af150db..9c092d27 100644 --- a/cmd/shared.go +++ b/cmd/shared.go @@ -2,9 +2,10 @@ package cmd import ( "fmt" - "github.com/uselagoon/lagoon-cli/pkg/output" "os" "strings" + + "github.com/uselagoon/lagoon-cli/pkg/output" ) // config vars diff --git a/cmd/ssh.go b/cmd/ssh.go index 37b97350..07646ea0 100644 --- a/cmd/ssh.go +++ b/cmd/ssh.go @@ -46,7 +46,7 @@ var sshEnvCmd = &cobra.Command{ // if the config for this lagoon is set to use ssh portal support, handle that here lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) diff --git a/cmd/tasks.go b/cmd/tasks.go index 96f1a041..888a5708 100644 --- a/cmd/tasks.go +++ b/cmd/tasks.go @@ -43,7 +43,7 @@ var getTaskByID = &cobra.Command{ return fmt.Errorf("Missing arguments: ID is not defined") } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -102,7 +102,7 @@ If the task fails or fails to update, contact your Lagoon administrator for assi } if yesNo(fmt.Sprintf("You are attempting to run the active/standby switch for project '%s', are you sure?", cmdProjectName)) { current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -304,7 +304,7 @@ var uploadFilesToTask = &cobra.Command{ return err } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, diff --git a/cmd/users.go b/cmd/users.go index 4cdec7a4..d26ae991 100644 --- a/cmd/users.go +++ b/cmd/users.go @@ -166,7 +166,7 @@ var deleteSSHKeyCmd = &cobra.Command{ return nil } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -263,7 +263,7 @@ var getUserKeysCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -317,7 +317,7 @@ var getAllUserKeysCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -392,7 +392,7 @@ var addUserToOrganizationCmd = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, @@ -455,7 +455,7 @@ var RemoveUserFromOrganization = &cobra.Command{ } current := lagoonCLIConfig.Current - token := lagoonCLIConfig.Lagoons[current].Token + token := lagoonCLIConfig.Lagoons[current].Grant.AccessToken lc := lclient.New( lagoonCLIConfig.Lagoons[current].GraphQL, lagoonCLIVersion, diff --git a/cmd/variables.go b/cmd/variables.go index 1924b6e9..1b6919d6 100644 --- a/cmd/variables.go +++ b/cmd/variables.go @@ -51,7 +51,7 @@ var addVariableCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) @@ -131,7 +131,7 @@ var deleteVariableCmd = &cobra.Command{ current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) diff --git a/cmd/whoami.go b/cmd/whoami.go index 14d914a8..aebce030 100644 --- a/cmd/whoami.go +++ b/cmd/whoami.go @@ -34,7 +34,7 @@ This is useful if you have multiple keys or accounts in multiple lagoons and nee current := lagoonCLIConfig.Current lc := client.New( lagoonCLIConfig.Lagoons[current].GraphQL, - lagoonCLIConfig.Lagoons[current].Token, + lagoonCLIConfig.Lagoons[current].Grant.AccessToken, lagoonCLIConfig.Lagoons[current].Version, lagoonCLIVersion, debug) diff --git a/docs/commands/lagoon_config_add.md b/docs/commands/lagoon_config_add.md index d943a3a8..70310ffd 100644 --- a/docs/commands/lagoon_config_add.md +++ b/docs/commands/lagoon_config_add.md @@ -9,15 +9,26 @@ lagoon config add [flags] ### Options ``` - --create-config Create the config file if it is non existent (to be used with --config-file) - -g, --graphql string Lagoon GraphQL endpoint - -h, --help help for add - -H, --hostname string Lagoon SSH hostname - -k, --kibana string Lagoon Kibana URL (https://logs.amazeeio.cloud) - -P, --port string Lagoon SSH port - --ssh-key string SSH Key to use for this cluster for generating tokens - -t, --token string Lagoon GraphQL token - -u, --ui string Lagoon UI location (https://dashboard.amazeeio.cloud) + --create-config Create the config file if it is non existent (to be used with --config-file) + -g, --graphql string Lagoon GraphQL endpoint (eg, https://api.amazeeio.cloud/graphql) + -h, --help help for add + -H, --hostname string Lagoon token endpoint hostname (eg, token.amazeeio.cloud) + --keycloak-idp string Optional: Lagoon Keycloak Identity Provider name. + Set this to the name of the separate Identity Provider within keycloak if you use one. + You may need to check with your Lagoon administrator if you use another SSO provider + -K, --keycloak-url string Lagoon Keycloak URL (eg, https://keycloak.amazeeio.cloud). + Setting this will use keycloak for authentication instead of SSH based tokens. + Set 'ssh-token=true' to override. + Note: SSH keys are still required for SSH access. + -k, --kibana string Optional: Lagoon Kibana URL (eg, https://logs.amazeeio.cloud) + -P, --port string Lagoon token endpoint port (22) + --ssh-key string SSH Key to use for this cluster for generating tokens + --ssh-token Set this context to only use ssh based tokens + Set this to only use SSH based tokens if you're using the CLI in CI jobs or other automated processes + where logging in via keycloak is not possible. + This is enabled by default, it will be disabled by default in a future release. (default true) + -t, --token string Lagoon GraphQL token + -u, --ui string Optional: Lagoon UI location (eg, https://dashboard.amazeeio.cloud) ``` ### Options inherited from parent commands diff --git a/go.mod b/go.mod index 27ff3ee7..410fc15b 100644 --- a/go.mod +++ b/go.mod @@ -19,8 +19,10 @@ require ( github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.2 - github.com/uselagoon/machinery v0.0.16 - golang.org/x/crypto v0.17.0 + github.com/uselagoon/machinery v0.0.17-0.20240215231357-96d15325daee + golang.org/x/crypto v0.19.0 + golang.org/x/oauth2 v0.17.0 + golang.org/x/term v0.17.0 gopkg.in/yaml.v3 v3.0.1 sigs.k8s.io/yaml v1.4.0 ) @@ -29,6 +31,7 @@ require ( github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect @@ -36,8 +39,10 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.31.0 // indirect ) //replace github.com/uselagoon/machinery => ../machinery diff --git a/go.sum b/go.sum index c569c611..b9372dba 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,11 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v0.0.0-20180716180158-c0b63e2f9bb1 h1:tV3a8xSFYfQgA9b54eOE0A6Db//aeD+SQWawHfhaoLs= @@ -66,17 +71,22 @@ github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/uselagoon/machinery v0.0.16 h1:LGWUaESTXPfiTyJTJCC1i+3ghidY2qsf9li7mGbH9Wo= -github.com/uselagoon/machinery v0.0.16/go.mod h1:Duljjz/3d/7m0jbmF1nVRDTNaMxMr6m+5LkgjiRrQaU= +github.com/uselagoon/machinery v0.0.17-0.20240215231357-96d15325daee h1:GjDAZCm8BtTmld9DLALCgN4AgqPRxXymPaHyJ3cBLkY= +github.com/uselagoon/machinery v0.0.17-0.20240215231357-96d15325daee/go.mod h1:LZwsUBTxUTh6u245YKwJH1+q/VNfrEIlQ0DHhR1m1cQ= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -86,19 +96,27 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/lagoon/config.go b/internal/lagoon/config.go index 507fa516..d9632f3f 100644 --- a/internal/lagoon/config.go +++ b/internal/lagoon/config.go @@ -1,5 +1,9 @@ package lagoon +import ( + "golang.org/x/oauth2" +) + // Config is used for the lagoon configuration. type Config struct { Current string `json:"current"` @@ -11,13 +15,16 @@ type Config struct { // Context is used for each lagoon context in the config file. type Context struct { - GraphQL string `json:"graphql"` - HostName string `json:"hostname"` - UI string `json:"ui,omitempty"` - Kibana string `json:"kibana,omitempty"` - Port string `json:"port"` - Token string `json:"token,omitempty"` - Version string `json:"version,omitempty"` - SSHKey string `json:"sshkey,omitempty"` - SSHPortal bool `json:"sshPortal,omitempty"` + GraphQL string `json:"graphql"` + HostName string `json:"hostname"` + UI string `json:"ui,omitempty"` + Kibana string `json:"kibana,omitempty"` + Port string `json:"port"` + Token string `json:"token,omitempty"` + Grant *oauth2.Token `json:"grant,omitempty"` + Version string `json:"version,omitempty"` + SSHKey string `json:"sshkey,omitempty"` + SSHToken bool `json:"sshToken,omitempty"` + KeycloakURL string `json:"keycloakUrl,omitempty"` + KeycloakIDP string `json:"keycloakidp,omitempty"` } diff --git a/pkg/graphql/main.go b/pkg/graphql/main.go index b091b559..93959068 100644 --- a/pkg/graphql/main.go +++ b/pkg/graphql/main.go @@ -10,7 +10,7 @@ import ( func LagoonAPI(lc *lagoon.Config, debug bool) (api.Client, error) { lagoon := lc.Current lagoonAPI, err := api.NewWithToken( - lc.Lagoons[lagoon].Token, + lc.Lagoons[lagoon].Grant.AccessToken, lc.Lagoons[lagoon].GraphQL, ) lagoonAPI.Debug(debug) @@ -21,14 +21,18 @@ func LagoonAPI(lc *lagoon.Config, debug bool) (api.Client, error) { } func hasValidToken(lc *lagoon.Config, lagoon string) bool { - return lc.Lagoons[lagoon].Token != "" + if lc.Lagoons[lagoon].Grant == nil { + return lc.Lagoons[lagoon].Token != "" + } else { + return lc.Lagoons[lagoon].Grant.AccessToken != "" + } } // VerifyTokenExpiry verfies if the current token is valid or not func VerifyTokenExpiry(lc *lagoon.Config, lagoon string) bool { var p jwt.Parser token, _, err := p.ParseUnverified( - lc.Lagoons[lagoon].Token, &jwt.StandardClaims{}) + lc.Lagoons[lagoon].Grant.AccessToken, &jwt.StandardClaims{}) if err != nil { return false } diff --git a/pkg/graphql/main_test.go b/pkg/graphql/main_test.go index a1505738..9aedbeaf 100644 --- a/pkg/graphql/main_test.go +++ b/pkg/graphql/main_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/uselagoon/lagoon-cli/internal/lagoon" + "golang.org/x/oauth2" ) func TestValidateToken(t *testing.T) { @@ -16,7 +17,9 @@ func TestValidateToken(t *testing.T) { } // set the context with a valid token lc.Lagoons["test"] = lagoon.Context{ - Token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1NzI3Nzk5MjYsImV4cCI6NDc1OTk4OTUzMSwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.9XKxJps00mGzgneaEp0nmI8aXlolMrD-Do2IooTP7d0", + Grant: &oauth2.Token{ + AccessToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1NzI3Nzk5MjYsImV4cCI6NDc1OTk4OTUzMSwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.9XKxJps00mGzgneaEp0nmI8aXlolMrD-Do2IooTP7d0", + }, } got = hasValidToken(&lc, "test") if got == false { @@ -28,7 +31,9 @@ func TestValidateToken(t *testing.T) { } // set the context with an expired token lc.Lagoons["test"] = lagoon.Context{ - Token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1NDEyNDM0MTMsImV4cCI6MTU0MTI0NDYxNCwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.2wkAPeyVHAhGps_OEe_a8RXmv7_9GwP4ttFsjrLPZ84", + Grant: &oauth2.Token{ + AccessToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1NDEyNDM0MTMsImV4cCI6MTU0MTI0NDYxNCwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.2wkAPeyVHAhGps_OEe_a8RXmv7_9GwP4ttFsjrLPZ84", + }, } got = VerifyTokenExpiry(&lc, "test") if got == true { diff --git a/pkg/lagoon/ssh/main.go b/pkg/lagoon/ssh/main.go index ea297dd4..225efa79 100644 --- a/pkg/lagoon/ssh/main.go +++ b/pkg/lagoon/ssh/main.go @@ -7,7 +7,7 @@ import ( "os" "golang.org/x/crypto/ssh" - "golang.org/x/crypto/ssh/terminal" + terminal "golang.org/x/term" ) // InteractiveSSH .