From dd87f94dfb32a603c7dd3113661c94e355efc62d Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Wed, 15 Apr 2015 14:24:07 -0700 Subject: [PATCH] vault: token store allows specifying display_name --- vault/core_test.go | 9 ++++--- vault/token_store.go | 46 ++++++++++++++++++++++---------- vault/token_store_test.go | 55 ++++++++++++++++++++++++++++++--------- 3 files changed, 80 insertions(+), 30 deletions(-) diff --git a/vault/core_test.go b/vault/core_test.go index c829706403..f5e7292f32 100644 --- a/vault/core_test.go +++ b/vault/core_test.go @@ -971,10 +971,11 @@ func TestCore_HandleRequest_CreateToken_Lease(t *testing.T) { t.Fatalf("err: %v", err) } expect := &TokenEntry{ - ID: clientToken, - Parent: root, - Policies: []string{"foo"}, - Path: "auth/token/create", + ID: clientToken, + Parent: root, + Policies: []string{"foo"}, + Path: "auth/token/create", + DisplayName: "token", } if !reflect.DeepEqual(te, expect) { t.Fatalf("Bad: %#v expect: %#v", te, expect) diff --git a/vault/token_store.go b/vault/token_store.go index 3fbd76a43f..078b81f5b1 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "regexp" "strings" "time" @@ -34,6 +35,11 @@ const ( tokenSubPath = "token/" ) +var ( + // displayNameSanitize is used to sanitize a display name given to a token. + displayNameSanitize = regexp.MustCompile("[^a-zA-Z0-9-]") +) + // TokenStore is used to manage client tokens. Tokens are used for // clients to authenticate, and each token is mapped to an applicable // set of policy which is used for authorization. @@ -247,8 +253,9 @@ func (ts *TokenStore) SaltID(id string) string { // RootToken is used to generate a new token with root privileges and no parent func (ts *TokenStore) RootToken() (*TokenEntry, error) { te := &TokenEntry{ - Policies: []string{"root"}, - Path: "auth/token/root", + Policies: []string{"root"}, + Path: "auth/token/root", + DisplayName: "root", } if err := ts.Create(te); err != nil { return nil, err @@ -435,11 +442,12 @@ func (ts *TokenStore) handleCreate( // Read and parse the fields var data struct { - ID string - Policies []string - Metadata map[string]string `mapstructure:"meta"` - NoParent bool `mapstructure:"no_parent"` - Lease string + ID string + Policies []string + Metadata map[string]string `mapstructure:"meta"` + NoParent bool `mapstructure:"no_parent"` + Lease string + DisplayName string `mapstructure:"display_name"` } if err := mapstructure.WeakDecode(req.Data, &data); err != nil { return logical.ErrorResponse(fmt.Sprintf( @@ -448,9 +456,18 @@ func (ts *TokenStore) handleCreate( // Setup the token entry te := TokenEntry{ - Parent: req.ClientToken, - Path: "auth/token/create", - Meta: data.Metadata, + Parent: req.ClientToken, + Path: "auth/token/create", + Meta: data.Metadata, + DisplayName: "token", + } + + // Attach the given display name if any + if data.DisplayName != "" { + full := "token-" + data.DisplayName + full = displayNameSanitize.ReplaceAllString(full, "-") + full = strings.TrimSuffix(full, "-") + te.DisplayName = full } // Allow specifying the ID of the token if the client is root @@ -593,10 +610,11 @@ func (ts *TokenStore) handleLookup( // you could escalade your privileges. resp := &logical.Response{ Data: map[string]interface{}{ - "id": out.ID, - "policies": out.Policies, - "path": out.Path, - "meta": out.Meta, + "id": out.ID, + "policies": out.Policies, + "path": out.Path, + "meta": out.Meta, + "display_name": out.DisplayName, }, } return resp, nil diff --git a/vault/token_store_test.go b/vault/token_store_test.go index 18782df9ff..b29f9c5596 100644 --- a/vault/token_store_test.go +++ b/vault/token_store_test.go @@ -269,6 +269,34 @@ func TestTokenStore_RevokeTree(t *testing.T) { } } +func TestTokenStore_HandleRequest_CreateToken_DisplayName(t *testing.T) { + _, ts, root := mockTokenStore(t) + + req := logical.TestRequest(t, logical.WriteOperation, "create") + req.ClientToken = root + req.Data["display_name"] = "foo_bar.baz!" + + resp, err := ts.HandleRequest(req) + if err != nil { + t.Fatalf("err: %v %v", err, resp) + } + + expected := &TokenEntry{ + ID: resp.Auth.ClientToken, + Parent: root, + Policies: []string{"root"}, + Path: "auth/token/create", + DisplayName: "token-foo-bar-baz", + } + out, err := ts.Lookup(resp.Auth.ClientToken) + if err != nil { + t.Fatalf("err: %v", err) + } + if !reflect.DeepEqual(out, expected) { + t.Fatalf("bad: %#v", out) + } +} + func TestTokenStore_HandleRequest_CreateToken_NoPolicy(t *testing.T) { _, ts, root := mockTokenStore(t) @@ -281,10 +309,11 @@ func TestTokenStore_HandleRequest_CreateToken_NoPolicy(t *testing.T) { } expected := &TokenEntry{ - ID: resp.Auth.ClientToken, - Parent: root, - Policies: []string{"root"}, - Path: "auth/token/create", + ID: resp.Auth.ClientToken, + Parent: root, + Policies: []string{"root"}, + Path: "auth/token/create", + DisplayName: "token", } out, err := ts.Lookup(resp.Auth.ClientToken) if err != nil { @@ -560,10 +589,11 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { } exp := map[string]interface{}{ - "id": root, - "policies": []string{"root"}, - "path": "auth/token/root", - "meta": map[string]string(nil), + "id": root, + "policies": []string{"root"}, + "path": "auth/token/root", + "meta": map[string]string(nil), + "display_name": "root", } if !reflect.DeepEqual(resp.Data, exp) { t.Fatalf("bad: %#v exp: %#v", resp.Data, exp) @@ -623,10 +653,11 @@ func TestTokenStore_HandleRequest_LookupSelf(t *testing.T) { } exp := map[string]interface{}{ - "id": root, - "policies": []string{"root"}, - "path": "auth/token/root", - "meta": map[string]string(nil), + "id": root, + "policies": []string{"root"}, + "path": "auth/token/root", + "meta": map[string]string(nil), + "display_name": "root", } if !reflect.DeepEqual(resp.Data, exp) { t.Fatalf("bad: %#v exp: %#v", resp.Data, exp)