Skip to content

Commit

Permalink
ACL Token CLI implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
mkeeler committed Oct 13, 2018
1 parent 46c9ff3 commit a01570d
Show file tree
Hide file tree
Showing 7 changed files with 308 additions and 26 deletions.
45 changes: 45 additions & 0 deletions command/acl/acl_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@ func PrintToken(token *api.ACLToken, ui cli.Ui, showMeta bool) {
}
}

func PrintTokenListEntry(token *api.ACLTokenListEntry, ui cli.Ui, showMeta bool) {
ui.Info(fmt.Sprintf("AccessorID: %s", token.AccessorID))
ui.Info(fmt.Sprintf("Description: %s", token.Description))
ui.Info(fmt.Sprintf("Local: %t", token.Local))
ui.Info(fmt.Sprintf("Create Time: %v", token.CreateTime))
ui.Info(fmt.Sprintf("Legacy: %t", token.Legacy))
if showMeta {
ui.Info(fmt.Sprintf("Hash: %d", token.Hash))
ui.Info(fmt.Sprintf("Create Index: %d", token.CreateIndex))
ui.Info(fmt.Sprintf("Modify Index: %d", token.ModifyIndex))
}
ui.Info(fmt.Sprintf("Policies:"))
for _, policy := range token.Policies {
ui.Info(fmt.Sprintf(" %s - %s", policy.ID, policy.Name))
}
}

func PrintPolicy(policy *api.ACLPolicy, ui cli.Ui, showMeta bool) {
ui.Info(fmt.Sprintf("ID: %s", policy.ID))
ui.Info(fmt.Sprintf("Name: %s", policy.Name))
Expand All @@ -55,6 +72,34 @@ func PrintPolicyListEntry(policy *api.ACLPolicyListEntry, ui cli.Ui, showMeta bo
}
}

func GetTokenIDFromPartial(client *api.Client, partialID string) (string, error) {
// the full UUID string was given
if len(partialID) == 36 {
return partialID, nil
}

tokens, _, err := client.ACL().TokenList(nil)
if err != nil {
return "", err
}

tokenID := ""
for _, token := range tokens {
if strings.HasPrefix(token.AccessorID, partialID) {
if tokenID != "" {
return "", fmt.Errorf("Partial token ID is not unique")
}
tokenID = token.AccessorID
}
}

if tokenID == "" {
return "", fmt.Errorf("No such token ID with prefix: %s", partialID)
}

return tokenID, nil
}

