-
Notifications
You must be signed in to change notification settings - Fork 118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Auth list providers #1187
Auth list providers #1187
Changes from 4 commits
393a290
c8f7ada
db3c89f
6263db2
a4097d8
e7c945d
8efad16
3bdeaad
c3b0ea6
f61e196
aa74c1a
fa4be9d
e5fd923
69b41ad
25c162a
5e35527
21c8290
cb2df28
bd9084b
889bed6
56fd31a
4224085
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,16 +9,20 @@ import ( | |
|
||
"github.com/dcos/dcos-cli/pkg/config" | ||
"github.com/spf13/cobra" | ||
"io" | ||
) | ||
|
||
// rootCmd represents the base command when called without any subcommands. | ||
var rootCmd = &cobra.Command{ | ||
Use: "dcos", | ||
} | ||
|
||
var dcosConfig *Cluster | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should avoid global variables in general, I know there are already some but this will be refactored. How about calling |
||
|
||
// Execute adds all child commands to the root command and sets flags appropriately. | ||
// This is called by main.main(). It only needs to happen once to the rootCmd. | ||
func Execute() { | ||
dcosConfig = attachedCluster() | ||
if err := rootCmd.Execute(); err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
|
@@ -46,8 +50,27 @@ func attachedCluster() *Cluster { | |
os.Exit(1) | ||
} | ||
|
||
// Default to the old config path. If .dcos/clusters exists, it will search that for an attached cluster and if it | ||
// finds one there, it will override this var to use that cluster's config instead. | ||
configPath := filepath.Join(usr.HomeDir, ".dcos") | ||
|
||
clustersDir := filepath.Join(configPath, "clusters") | ||
|
||
// Find the config of the attached cluster. | ||
err = filepath.Walk(clustersDir, func(path string, info os.FileInfo, err error) error { | ||
if filepath.Base(path) == "attached" { | ||
configPath = filepath.Dir(path) | ||
return io.EOF | ||
} | ||
return nil | ||
}) | ||
if err != io.EOF && err != filepath.SkipDir && err != nil { | ||
fmt.Println(err) | ||
} | ||
|
||
|
||
// Make sure "~/.dcos/dcos.toml" exists. | ||
path := filepath.Join(dir, "dcos.toml") | ||
path := filepath.Join(configPath, "dcos.toml") | ||
f, err := os.OpenFile(path, os.O_RDONLY|os.O_CREATE, 0600) | ||
if err != nil { | ||
fmt.Printf("Couldn't create config file \"%s\": %s.\n", path, err) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package cmd | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"github.com/dcos/dcos-cli/pkg/httpclient" | ||
"github.com/olekukonko/tablewriter" | ||
"github.com/spf13/cobra" | ||
"os" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. imports should be sorted as mentioned before. |
||
) | ||
|
||
// These are the different auth types that DC/OS supports with the names that they'll be given from the providers | ||
// endpoint. | ||
const ( | ||
AuthTypeDCOSUidPassword = "dcos-uid-password" | ||
AuthTypeDCOSUidServiceKey = "dcos-uid-servicekey" | ||
AuthTypeDCOSUidPasswordLDAP = "dcos-uid-password-ldap" | ||
AuthTypeSAMLSpInitiated = "saml-sp-initiated" | ||
AuthTypeOIDCAuthCodeFlow = "oidc-authorization-code-flow" | ||
AuthTypeOIDCImplicitFlow = "oidc-implicit-flow" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JP suggests using the "login provider" semantic here, as authentication is only about sending the ACS token to DC/OS. We still have quite some docs using "auth provider" wording, but I'd rather use the wording recommended by the security team. |
||
) | ||
|
||
var jsonOutput bool | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would require having constructor-based commands rather than global variables like (I'm leaving this comment for reference not necessarily to tackle in this PR). |
||
|
||
// authCmd represents the `dcos auth` subcommand. | ||
var authListProvidersCmd = &cobra.Command{ | ||
Use: "list-providers", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
Run: listProviders, | ||
} | ||
|
||
func init() { | ||
authCmd.AddCommand(authListProvidersCmd) | ||
authListProvidersCmd.Flags().BoolVar(&jsonOutput, "json", false, | ||
"returns providers in json format") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no line-length limit in Go, we use ~110 characters as a rule of thumb, a single line is fine here. |
||
} | ||
|
||
func listProviders(cmd *cobra.Command, args []string) { | ||
providers, err := getProviders() | ||
if err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of printing and calling exit like here or below, other commands define the |
||
} | ||
|
||
if jsonOutput { | ||
// re-marshal it into json with indents added in for pretty printing | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use sentences starting with uppercase and ending with a dot. The cmd package was a quick proof of concept but we started doing this in other packages. |
||
out, err := json.MarshalIndent(providers, "", "\t") | ||
if err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
fmt.Println(string(out)) | ||
} else { | ||
table := tablewriter.NewWriter(os.Stdout) | ||
table.SetHeader([]string{"PROVIDER ID", "AUTHENTICATION TYPE"}) | ||
// turn off wrapping because it seems to wrap even if the column is set to be wide enough | ||
table.SetAutoWrapText(false) | ||
table.SetBorder(false) | ||
table.SetRowSeparator(" ") | ||
table.SetColumnSeparator(" ") | ||
table.SetCenterSeparator(" ") | ||
|
||
for name, provider := range *providers { | ||
desc, err := authTypeDescription(provider.AuthenticationType, provider) | ||
if err != nil { | ||
fmt.Printf("Unknown authentication type %s", provider.AuthenticationType) | ||
os.Exit(1) | ||
} | ||
table.Append([]string{name, desc}) | ||
} | ||
table.Render() | ||
} | ||
} | ||
|
||
func getProviders() (*map[string]authProvider, error) { | ||
var config = dcosConfig.Config | ||
var client = httpclient.New(config) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
var response, err = client.Get("/acs/api/v1/auth/providers") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the short variable declaration is more common in go, that's what we currently use, eg. |
||
if err != nil { | ||
return nil, err | ||
} | ||
defer response.Body.Close() | ||
|
||
var resp map[string]authProvider | ||
err = json.NewDecoder(response.Body).Decode(&resp) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &resp, nil | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit, this is enough as callers must check for the error and not for the pointer to be nil : err = json.NewDecoder(response.Body).Decode(&resp)
return &resp, err |
||
} | ||
|
||
func authTypeDescription(authType string, provider authProvider) (string, error) { | ||
var desc string | ||
if authType == AuthTypeDCOSUidPassword { | ||
desc = "Log in using a standard DC/OS user account (username and password)" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: returning directly from switch cases removes the need for the lines surrounding the switch block. |
||
} else if authType == AuthTypeDCOSUidServiceKey { | ||
desc = "Log in using a DC/OS service user account (username and private key)" | ||
} else if authType == AuthTypeDCOSUidPasswordLDAP { | ||
desc = "Log in in using an LDAP user account (username and password)" | ||
} else if authType == AuthTypeSAMLSpInitiated { | ||
desc = fmt.Sprintf("Log in using SAML 2.0 (%s)", provider.Description) | ||
} else if authType == AuthTypeOIDCAuthCodeFlow || authType == AuthTypeOIDCImplicitFlow { | ||
desc = fmt.Sprintf("Log in using OpenID Connect(%s)", provider.Description) | ||
} else { | ||
return "", errors.New("unknown authentication type") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A switch statement would be more readable here. |
||
} | ||
return desc, nil | ||
} | ||
|
||
type authProvider struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As commented above : |
||
AuthenticationType string `json:"authentication-type"` | ||
ClientMethod string `json:"client-method"` | ||
Config authListProviderConfig `json:"config"` | ||
Description string `json:"description"` | ||
} | ||
|
||
type authListProviderConfig struct { | ||
StartFlowUrl string `json:"start_flow_url"` | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should go in the previous import section (https://github.com/golang/go/wiki/CodeReviewComments#imports).