github.com/pachyderm/pachyderm@v1.13.4/src/server/auth/cmds/cmds.go (about) 1 package cmds 2 3 import ( 4 "bufio" 5 "fmt" 6 "os" 7 "strings" 8 "text/template" 9 "time" 10 11 "github.com/pachyderm/pachyderm/src/client" 12 "github.com/pachyderm/pachyderm/src/client/auth" 13 "github.com/pachyderm/pachyderm/src/client/pkg/config" 14 "github.com/pachyderm/pachyderm/src/client/pkg/errors" 15 "github.com/pachyderm/pachyderm/src/client/pkg/grpcutil" 16 "github.com/pachyderm/pachyderm/src/server/pkg/cmdutil" 17 "github.com/pkg/browser" 18 19 "github.com/spf13/cobra" 20 ) 21 22 var githubAuthLink = `https://github.com/login/oauth/authorize?client_id=d3481e92b4f09ea74ff8&redirect_uri=https%3A%2F%2Fpachyderm.io%2Flogin-hook%2Fdisplay-token.html` 23 24 func githubLogin() (string, error) { 25 fmt.Println("(1) Please paste this link into a browser:\n\n" + 26 githubAuthLink + "\n\n" + 27 "(You will be directed to GitHub and asked to authorize Pachyderm's " + 28 "login app on GitHub. If you accept, you will be given a token to " + 29 "paste here, which will give you an externally verified account in " + 30 "this Pachyderm cluster)\n\n(2) Please paste the token you receive " + 31 "from GitHub here:") 32 token, err := cmdutil.ReadPassword("") 33 if err != nil { 34 return "", errors.Wrapf(err, "error reading token") 35 } 36 return strings.TrimSpace(token), nil // drop trailing newline 37 } 38 39 func requestOIDCLogin(c *client.APIClient) (string, error) { 40 var authURL string 41 loginInfo, err := c.GetOIDCLogin(c.Ctx(), &auth.GetOIDCLoginRequest{}) 42 if err != nil { 43 return "", err 44 } 45 authURL = loginInfo.LoginURL 46 state := loginInfo.State 47 48 // print the prepared URL and promp the user to click on it 49 fmt.Println("You will momentarily be directed to your IdP and asked to authorize Pachyderm's " + 50 "login app on your IdP.\n\nPaste the following URL into a browser if not automatically redirected:\n\n" + 51 authURL + "\n\n" + 52 "") 53 54 err = browser.OpenURL(authURL) 55 if err != nil { 56 return "", err 57 } 58 59 return state, nil 60 } 61 62 // ActivateCmd returns a cobra.Command to activate Pachyderm's auth system 63 func ActivateCmd() *cobra.Command { 64 var initialAdmin string 65 activate := &cobra.Command{ 66 Short: "Activate Pachyderm's auth system", 67 Long: ` 68 Activate Pachyderm's auth system, and restrict access to existing data to the 69 user running the command (or the argument to --initial-admin), who will be the 70 first cluster admin`[1:], 71 Run: cmdutil.Run(func(args []string) error { 72 var token string 73 var err error 74 75 if !strings.HasPrefix(initialAdmin, auth.RobotPrefix) { 76 token, err = githubLogin() 77 if err != nil { 78 return err 79 } 80 } 81 // Exchange GitHub token for Pachyderm token 82 c, err := client.NewOnUserMachine("user") 83 if err != nil { 84 return errors.Wrapf(err, "could not connect") 85 } 86 defer c.Close() 87 88 fmt.Println("Retrieving Pachyderm token...") 89 90 resp, err := c.Activate(c.Ctx(), 91 &auth.ActivateRequest{ 92 GitHubToken: token, 93 Subject: initialAdmin, 94 }) 95 if err != nil { 96 return errors.Wrapf(grpcutil.ScrubGRPC(err), "error activating Pachyderm auth") 97 } 98 99 if err := config.WritePachTokenToConfig(resp.PachToken); err != nil { 100 return err 101 } 102 if strings.HasPrefix(initialAdmin, auth.RobotPrefix) { 103 fmt.Println("WARNING: DO NOT LOSE THE ROBOT TOKEN BELOW WITHOUT " + 104 "ADDING OTHER ADMINS.\nIF YOU DO, YOU WILL BE PERMANENTLY LOCKED OUT " + 105 "OF YOUR CLUSTER!") 106 fmt.Printf("Pachyderm token for \"%s\":\n%s\n", initialAdmin, resp.PachToken) 107 } 108 return nil 109 }), 110 } 111 activate.PersistentFlags().StringVar(&initialAdmin, "initial-admin", "", ` 112 The subject (robot user or github user) who 113 will be the first cluster admin; the user running 'activate' will identify as 114 this user once auth is active. If you set 'initial-admin' to a robot 115 user, pachctl will print that robot user's Pachyderm token; this token is 116 effectively a root token, and if it's lost you will be locked out of your 117 cluster`[1:]) 118 return cmdutil.CreateAlias(activate, "auth activate") 119 } 120 121 // DeactivateCmd returns a cobra.Command to delete all ACLs, tokens, and admins, 122 // deactivating Pachyderm's auth system 123 func DeactivateCmd() *cobra.Command { 124 deactivate := &cobra.Command{ 125 Short: "Delete all ACLs, tokens, and admins, and deactivate Pachyderm auth", 126 Long: "Deactivate Pachyderm's auth system, which will delete ALL auth " + 127 "tokens, ACLs and admins, and expose all data in the cluster to any " + 128 "user with cluster access. Use with caution.", 129 Run: cmdutil.Run(func(args []string) error { 130 fmt.Println("Are you sure you want to delete ALL auth information " + 131 "(ACLs, tokens, and admins) in this cluster, and expose ALL data? yN") 132 confirm, err := bufio.NewReader(os.Stdin).ReadString('\n') 133 if err != nil { 134 return err 135 } 136 if !strings.Contains("yY", confirm[:1]) { 137 return errors.Errorf("operation aborted") 138 } 139 c, err := client.NewOnUserMachine("user") 140 if err != nil { 141 return errors.Wrapf(err, "could not connect") 142 } 143 defer c.Close() 144 _, err = c.Deactivate(c.Ctx(), &auth.DeactivateRequest{}) 145 return grpcutil.ScrubGRPC(err) 146 }), 147 } 148 return cmdutil.CreateAlias(deactivate, "auth deactivate") 149 } 150 151 // LoginCmd returns a cobra.Command to login to a Pachyderm cluster with your 152 // GitHub account. Any resources that have been restricted to the email address 153 // registered with your GitHub account will subsequently be accessible. 154 func LoginCmd() *cobra.Command { 155 var useOTP bool 156 login := &cobra.Command{ 157 Short: "Log in to Pachyderm", 158 Long: "Login to Pachyderm. Any resources that have been restricted to " + 159 "the account you have with your ID provider (e.g. GitHub, Okta) " + 160 "account will subsequently be accessible.", 161 Run: cmdutil.Run(func([]string) error { 162 c, err := client.NewOnUserMachine("user") 163 if err != nil { 164 return errors.Wrapf(err, "could not connect") 165 } 166 defer c.Close() 167 168 // Issue authentication request to Pachyderm and get response 169 var resp *auth.AuthenticateResponse 170 var authErr error 171 if useOTP { 172 // Exhange short-lived Pachyderm auth code for long-lived Pachyderm token 173 code, err := cmdutil.ReadPassword("One-Time Password:") 174 if err != nil { 175 return errors.Wrapf(err, "error reading One-Time Password") 176 } 177 code = strings.TrimSpace(code) // drop trailing newline 178 resp, authErr = c.Authenticate( 179 c.Ctx(), 180 &auth.AuthenticateRequest{OneTimePassword: code}) 181 } else if state, err := requestOIDCLogin(c); err == nil { 182 // Exchange OIDC token for Pachyderm token 183 fmt.Println("Retrieving Pachyderm token...") 184 resp, authErr = c.Authenticate( 185 c.Ctx(), 186 &auth.AuthenticateRequest{OIDCState: state}) 187 if authErr != nil && !auth.IsErrPartiallyActivated(authErr) { 188 return errors.Wrapf(grpcutil.ScrubGRPC(authErr), 189 "authorization failed (OIDC state token: %q; Pachyderm logs may "+ 190 "contain more information)", 191 // Print state token as it's logged, for easy searching 192 fmt.Sprintf("%s.../%d", state[:len(state)/2], len(state))) 193 } 194 } else { 195 // Exchange GitHub token for Pachyderm token 196 token, err := githubLogin() 197 if err != nil { 198 return err 199 } 200 fmt.Println("Retrieving Pachyderm token...") 201 resp, authErr = c.Authenticate( 202 c.Ctx(), 203 &auth.AuthenticateRequest{GitHubToken: token}) 204 } 205 206 // Write new Pachyderm token to config 207 if auth.IsErrPartiallyActivated(authErr) { 208 return errors.Wrapf(authErr, "Pachyderm auth is partially activated "+ 209 "(if pachyderm is stuck in this state, you can revert by running "+ 210 "'pachctl auth deactivate' or retry by running 'pachctl auth "+ 211 "activate' again)") 212 } else if authErr != nil { 213 return errors.Wrapf(grpcutil.ScrubGRPC(authErr), "error authenticating with Pachyderm cluster") 214 } 215 return config.WritePachTokenToConfig(resp.PachToken) 216 }), 217 } 218 login.PersistentFlags().BoolVarP(&useOTP, "one-time-password", "o", false, 219 "If set, authenticate with a Dash-provided One-Time Password, rather than "+ 220 "via GitHub") 221 return cmdutil.CreateAlias(login, "auth login") 222 } 223 224 // LogoutCmd returns a cobra.Command that deletes your local Pachyderm 225 // credential, logging you out of your cluster. Note that this is not necessary 226 // to do before logging in as another user, but is useful for testing. 227 func LogoutCmd() *cobra.Command { 228 logout := &cobra.Command{ 229 Short: "Log out of Pachyderm by deleting your local credential", 230 Long: "Log out of Pachyderm by deleting your local credential. Note that " + 231 "it's not necessary to log out before logging in with another account " + 232 "(simply run 'pachctl auth login' twice) but 'logout' can be useful on " + 233 "shared workstations.", 234 Run: cmdutil.Run(func([]string) error { 235 cfg, err := config.Read(false, false) 236 if err != nil { 237 return errors.Wrapf(err, "error reading Pachyderm config (for cluster address)") 238 } 239 _, context, err := cfg.ActiveContext(true) 240 if err != nil { 241 return errors.Wrapf(err, "error getting the active context") 242 } 243 context.SessionToken = "" 244 return cfg.Write() 245 }), 246 } 247 return cmdutil.CreateAlias(logout, "auth logout") 248 } 249 250 // WhoamiCmd returns a cobra.Command that deletes your local Pachyderm 251 // credential, logging you out of your cluster. Note that this is not necessary 252 // to do before logging in as another user, but is useful for testing. 253 func WhoamiCmd() *cobra.Command { 254 whoami := &cobra.Command{ 255 Short: "Print your Pachyderm identity", 256 Long: "Print your Pachyderm identity.", 257 Run: cmdutil.Run(func([]string) error { 258 c, err := client.NewOnUserMachine("user") 259 if err != nil { 260 return errors.Wrapf(err, "could not connect") 261 } 262 defer c.Close() 263 resp, err := c.WhoAmI(c.Ctx(), &auth.WhoAmIRequest{}) 264 if err != nil { 265 return errors.Wrapf(grpcutil.ScrubGRPC(err), "error") 266 } 267 fmt.Printf("You are \"%s\"\n", resp.Username) 268 if resp.TTL > 0 { 269 fmt.Printf("session expires: %v\n", time.Now().Add(time.Duration(resp.TTL)*time.Second).Format(time.RFC822)) 270 } 271 if resp.IsAdmin { 272 fmt.Println("You are an administrator of this Pachyderm cluster") 273 } 274 return nil 275 }), 276 } 277 return cmdutil.CreateAlias(whoami, "auth whoami") 278 } 279 280 // CheckCmd returns a cobra command that sends an "Authorize" RPC to Pachd, to 281 // determine whether the specified user has access to the specified repo. 282 func CheckCmd() *cobra.Command { 283 check := &cobra.Command{ 284 Use: "{{alias}} (none|reader|writer|owner) <repo>", 285 Short: "Check whether you have reader/writer/etc-level access to 'repo'", 286 Long: "Check whether you have reader/writer/etc-level access to 'repo'. " + 287 "For example, 'pachctl auth check reader private-data' prints \"true\" " + 288 "if the you have at least \"reader\" access to the repo " + 289 "\"private-data\" (you could be a reader, writer, or owner). Unlike " + 290 "`pachctl auth get`, you do not need to have access to 'repo' to " + 291 "discover your own access level.", 292 Run: cmdutil.RunFixedArgs(2, func(args []string) error { 293 scope, err := auth.ParseScope(args[0]) 294 if err != nil { 295 return err 296 } 297 repo := args[1] 298 c, err := client.NewOnUserMachine("user") 299 if err != nil { 300 return errors.Wrapf(err, "could not connect") 301 } 302 defer c.Close() 303 resp, err := c.Authorize(c.Ctx(), &auth.AuthorizeRequest{ 304 Repo: repo, 305 Scope: scope, 306 }) 307 if err != nil { 308 return grpcutil.ScrubGRPC(err) 309 } 310 fmt.Printf("%t\n", resp.Authorized) 311 return nil 312 }), 313 } 314 return cmdutil.CreateAlias(check, "auth check") 315 } 316 317 // GetCmd returns a cobra command that gets either the ACL for a Pachyderm 318 // repo or another user's scope of access to that repo 319 func GetCmd() *cobra.Command { 320 get := &cobra.Command{ 321 Use: "{{alias}} [<username>] <repo>", 322 Short: "Get the ACL for 'repo' or the access that 'username' has to 'repo'", 323 Long: "Get the ACL for 'repo' or the access that 'username' has to " + 324 "'repo'. For example, 'pachctl auth get github-alice private-data' " + 325 "prints \"reader\", \"writer\", \"owner\", or \"none\", depending on " + 326 "the privileges that \"github-alice\" has in \"repo\". Currently all " + 327 "Pachyderm authentication uses GitHub OAuth, so 'username' must be a " + 328 "GitHub username", 329 Run: cmdutil.RunBoundedArgs(1, 2, func(args []string) error { 330 c, err := client.NewOnUserMachine("user") 331 if err != nil { 332 return errors.Wrapf(err, "could not connect") 333 } 334 defer c.Close() 335 if len(args) == 1 { 336 // Get ACL for a repo 337 repo := args[0] 338 resp, err := c.GetACL(c.Ctx(), &auth.GetACLRequest{ 339 Repo: repo, 340 }) 341 if err != nil { 342 return grpcutil.ScrubGRPC(err) 343 } 344 t := template.Must(template.New("ACLEntries").Parse( 345 "{{range .}}{{.Username }}: {{.Scope}}\n{{end}}")) 346 return t.Execute(os.Stdout, resp.Entries) 347 } 348 // Get User's scope on an acl 349 username, repo := args[0], args[1] 350 resp, err := c.GetScope(c.Ctx(), &auth.GetScopeRequest{ 351 Repos: []string{repo}, 352 Username: username, 353 }) 354 if err != nil { 355 return grpcutil.ScrubGRPC(err) 356 } 357 fmt.Println(resp.Scopes[0].String()) 358 return nil 359 }), 360 } 361 return cmdutil.CreateAlias(get, "auth get") 362 } 363 364 // SetScopeCmd returns a cobra command that lets a user set the level of access 365 // that another user has to a repo 366 func SetScopeCmd() *cobra.Command { 367 setScope := &cobra.Command{ 368 Use: "{{alias}} <username> (none|reader|writer|owner) <repo>", 369 Short: "Set the scope of access that 'username' has to 'repo'", 370 Long: "Set the scope of access that 'username' has to 'repo'. For " + 371 "example, 'pachctl auth set github-alice none private-data' prevents " + 372 "\"github-alice\" from interacting with the \"private-data\" repo in any " + 373 "way (the default). Similarly, 'pachctl auth set github-alice reader " + 374 "private-data' would let \"github-alice\" read from \"private-data\" but " + 375 "not create commits (writer) or modify the repo's access permissions " + 376 "(owner). Currently all Pachyderm authentication uses GitHub OAuth, so " + 377 "'username' must be a GitHub username", 378 Run: cmdutil.RunFixedArgs(3, func(args []string) error { 379 scope, err := auth.ParseScope(args[1]) 380 if err != nil { 381 return err 382 } 383 username, repo := args[0], args[2] 384 c, err := client.NewOnUserMachine("user") 385 if err != nil { 386 return errors.Wrapf(err, "could not connect") 387 } 388 defer c.Close() 389 _, err = c.SetScope(c.Ctx(), &auth.SetScopeRequest{ 390 Repo: repo, 391 Scope: scope, 392 Username: username, 393 }) 394 return grpcutil.ScrubGRPC(err) 395 }), 396 } 397 return cmdutil.CreateAlias(setScope, "auth set") 398 } 399 400 // ListAdminsCmd returns a cobra command that lists the current cluster admins 401 func ListAdminsCmd() *cobra.Command { 402 listAdmins := &cobra.Command{ 403 Short: "List the current cluster admins", 404 Long: "List the current cluster admins", 405 Run: cmdutil.Run(func([]string) error { 406 c, err := client.NewOnUserMachine("user") 407 if err != nil { 408 return err 409 } 410 defer c.Close() 411 resp, err := c.GetAdmins(c.Ctx(), &auth.GetAdminsRequest{}) 412 if err != nil { 413 return grpcutil.ScrubGRPC(err) 414 } 415 for _, user := range resp.Admins { 416 fmt.Println(user) 417 } 418 return nil 419 }), 420 } 421 return cmdutil.CreateAlias(listAdmins, "auth list-admins") 422 } 423 424 // ModifyAdminsCmd returns a cobra command that modifies the set of current 425 // cluster admins 426 func ModifyAdminsCmd() *cobra.Command { 427 var add []string 428 var remove []string 429 modifyAdmins := &cobra.Command{ 430 Short: "Modify the current cluster admins", 431 Long: "Modify the current cluster admins. --add accepts a comma-" + 432 "separated list of users to grant admin status, and --remove accepts a " + 433 "comma-separated list of users to revoke admin status", 434 Run: cmdutil.Run(func([]string) error { 435 c, err := client.NewOnUserMachine("user") 436 if err != nil { 437 return err 438 } 439 defer c.Close() 440 _, err = c.ModifyAdmins(c.Ctx(), &auth.ModifyAdminsRequest{ 441 Add: add, 442 Remove: remove, 443 }) 444 if auth.IsErrPartiallyActivated(err) { 445 return errors.Wrapf(err, "Errored, if pachyderm is stuck in this state, you "+ 446 "can revert by running 'pachctl auth deactivate' or retry by "+ 447 "running 'pachctl auth activate' again") 448 } 449 return grpcutil.ScrubGRPC(err) 450 }), 451 } 452 modifyAdmins.PersistentFlags().StringSliceVar(&add, "add", []string{}, 453 "Comma-separated list of users to grant admin status") 454 modifyAdmins.PersistentFlags().StringSliceVar(&remove, "remove", []string{}, 455 "Comma-separated list of users revoke admin status") 456 return cmdutil.CreateAlias(modifyAdmins, "auth modify-admins") 457 } 458 459 // GetAuthTokenCmd returns a cobra command that lets a user get a pachyderm 460 // token on behalf of themselves or another user 461 func GetAuthTokenCmd() *cobra.Command { 462 var quiet bool 463 var ttl string 464 getAuthToken := &cobra.Command{ 465 Use: "{{alias}} [username]", 466 Short: "Get an auth token that authenticates the holder as \"username\", " + 467 "or the currently signed-in user, if no 'username' is provided", 468 Long: "Get an auth token that authenticates the holder as \"username\"; " + 469 "or the currently signed-in user, if no 'username' is provided. Only " + 470 "cluster admins can obtain an auth token on behalf of another user.", 471 Run: cmdutil.RunBoundedArgs(0, 1, func(args []string) error { 472 c, err := client.NewOnUserMachine("user") 473 if err != nil { 474 return errors.Wrapf(err, "could not connect") 475 } 476 defer c.Close() 477 478 req := &auth.GetAuthTokenRequest{} 479 if ttl != "" { 480 d, err := time.ParseDuration(ttl) 481 if err != nil { 482 return errors.Wrapf(err, "could not parse duration %q", ttl) 483 } 484 req.TTL = int64(d.Seconds()) 485 } 486 if len(args) == 1 { 487 req.Subject = args[0] 488 } 489 resp, err := c.GetAuthToken(c.Ctx(), req) 490 if err != nil { 491 return grpcutil.ScrubGRPC(err) 492 } 493 if quiet { 494 fmt.Println(resp.Token) 495 } else { 496 fmt.Printf("New credentials:\n Subject: %s\n Token: %s\n", resp.Subject, resp.Token) 497 } 498 return nil 499 }), 500 } 501 getAuthToken.PersistentFlags().BoolVarP(&quiet, "quiet", "q", false, "if "+ 502 "set, only print the resulting token (if successful). This is useful for "+ 503 "scripting, as the output can be piped to use-auth-token") 504 getAuthToken.PersistentFlags().StringVar(&ttl, "ttl", "", "if set, the "+ 505 "resulting auth token will have the given lifetime (or the lifetime"+ 506 "of the caller's current session, whichever is shorter). This flag should "+ 507 "be a golang duration (e.g. \"30s\" or \"1h2m3s\"). If unset, tokens will "+ 508 "have a lifetime of 30 days.") 509 return cmdutil.CreateAlias(getAuthToken, "auth get-auth-token") 510 } 511 512 // UseAuthTokenCmd returns a cobra command that lets a user get a pachyderm 513 // token on behalf of themselves or another user 514 func UseAuthTokenCmd() *cobra.Command { 515 useAuthToken := &cobra.Command{ 516 Short: "Read a Pachyderm auth token from stdin, and write it to the " + 517 "current user's Pachyderm config file", 518 Long: "Read a Pachyderm auth token from stdin, and write it to the " + 519 "current user's Pachyderm config file", 520 Run: cmdutil.RunFixedArgs(0, func(args []string) error { 521 fmt.Println("Please paste your Pachyderm auth token:") 522 token, err := cmdutil.ReadPassword("") 523 if err != nil { 524 return errors.Wrapf(err, "error reading token") 525 } 526 config.WritePachTokenToConfig(strings.TrimSpace(token)) // drop trailing newline 527 return nil 528 }), 529 } 530 return cmdutil.CreateAlias(useAuthToken, "auth use-auth-token") 531 } 532 533 // GetOneTimePasswordCmd returns a cobra command that lets a user get an OTP. 534 func GetOneTimePasswordCmd() *cobra.Command { 535 var ttl string 536 getOneTimePassword := &cobra.Command{ 537 Use: "{{alias}} <username>", 538 Short: "Get a one-time password that authenticates the holder as " + 539 "\"username\", or the currently signed in user if no 'username' is " + 540 "specified", 541 Long: "Get a one-time password that authenticates the holder as " + 542 "\"username\", or the currently signed in user if no 'username' is " + 543 "specified. Only cluster admins may obtain a one-time password on " + 544 "behalf of another user.", 545 Run: cmdutil.RunBoundedArgs(0, 1, func(args []string) error { 546 c, err := client.NewOnUserMachine("user") 547 if err != nil { 548 return errors.Wrapf(err, "could not connect") 549 } 550 defer c.Close() 551 552 req := &auth.GetOneTimePasswordRequest{} 553 if ttl != "" { 554 d, err := time.ParseDuration(ttl) 555 if err != nil { 556 return errors.Wrapf(err, "could not parse duration %q", ttl) 557 } 558 req.TTL = int64(d.Seconds()) 559 } 560 if len(args) == 1 { 561 req.Subject = args[0] 562 } 563 resp, err := c.GetOneTimePassword(c.Ctx(), req) 564 if err != nil { 565 return grpcutil.ScrubGRPC(err) 566 } 567 fmt.Println(resp.Code) 568 return nil 569 }), 570 } 571 getOneTimePassword.PersistentFlags().StringVar(&ttl, "ttl", "", "if set, "+ 572 "the resulting one-time password will have the given lifetime (or the "+ 573 "lifetime of the caller's current session, whichever is shorter). This "+ 574 "flag should be a golang duration (e.g. \"30s\" or \"1h2m3s\"). If unset, "+ 575 "one-time passwords will have a lifetime of 5 minutes") 576 return cmdutil.CreateAlias(getOneTimePassword, "auth get-otp") 577 } 578 579 // Cmds returns a list of cobra commands for authenticating and authorizing 580 // users in an auth-enabled Pachyderm cluster. 581 func Cmds() []*cobra.Command { 582 var commands []*cobra.Command 583 584 auth := &cobra.Command{ 585 Short: "Auth commands manage access to data in a Pachyderm cluster", 586 Long: "Auth commands manage access to data in a Pachyderm cluster", 587 } 588 589 commands = append(commands, cmdutil.CreateAlias(auth, "auth")) 590 commands = append(commands, ActivateCmd()) 591 commands = append(commands, DeactivateCmd()) 592 commands = append(commands, LoginCmd()) 593 commands = append(commands, LogoutCmd()) 594 commands = append(commands, WhoamiCmd()) 595 commands = append(commands, CheckCmd()) 596 commands = append(commands, SetScopeCmd()) 597 commands = append(commands, GetCmd()) 598 commands = append(commands, ListAdminsCmd()) 599 commands = append(commands, ModifyAdminsCmd()) 600 commands = append(commands, GetAuthTokenCmd()) 601 commands = append(commands, UseAuthTokenCmd()) 602 commands = append(commands, GetConfigCmd()) 603 commands = append(commands, SetConfigCmd()) 604 commands = append(commands, GetOneTimePasswordCmd()) 605 606 return commands 607 }