func GetPolicyIDFromPartial(client *api.Client, partialID string) (string, error) {
// The full UUID string was given
if len(partialID) == 36 {
Expand Down
2 changes: 1 addition & 1 deletion command/acl/policy/list/policy_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (c *cmd) Run(args []string) int {

policies, _, err := client.ACL().PolicyList(nil)
if err != nil {
c.UI.Error(fmt.Sprintf("Failed to get retrieve the policy list: %v", err))
c.UI.Error(fmt.Sprintf("Failed to retrieve the policy list: %v", err))
return 1
}

Expand Down
62 changes: 57 additions & 5 deletions command/acl/token/create/token_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"flag"
"fmt"

"github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/command/acl"
"github.com/hashicorp/consul/command/flags"
"github.com/mitchellh/cli"
)
Expand All @@ -19,10 +21,21 @@ type cmd struct {
flags *flag.FlagSet
http *flags.HTTPFlags
help string

policyIDs []string
policyNames []string
description string
local bool
}

func (c *cmd) init() {
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
c.flags.BoolVar(&c.local, "local", false, "Create this as a datacenter local token")
c.flags.StringVar(&c.description, "description", "", "A description of the token")
c.flags.Var((*flags.AppendSliceValue)(&c.policyIDs), "policy-id", "ID of a "+
"policy to use for this token. May be specified multiple times")
c.flags.Var((*flags.AppendSliceValue)(&c.policyNames), "policy-name", "Name of a "+
"policy to use for this token. May be specified multiple times")
c.http = &flags.HTTPFlags{}
flags.Merge(c.flags, c.http.ClientFlags())
flags.Merge(c.flags, c.http.ServerFlags())
Expand All @@ -34,27 +47,66 @@ func (c *cmd) Run(args []string) int {
return 1
}

_, err := c.http.APIClient()
if len(c.policyNames) == 0 && len(c.policyIDs) == 0 {
c.UI.Error(fmt.Sprintf("Cannot create a token without specifying -policy-name or -policy-id at least once"))
return 1
}

client, err := c.http.APIClient()
if err != nil {
c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1
}

c.UI.Error("Unimplemented")
return 1
newToken := &api.ACLToken{
Description: c.description,
Local: c.local,
}

for _, policyName := range c.policyNames {
// We could resolve names to IDs here but there isn't any reason why its would be better
// than allowing the agent to do it.
newToken.Policies = append(newToken.Policies, &api.ACLTokenPolicyLink{Name: policyName})
}

for _, policyID := range c.policyIDs {
policyID, err := acl.GetPolicyIDFromPartial(client, policyID)
if err != nil {
c.UI.Error(fmt.Sprintf("Error resolving policy ID %s: %v", policyID, err))
return 1
}
newToken.Policies = append(newToken.Policies, &api.ACLTokenPolicyLink{ID: policyID})
}

token, _, err := client.ACL().TokenCreate(newToken, nil)
if err != nil {
c.UI.Error(fmt.Sprintf("Failed to create new token: %v", err))
return 1
}

acl.PrintToken(token, c.UI, false)
return 0
}

func (c *cmd) Synopsis() string {
return synopsis
}

func (c *cmd) Help() string {
return flags.Usage(help, nil)
return flags.Usage(c.help, nil)
}

const synopsis = "Create an ACL Token"
const help = `
Usage: consul acl token create [options]
Need more help usage
When creating a new token policies may be linked using either the -policy-id
or the -policy-name options. When specifying policies by IDs you may use a
unique prefix of the UUID as a shortcut for specifying the entire UUID.
Create a new token:
$ consul acl token create -description "Replication token"
-policy-id b52fc3de-5
-policy-name "acl-replication"
`
43 changes: 37 additions & 6 deletions command/acl/token/delete/token_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"flag"
"fmt"

"github.com/hashicorp/consul/command/acl"
"github.com/hashicorp/consul/command/flags"
"github.com/mitchellh/cli"
)
Expand All @@ -19,10 +20,16 @@ type cmd struct {
flags *flag.FlagSet
http *flags.HTTPFlags
help string

tokenID string
}

func (c *cmd) init() {
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
c.flags.StringVar(&c.tokenID, "id", "", "The Accessor ID of the token to delete. "+
"It may be specified as a unique ID prefix but will error if the prefix "+
"matches multiple token Accessor IDs")
c.http = &flags.HTTPFlags{}
c.http = &flags.HTTPFlags{}
flags.Merge(c.flags, c.http.ClientFlags())
flags.Merge(c.flags, c.http.ServerFlags())
Expand All @@ -34,27 +41,51 @@ func (c *cmd) Run(args []string) int {
return 1
}

_, err := c.http.APIClient()
if c.tokenID == "" {
c.UI.Error(fmt.Sprintf("Must specify the -id paramter"))
return 1
}

client, err := c.http.APIClient()
if err != nil {
c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1
}

c.UI.Error("Unimplemented")
return 1
tokenID, err := acl.GetTokenIDFromPartial(client, c.tokenID)
if err != nil {
c.UI.Error(fmt.Sprintf("Error determining token ID: %v", err))
return 1
}

if _, err := client.ACL().TokenDelete(tokenID, nil); err != nil {
c.UI.Error(fmt.Sprintf("Error deleting token %q: %v", tokenID, err))
return 1
}

c.UI.Info(fmt.Sprintf("Token %q deleted successfully", tokenID))
return 0
}

func (c *cmd) Synopsis() string {
return synopsis
}

func (c *cmd) Help() string {
return flags.Usage(help, nil)
return flags.Usage(c.help, nil)
}

const synopsis = "Delete an ACL Token"
const help = `
Usage: consul acl token delete [options]
Usage: consul acl token delete [options] -id TOKEN
Deletes an ACL token by providing either the ID or a unique ID prefix.
Delete by prefix:
$ consul acl token delete -id b6b85
Delete by full ID:
Need more help usage
$ consul acl token delete -id b6b856da-5193-4e78-845a-7d61ca8371ba
`
32 changes: 27 additions & 5 deletions command/acl/token/list/token_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"flag"
"fmt"

"github.com/hashicorp/consul/command/acl"
"github.com/hashicorp/consul/command/flags"
"github.com/mitchellh/cli"
)
Expand All @@ -19,10 +20,14 @@ type cmd struct {
flags *flag.FlagSet
http *flags.HTTPFlags
help string

showMeta bool
}

func (c *cmd) init() {
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
c.flags.BoolVar(&c.showMeta, "meta", false, "Indicates that token metadata such "+
"as the content hash and raft indices should be show for each entry")
c.http = &flags.HTTPFlags{}
flags.Merge(c.flags, c.http.ClientFlags())
flags.Merge(c.flags, c.http.ServerFlags())
Expand All @@ -34,27 +39,44 @@ func (c *cmd) Run(args []string) int {
return 1
}

_, err := c.http.APIClient()
client, err := c.http.APIClient()
if err != nil {
c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
return 1
}

c.UI.Error("Unimplemented")
return 1
tokens, _, err := client.ACL().TokenList(nil)
if err != nil {
c.UI.Error(fmt.Sprintf("Failed to retrieve the token list: %v", err))
return 1
}

first := true
for _, token := range tokens {
if first {
first = false
} else {
c.UI.Info("")
}
acl.PrintTokenListEntry(token, c.UI, c.showMeta)
}

return 0
}

func (c *cmd) Synopsis() string {
return synopsis
}

func (c *cmd) Help() string {
return flags.Usage(help, nil)
return flags.Usage(c.help, nil)
}

const synopsis = "List ACL Tokens"
const help = `
Usage: consul acl token list [options]
Need more help usage
List all the ALC tokens
$ consul acl token list
`
Loading

0 comments on commit a01570d

Please sign in to comment.