From 38a5d75caac0203d417203e5e253abd02a7a6cfc Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Tue, 8 Mar 2016 12:51:38 -0500 Subject: [PATCH 01/20] Introduced AccessorID in TokenEntry and returning it along with token --- api/secret.go | 1 + command/format.go | 1 + http/logical.go | 2 ++ logical/auth.go | 7 +++++++ vault/token_store.go | 20 ++++++++++++++++++++ 5 files changed, 31 insertions(+) diff --git a/api/secret.go b/api/secret.go index bc52f78b72228..5c77da2b7409c 100644 --- a/api/secret.go +++ b/api/secret.go @@ -28,6 +28,7 @@ type Secret struct { // SecretAuth is the structure containing auth information if we have it. type SecretAuth struct { ClientToken string `json:"client_token"` + AccessorID string `json:"accessor_id"` Policies []string `json:"policies"` Metadata map[string]string `json:"metadata"` diff --git a/command/format.go b/command/format.go index 0acac4b70a774..800930b5131cd 100644 --- a/command/format.go +++ b/command/format.go @@ -143,6 +143,7 @@ func (t TableFormatter) OutputSecret(ui cli.Ui, secret, s *api.Secret) error { if s.Auth != nil { input = append(input, fmt.Sprintf("token %s %s", config.Delim, s.Auth.ClientToken)) + input = append(input, fmt.Sprintf("token_accessor %s %s", config.Delim, s.Auth.AccessorID)) input = append(input, fmt.Sprintf("token_duration %s %d", config.Delim, s.Auth.LeaseDuration)) input = append(input, fmt.Sprintf("token_renewable %s %v", config.Delim, s.Auth.Renewable)) input = append(input, fmt.Sprintf("token_policies %s %v", config.Delim, s.Auth.Policies)) diff --git a/http/logical.go b/http/logical.go index eb07a8f6404dd..1b30c0781a392 100644 --- a/http/logical.go +++ b/http/logical.go @@ -124,6 +124,7 @@ func respondLogical(w http.ResponseWriter, r *http.Request, path string, dataOnl if resp.Auth != nil { logicalResp.Auth = &Auth{ ClientToken: resp.Auth.ClientToken, + AccessorID: resp.Auth.AccessorID, Policies: resp.Auth.Policies, Metadata: resp.Auth.Metadata, LeaseDuration: int(resp.Auth.TTL.Seconds()), @@ -218,6 +219,7 @@ type LogicalResponse struct { type Auth struct { ClientToken string `json:"client_token"` + AccessorID string `json:"accessor_id"` Policies []string `json:"policies"` Metadata map[string]string `json:"metadata"` LeaseDuration int `json:"lease_duration"` diff --git a/logical/auth.go b/logical/auth.go index e297e10eb3ab4..fa887e3d29d45 100644 --- a/logical/auth.go +++ b/logical/auth.go @@ -33,6 +33,13 @@ type Auth struct { // This will be filled in by Vault core when an auth structure is // returned. Setting this manually will have no effect. ClientToken string + + // AccessorID is the identifier for the ClientToken. This can be used + // to perform management functionalities (especially revocation) when + // ClientToken in the audit logs are obfuscated. AccessorID can be used + // to revoke a ClientToken and to lookup the capabilities of the ClientToken, + // all without actually knowing the ClientToken. + AccessorID string } func (a *Auth) GoString() string { diff --git a/vault/token_store.go b/vault/token_store.go index b319e04a6eea7..b6afaf57320fa 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -264,6 +264,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) // TokenEntry is used to represent a given token type TokenEntry struct { ID string // ID of this entry, generally a random UUID + AccessorID string // Accessor ID for this token, a random UUID Parent string // Parent token, used for revocation trees Policies []string // Which named policies should be used Path string // Used for audit trails, this is something like "auth/user/login" @@ -300,6 +301,19 @@ func (ts *TokenStore) rootToken() (*TokenEntry, error) { return te, nil } +// CreateAccessorID is used to create an identifier for the token ID. +func (ts *TokenStore) createAccessorID(entry *TokenEntry) error { + defer metrics.MeasureSince([]string{"token", "createAccessorID"}, time.Now()) + + // Create a random accessor ID + accessorUUID, err := uuid.GenerateUUID() + if err != nil { + return err + } + entry.AccessorID = accessorUUID + return nil +} + // Create is used to create a new token entry. The entry is assigned // a newly generated ID if not provided. func (ts *TokenStore) create(entry *TokenEntry) error { @@ -313,6 +327,11 @@ func (ts *TokenStore) create(entry *TokenEntry) error { entry.ID = entryUUID } + err := ts.createAccessorID(entry) + if err != nil { + return err + } + return ts.storeCommon(entry, true) } @@ -705,6 +724,7 @@ func (ts *TokenStore) handleCreateCommon( Renewable: true, }, ClientToken: te.ID, + AccessorID: te.AccessorID, }, } From a713720bd484251224d35449ab77da506cf8a55a Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Tue, 8 Mar 2016 13:12:53 -0500 Subject: [PATCH 02/20] Create indexing from Accessor ID to Token ID --- vault/token_store.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/vault/token_store.go b/vault/token_store.go index b6afaf57320fa..35d358762a853 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -311,6 +311,17 @@ func (ts *TokenStore) createAccessorID(entry *TokenEntry) error { return err } entry.AccessorID = accessorUUID + + // Create salted token and accessor IDs + saltedTokenId := ts.SaltID(entry.ID) + saltedAccessorID := ts.SaltID(entry.AccessorID) + + // Create index, mapping the Accessor ID to the Token ID + path := lookupPrefix + saltedTokenId + "/" + saltedAccessorID + le := &logical.StorageEntry{Key: path, Value: []byte(entry.ID)} + if err := ts.view.Put(le); err != nil { + return fmt.Errorf("failed to persist accessor index entry: %v", err) + } return nil } From 4ed3a85f73f58614c2ea8179cb23d2d55b6775b9 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Tue, 8 Mar 2016 14:04:20 -0500 Subject: [PATCH 03/20] Clear the accessor index during revocation --- vault/token_store.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/vault/token_store.go b/vault/token_store.go index 35d358762a853..b148b3189c29e 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -313,11 +313,9 @@ func (ts *TokenStore) createAccessorID(entry *TokenEntry) error { entry.AccessorID = accessorUUID // Create salted token and accessor IDs - saltedTokenId := ts.SaltID(entry.ID) - saltedAccessorID := ts.SaltID(entry.AccessorID) // Create index, mapping the Accessor ID to the Token ID - path := lookupPrefix + saltedTokenId + "/" + saltedAccessorID + path := lookupPrefix + ts.SaltID(entry.ID) + "/" + ts.SaltID(entry.AccessorID) le := &logical.StorageEntry{Key: path, Value: []byte(entry.ID)} if err := ts.view.Put(le); err != nil { return fmt.Errorf("failed to persist accessor index entry: %v", err) @@ -499,6 +497,14 @@ func (ts *TokenStore) revokeSalted(saltedId string) error { } } + // Clear the accessor ID index if any + if entry != nil && entry.AccessorID != "" { + path := lookupPrefix + ts.SaltID(entry.ID) + "/" + ts.SaltID(entry.AccessorID) + if ts.view.Delete(path); err != nil { + return fmt.Errorf("failed to delete entry: %v", err) + } + } + // Revoke all secrets under this token if entry != nil { if err := ts.expiration.RevokeByToken(entry.ID); err != nil { From c7033b15063f51accd9bc203f7a2f696c1ee855a Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Tue, 8 Mar 2016 15:13:29 -0500 Subject: [PATCH 04/20] placeholders for revoke-accessor and lookup-accessor --- vault/token_store.go | 69 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/vault/token_store.go b/vault/token_store.go index b148b3189c29e..08bc654d8d7b3 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -3,6 +3,7 @@ package vault import ( "encoding/json" "fmt" + "log" "regexp" "sort" "strings" @@ -127,6 +128,24 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) HelpDescription: strings.TrimSpace(tokenLookupHelp), }, + &framework.Path{ + Pattern: "lookup-accessor$", + + Fields: map[string]*framework.FieldSchema{ + "accessor_id": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Accessor ID to lookup", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: t.handleLookupAccessor, + }, + + HelpSynopsis: strings.TrimSpace(tokenLookupAccessorHelp), + HelpDescription: strings.TrimSpace(tokenLookupAccessorHelp), + }, + &framework.Path{ Pattern: "lookup-self$", @@ -145,6 +164,17 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) HelpDescription: strings.TrimSpace(tokenLookupHelp), }, + &framework.Path{ + Pattern: "revoke-accessor$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: t.handleRevokeAccessor, + }, + + HelpSynopsis: strings.TrimSpace(tokenRevokeAccessorHelp), + HelpDescription: strings.TrimSpace(tokenRevokeAccessorHelp), + }, + &framework.Path{ Pattern: "revoke-self$", @@ -312,9 +342,7 @@ func (ts *TokenStore) createAccessorID(entry *TokenEntry) error { } entry.AccessorID = accessorUUID - // Create salted token and accessor IDs - - // Create index, mapping the Accessor ID to the Token ID + // Create index entry, mapping the Accessor ID to the Token ID path := lookupPrefix + ts.SaltID(entry.ID) + "/" + ts.SaltID(entry.AccessorID) le := &logical.StorageEntry{Key: path, Value: []byte(entry.ID)} if err := ts.view.Put(le); err != nil { @@ -566,6 +594,20 @@ func (ts *TokenStore) revokeTreeSalted(saltedId string) error { return nil } +// handleLookupAccessor handles the auth/token/lookup-accessor path for returning +// the properties of the token associated with the accessor ID +func (ts *TokenStore) handleLookupAccessor(req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + log.Printf("token_store.go: handleLookupAccessor req: %#v d: %#v\n", req, d) + return nil, nil +} + +// handleRevokeAccessor handles the auth/token/revoke-accessor path for revoking +// the token associated with the accessor ID +func (ts *TokenStore) handleRevokeAccessor(req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + log.Printf("token_store.go: handleRevokeAccessor req: %#v d: %#v\n", req, d) + return nil, nil +} + // handleCreate handles the auth/token/create path for creation of new orphan // tokens func (ts *TokenStore) handleCreateOrphan( @@ -955,13 +997,16 @@ const ( Client tokens are used to identify a client and to allow Vault to associate policies and ACLs which are enforced on every request. This backend also allows for generating sub-tokens as well as revocation of tokens. The tokens are renewable if associated with a lease.` - tokenCreateHelp = `The token create path is used to create new tokens.` - tokenCreateOrphanHelp = `The token create path is used to create new orphan tokens.` - tokenLookupHelp = `This endpoint will lookup a token and its properties.` - tokenRevokeHelp = `This endpoint will delete the given token and all of its child tokens.` - tokenRevokeSelfHelp = `This endpoint will delete the token used to call it and all of its child tokens.` - tokenRevokeOrphanHelp = `This endpoint will delete the token and orphan its child tokens.` - tokenRevokePrefixHelp = `This endpoint will delete all tokens generated under a prefix with their child tokens.` - tokenRenewHelp = `This endpoint will renew the given token and prevent expiration.` - tokenRenewSelfHelp = `This endpoint will renew the token used to call it and prevent expiration.` + tokenCreateHelp = `The token create path is used to create new tokens.` + tokenCreateOrphanHelp = `The token create path is used to create new orphan tokens.` + tokenLookupHelp = `This endpoint will lookup a token and its properties.` + tokenLookupAccessorHelp = `This endpoint will lookup an accessor and its properties. +This will not return the token ID associated with the accessor ID.` + tokenRevokeHelp = `This endpoint will delete the given token and all of its child tokens.` + tokenRevokeSelfHelp = `This endpoint will delete the token used to call it and all of its child tokens.` + tokenRevokeAccessorHelp = `This endpoint will delete the token associated with the accessor ID` + tokenRevokeOrphanHelp = `This endpoint will delete the token and orphan its child tokens.` + tokenRevokePrefixHelp = `This endpoint will delete all tokens generated under a prefix with their child tokens.` + tokenRenewHelp = `This endpoint will renew the given token and prevent expiration.` + tokenRenewSelfHelp = `This endpoint will renew the token used to call it and prevent expiration.` ) From bb927e34d1999386df200ed4844be4ce1874a138 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Tue, 8 Mar 2016 17:38:19 -0500 Subject: [PATCH 05/20] Implemented lookup-accessor as a token_store endpoint --- vault/token_store.go | 57 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/vault/token_store.go b/vault/token_store.go index 08bc654d8d7b3..412260fb4af3e 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -139,7 +139,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: t.handleLookupAccessor, + logical.UpdateOperation: t.handleLookupAccessor, }, HelpSynopsis: strings.TrimSpace(tokenLookupAccessorHelp), @@ -343,7 +343,7 @@ func (ts *TokenStore) createAccessorID(entry *TokenEntry) error { entry.AccessorID = accessorUUID // Create index entry, mapping the Accessor ID to the Token ID - path := lookupPrefix + ts.SaltID(entry.ID) + "/" + ts.SaltID(entry.AccessorID) + path := lookupPrefix + ts.SaltID(entry.AccessorID) le := &logical.StorageEntry{Key: path, Value: []byte(entry.ID)} if err := ts.view.Put(le); err != nil { return fmt.Errorf("failed to persist accessor index entry: %v", err) @@ -527,7 +527,7 @@ func (ts *TokenStore) revokeSalted(saltedId string) error { // Clear the accessor ID index if any if entry != nil && entry.AccessorID != "" { - path := lookupPrefix + ts.SaltID(entry.ID) + "/" + ts.SaltID(entry.AccessorID) + path := lookupPrefix + ts.SaltID(entry.AccessorID) if ts.view.Delete(path); err != nil { return fmt.Errorf("failed to delete entry: %v", err) } @@ -596,15 +596,56 @@ func (ts *TokenStore) revokeTreeSalted(saltedId string) error { // handleLookupAccessor handles the auth/token/lookup-accessor path for returning // the properties of the token associated with the accessor ID -func (ts *TokenStore) handleLookupAccessor(req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - log.Printf("token_store.go: handleLookupAccessor req: %#v d: %#v\n", req, d) - return nil, nil +func (ts *TokenStore) handleLookupAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { + accessorID := data.Get("accessor_id").(string) + if accessorID == "" { + return logical.ErrorResponse("missing accessor_id"), nil + } + + entry, err := ts.view.Get(lookupPrefix + ts.SaltID(accessorID)) + if err != nil { + return nil, fmt.Errorf("failed to read token using accessor ID: %s", err) + } + if entry == nil { + return nil, fmt.Errorf("invalid accessor ID") + } + + // Prepare the field data required for a lookup call + d := &framework.FieldData{ + Raw: map[string]interface{}{ + "token": string(entry.Value), + }, + Schema: map[string]*framework.FieldSchema{ + "token": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Token to lookup", + }, + }, + } + resp, err := ts.handleLookup(req, d) + if err != nil { + return nil, err + } + if resp == nil { + return nil, fmt.Errorf("failed to lookup the token") + } + if resp.IsError() { + return resp, nil + + } + + // Remove the token ID from the response + if resp.Data != nil && resp.Data["id"] != "" { + resp.Data["id"] = "" + } + + return resp, nil } // handleRevokeAccessor handles the auth/token/revoke-accessor path for revoking // the token associated with the accessor ID -func (ts *TokenStore) handleRevokeAccessor(req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - log.Printf("token_store.go: handleRevokeAccessor req: %#v d: %#v\n", req, d) +func (ts *TokenStore) handleRevokeAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { + log.Printf("token_store.go: handleRevokeAccessor req: %#v d: %#v\n", req, data) return nil, nil } From 5dcc6f0573ecd40be846e1c7c93bced2bf591550 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Tue, 8 Mar 2016 18:07:27 -0500 Subject: [PATCH 06/20] Implemented /auth/token/revoke-accessor in token_store --- vault/token_store.go | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/vault/token_store.go b/vault/token_store.go index 412260fb4af3e..e3e03661cfc40 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -167,6 +167,13 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) &framework.Path{ Pattern: "revoke-accessor$", + Fields: map[string]*framework.FieldSchema{ + "accessor_id": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Accessor ID associated with the token being revoked", + }, + }, + Callbacks: map[logical.Operation]framework.OperationFunc{ logical.UpdateOperation: t.handleRevokeAccessor, }, @@ -594,6 +601,20 @@ func (ts *TokenStore) revokeTreeSalted(saltedId string) error { return nil } +func (ts *TokenStore) lookupByAccessorID(accessorID string) (string, error) { + entry, err := ts.view.Get(lookupPrefix + ts.SaltID(accessorID)) + if err != nil { + return "", fmt.Errorf("failed to read index using accessor ID: %s", err) + } + if entry == nil { + return "", &ErrUserInput{ + Message: "invalid accessor ID", + } + } + + return string(entry.Value), nil +} + // handleLookupAccessor handles the auth/token/lookup-accessor path for returning // the properties of the token associated with the accessor ID func (ts *TokenStore) handleLookupAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { @@ -602,18 +623,15 @@ func (ts *TokenStore) handleLookupAccessor(req *logical.Request, data *framework return logical.ErrorResponse("missing accessor_id"), nil } - entry, err := ts.view.Get(lookupPrefix + ts.SaltID(accessorID)) + tokenID, err := ts.lookupByAccessorID(accessorID) if err != nil { - return nil, fmt.Errorf("failed to read token using accessor ID: %s", err) - } - if entry == nil { - return nil, fmt.Errorf("invalid accessor ID") + return nil, err } // Prepare the field data required for a lookup call d := &framework.FieldData{ Raw: map[string]interface{}{ - "token": string(entry.Value), + "token": tokenID, }, Schema: map[string]*framework.FieldSchema{ "token": &framework.FieldSchema{ @@ -646,6 +664,20 @@ func (ts *TokenStore) handleLookupAccessor(req *logical.Request, data *framework // the token associated with the accessor ID func (ts *TokenStore) handleRevokeAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { log.Printf("token_store.go: handleRevokeAccessor req: %#v d: %#v\n", req, data) + accessorID := data.Get("accessor_id").(string) + if accessorID == "" { + return logical.ErrorResponse("missing accessor_id"), nil + } + + tokenID, err := ts.lookupByAccessorID(accessorID) + if err != nil { + return nil, err + } + + // Revoke the token and its children + if err := ts.RevokeTree(tokenID); err != nil { + return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest + } return nil, nil } From 048f3b2fe4fba97fcc3860aff09fb152bbd03228 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Tue, 8 Mar 2016 18:27:03 -0500 Subject: [PATCH 07/20] Lay the foundation for returning proper HTTP status codes --- http/handler.go | 8 +++++++- vault/token_store.go | 2 -- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/http/handler.go b/http/handler.go index 0a6a1081b56ff..951d961fc9d47 100644 --- a/http/handler.go +++ b/http/handler.go @@ -8,6 +8,7 @@ import ( "net/url" "strings" + "github.com/hashicorp/errwrap" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/vault" ) @@ -79,7 +80,12 @@ func request(core *vault.Core, w http.ResponseWriter, rawReq *http.Request, r *l return resp, false } if err != nil { - respondError(w, http.StatusInternalServerError, err) + // Keep on adding error types here to set proper HTTP status code + if errwrap.ContainsType(err, new(vault.ErrUserInput)) { + respondError(w, http.StatusBadRequest, err) + } else { + respondError(w, http.StatusInternalServerError, err) + } return resp, false } diff --git a/vault/token_store.go b/vault/token_store.go index e3e03661cfc40..39600b169f60d 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -3,7 +3,6 @@ package vault import ( "encoding/json" "fmt" - "log" "regexp" "sort" "strings" @@ -663,7 +662,6 @@ func (ts *TokenStore) handleLookupAccessor(req *logical.Request, data *framework // handleRevokeAccessor handles the auth/token/revoke-accessor path for revoking // the token associated with the accessor ID func (ts *TokenStore) handleRevokeAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { - log.Printf("token_store.go: handleRevokeAccessor req: %#v d: %#v\n", req, data) accessorID := data.Get("accessor_id").(string) if accessorID == "" { return logical.ErrorResponse("missing accessor_id"), nil From 9da292932e4c640c57f1bfe67e82e61df3c4ae32 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Tue, 8 Mar 2016 19:14:29 -0500 Subject: [PATCH 08/20] Implemented /sys/capabilities-accessor and a way for setting HTTP error code in all the responses --- http/handler.go | 20 +++++++++++----- http/sys_capabilities.go | 50 +++++++++++++++++++++++++++++++--------- vault/capabilities.go | 23 ++++++++++++++++++ 3 files changed, 76 insertions(+), 17 deletions(-) diff --git a/http/handler.go b/http/handler.go index 951d961fc9d47..d77f602e13bbd 100644 --- a/http/handler.go +++ b/http/handler.go @@ -35,6 +35,7 @@ func Handler(core *vault.Core) http.Handler { mux.Handle("/v1/sys/rekey/update", handleSysRekeyUpdate(core)) mux.Handle("/v1/sys/capabilities", handleSysCapabilities(core)) mux.Handle("/v1/sys/capabilities-self", handleSysCapabilities(core)) + mux.Handle("/v1/sys/capabilities-accessor", handleSysCapabilitiesAccessor(core)) mux.Handle("/v1/sys/", handleLogical(core, true)) mux.Handle("/v1/", handleLogical(core, false)) @@ -80,12 +81,7 @@ func request(core *vault.Core, w http.ResponseWriter, rawReq *http.Request, r *l return resp, false } if err != nil { - // Keep on adding error types here to set proper HTTP status code - if errwrap.ContainsType(err, new(vault.ErrUserInput)) { - respondError(w, http.StatusBadRequest, err) - } else { - respondError(w, http.StatusInternalServerError, err) - } + respondErrorStatus(w, err) return resp, false } @@ -145,6 +141,18 @@ func requestAuth(r *http.Request, req *logical.Request) *logical.Request { return req } +// Determines the type of the error being returned and sets the HTTP +// status code appropriately +func respondErrorStatus(w http.ResponseWriter, err error) { + status := http.StatusInternalServerError + switch { + // Keep adding more error types here to appropriate the status codes + case errwrap.ContainsType(err, new(vault.ErrUserInput)): + status = http.StatusBadRequest + } + respondError(w, status, err) +} + func respondError(w http.ResponseWriter, status int, err error) { // Adjust status code when sealed if err == vault.ErrSealed { diff --git a/http/sys_capabilities.go b/http/sys_capabilities.go index 48f41c778c245..701bad8274e15 100644 --- a/http/sys_capabilities.go +++ b/http/sys_capabilities.go @@ -4,12 +4,11 @@ import ( "net/http" "strings" - "github.com/hashicorp/errwrap" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/vault" ) -func handleSysCapabilities(core *vault.Core) http.Handler { +func handleSysCapabilitiesAccessor(core *vault.Core) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "PUT": @@ -19,8 +18,35 @@ func handleSysCapabilities(core *vault.Core) http.Handler { return } - // Get the auth for the request so we can access the token directly - req := requestAuth(r, &logical.Request{}) + // Parse the request if we can + var data capabilitiesAccessorRequest + if err := parseRequest(r, &data); err != nil { + respondError(w, http.StatusBadRequest, err) + return + } + + capabilities, err := core.CapabilitiesAccessor(data.AccessorID, data.Path) + if err != nil { + respondErrorStatus(w, err) + return + } + + respondOk(w, &capabilitiesResponse{ + Capabilities: capabilities, + }) + }) + +} + +func handleSysCapabilities(core *vault.Core) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case "PUT": + case "POST": + default: + respondError(w, http.StatusMethodNotAllowed, nil) + return + } // Parse the request if we can var data capabilitiesRequest @@ -30,18 +56,15 @@ func handleSysCapabilities(core *vault.Core) http.Handler { } if strings.HasPrefix(r.URL.Path, "/v1/sys/capabilities-self") { + // Get the auth for the request so we can access the token directly + req := requestAuth(r, &logical.Request{}) data.Token = req.ClientToken } capabilities, err := core.Capabilities(data.Token, data.Path) if err != nil { - if errwrap.ContainsType(err, new(vault.ErrUserInput)) { - respondError(w, http.StatusBadRequest, err) - return - } else { - respondError(w, http.StatusInternalServerError, err) - return - } + respondErrorStatus(w, err) + return } respondOk(w, &capabilitiesResponse{ @@ -59,3 +82,8 @@ type capabilitiesRequest struct { Token string `json:"token"` Path string `json:"path"` } + +type capabilitiesAccessorRequest struct { + AccessorID string `json:"accessor_id"` + Path string `json:"path"` +} diff --git a/vault/capabilities.go b/vault/capabilities.go index fb9c1ad93486b..39a30bd3db9f6 100644 --- a/vault/capabilities.go +++ b/vault/capabilities.go @@ -12,6 +12,29 @@ func (e *ErrUserInput) Error() string { return e.Message } +// CapabilitiesAccessor is used to fetch the capabilities of the token associated with +// the token accessor an the given path +func (c *Core) CapabilitiesAccessor(accessorID, path string) ([]string, error) { + if path == "" { + return nil, &ErrUserInput{ + Message: "missing path", + } + } + + if accessorID == "" { + return nil, &ErrUserInput{ + Message: "missing accessor_id", + } + } + + token, err := c.tokenStore.lookupByAccessorID(accessorID) + if err != nil { + return nil, err + } + + return c.Capabilities(token, path) +} + // Capabilities is used to fetch the capabilities of the given token on the given path func (c *Core) Capabilities(token, path string) ([]string, error) { if path == "" { From edfba16e9534ce0dc704eb2d8172be64a596ae4b Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Tue, 8 Mar 2016 21:47:24 -0500 Subject: [PATCH 09/20] ErrUserInput --> StatusBadRequest --- http/handler.go | 2 +- vault/capabilities.go | 14 +++++++------- vault/token_store.go | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/http/handler.go b/http/handler.go index d77f602e13bbd..67cff6af4cad0 100644 --- a/http/handler.go +++ b/http/handler.go @@ -147,7 +147,7 @@ func respondErrorStatus(w http.ResponseWriter, err error) { status := http.StatusInternalServerError switch { // Keep adding more error types here to appropriate the status codes - case errwrap.ContainsType(err, new(vault.ErrUserInput)): + case errwrap.ContainsType(err, new(vault.StatusBadRequest)): status = http.StatusBadRequest } respondError(w, status, err) diff --git a/vault/capabilities.go b/vault/capabilities.go index 39a30bd3db9f6..82d65558aa8eb 100644 --- a/vault/capabilities.go +++ b/vault/capabilities.go @@ -3,12 +3,12 @@ package vault // Struct to identify user input errors. // This is helpful in responding the appropriate status codes to clients // from the HTTP endpoints. -type ErrUserInput struct { +type StatusBadRequest struct { Message string } // Implementing error interface -func (e *ErrUserInput) Error() string { +func (e *StatusBadRequest) Error() string { return e.Message } @@ -16,13 +16,13 @@ func (e *ErrUserInput) Error() string { // the token accessor an the given path func (c *Core) CapabilitiesAccessor(accessorID, path string) ([]string, error) { if path == "" { - return nil, &ErrUserInput{ + return nil, &StatusBadRequest{ Message: "missing path", } } if accessorID == "" { - return nil, &ErrUserInput{ + return nil, &StatusBadRequest{ Message: "missing accessor_id", } } @@ -38,13 +38,13 @@ func (c *Core) CapabilitiesAccessor(accessorID, path string) ([]string, error) { // Capabilities is used to fetch the capabilities of the given token on the given path func (c *Core) Capabilities(token, path string) ([]string, error) { if path == "" { - return nil, &ErrUserInput{ + return nil, &StatusBadRequest{ Message: "missing path", } } if token == "" { - return nil, &ErrUserInput{ + return nil, &StatusBadRequest{ Message: "missing token", } } @@ -54,7 +54,7 @@ func (c *Core) Capabilities(token, path string) ([]string, error) { return nil, err } if te == nil { - return nil, &ErrUserInput{ + return nil, &StatusBadRequest{ Message: "invalid token", } } diff --git a/vault/token_store.go b/vault/token_store.go index 39600b169f60d..66d1d6ecb8c85 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -606,7 +606,7 @@ func (ts *TokenStore) lookupByAccessorID(accessorID string) (string, error) { return "", fmt.Errorf("failed to read index using accessor ID: %s", err) } if entry == nil { - return "", &ErrUserInput{ + return "", &StatusBadRequest{ Message: "invalid accessor ID", } } From 7b996523cda1ba0ddc4497cd6737dfc52d530a96 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Tue, 8 Mar 2016 22:27:24 -0500 Subject: [PATCH 10/20] Error text corrections and minor refactoring --- logical/auth.go | 2 +- vault/capabilities.go | 30 ++++++++++-------------------- vault/token_store.go | 18 ++++++++---------- 3 files changed, 19 insertions(+), 31 deletions(-) diff --git a/logical/auth.go b/logical/auth.go index fa887e3d29d45..129fecc145a93 100644 --- a/logical/auth.go +++ b/logical/auth.go @@ -38,7 +38,7 @@ type Auth struct { // to perform management functionalities (especially revocation) when // ClientToken in the audit logs are obfuscated. AccessorID can be used // to revoke a ClientToken and to lookup the capabilities of the ClientToken, - // all without actually knowing the ClientToken. + // both without actually knowing the ClientToken. AccessorID string } diff --git a/vault/capabilities.go b/vault/capabilities.go index 82d65558aa8eb..ab7dd4f52a6ee 100644 --- a/vault/capabilities.go +++ b/vault/capabilities.go @@ -4,27 +4,23 @@ package vault // This is helpful in responding the appropriate status codes to clients // from the HTTP endpoints. type StatusBadRequest struct { - Message string + Err string } // Implementing error interface -func (e *StatusBadRequest) Error() string { - return e.Message +func (s *StatusBadRequest) Error() string { + return s.Err } -// CapabilitiesAccessor is used to fetch the capabilities of the token associated with -// the token accessor an the given path +// CapabilitiesAccessor is used to fetch the capabilities of the token +// which associated with the given accessorID on the given path func (c *Core) CapabilitiesAccessor(accessorID, path string) ([]string, error) { if path == "" { - return nil, &StatusBadRequest{ - Message: "missing path", - } + return nil, &StatusBadRequest{Err: "missing path"} } if accessorID == "" { - return nil, &StatusBadRequest{ - Message: "missing accessor_id", - } + return nil, &StatusBadRequest{Err: "missing accessor_id"} } token, err := c.tokenStore.lookupByAccessorID(accessorID) @@ -38,15 +34,11 @@ func (c *Core) CapabilitiesAccessor(accessorID, path string) ([]string, error) { // Capabilities is used to fetch the capabilities of the given token on the given path func (c *Core) Capabilities(token, path string) ([]string, error) { if path == "" { - return nil, &StatusBadRequest{ - Message: "missing path", - } + return nil, &StatusBadRequest{Err: "missing path"} } if token == "" { - return nil, &StatusBadRequest{ - Message: "missing token", - } + return nil, &StatusBadRequest{Err: "missing token"} } te, err := c.tokenStore.Lookup(token) @@ -54,9 +46,7 @@ func (c *Core) Capabilities(token, path string) ([]string, error) { return nil, err } if te == nil { - return nil, &StatusBadRequest{ - Message: "invalid token", - } + return nil, &StatusBadRequest{Err: "invalid token"} } if te.Policies == nil { diff --git a/vault/token_store.go b/vault/token_store.go index 66d1d6ecb8c85..948ca32f98cd0 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -169,7 +169,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) Fields: map[string]*framework.FieldSchema{ "accessor_id": &framework.FieldSchema{ Type: framework.TypeString, - Description: "Accessor ID associated with the token being revoked", + Description: "Accessor ID of the token", }, }, @@ -338,6 +338,7 @@ func (ts *TokenStore) rootToken() (*TokenEntry, error) { } // CreateAccessorID is used to create an identifier for the token ID. +// An storage index, mapping the accessor ID to the token ID is also created. func (ts *TokenStore) createAccessorID(entry *TokenEntry) error { defer metrics.MeasureSince([]string{"token", "createAccessorID"}, time.Now()) @@ -352,7 +353,7 @@ func (ts *TokenStore) createAccessorID(entry *TokenEntry) error { path := lookupPrefix + ts.SaltID(entry.AccessorID) le := &logical.StorageEntry{Key: path, Value: []byte(entry.ID)} if err := ts.view.Put(le); err != nil { - return fmt.Errorf("failed to persist accessor index entry: %v", err) + return fmt.Errorf("failed to persist accessor ID index entry: %v", err) } return nil } @@ -606,9 +607,7 @@ func (ts *TokenStore) lookupByAccessorID(accessorID string) (string, error) { return "", fmt.Errorf("failed to read index using accessor ID: %s", err) } if entry == nil { - return "", &StatusBadRequest{ - Message: "invalid accessor ID", - } + return "", &StatusBadRequest{Err: "invalid accessor ID"} } return string(entry.Value), nil @@ -619,7 +618,7 @@ func (ts *TokenStore) lookupByAccessorID(accessorID string) (string, error) { func (ts *TokenStore) handleLookupAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { accessorID := data.Get("accessor_id").(string) if accessorID == "" { - return logical.ErrorResponse("missing accessor_id"), nil + return nil, &StatusBadRequest{Err: "missing accessor_id"} } tokenID, err := ts.lookupByAccessorID(accessorID) @@ -664,7 +663,7 @@ func (ts *TokenStore) handleLookupAccessor(req *logical.Request, data *framework func (ts *TokenStore) handleRevokeAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { accessorID := data.Get("accessor_id").(string) if accessorID == "" { - return logical.ErrorResponse("missing accessor_id"), nil + return nil, &StatusBadRequest{Err: "missing accessor_id"} } tokenID, err := ts.lookupByAccessorID(accessorID) @@ -1071,11 +1070,10 @@ as revocation of tokens. The tokens are renewable if associated with a lease.` tokenCreateHelp = `The token create path is used to create new tokens.` tokenCreateOrphanHelp = `The token create path is used to create new orphan tokens.` tokenLookupHelp = `This endpoint will lookup a token and its properties.` - tokenLookupAccessorHelp = `This endpoint will lookup an accessor and its properties. -This will not return the token ID associated with the accessor ID.` + tokenLookupAccessorHelp = `This endpoint will lookup a token associated with the given accessor ID and its properties. Response will not contain the token ID.` tokenRevokeHelp = `This endpoint will delete the given token and all of its child tokens.` tokenRevokeSelfHelp = `This endpoint will delete the token used to call it and all of its child tokens.` - tokenRevokeAccessorHelp = `This endpoint will delete the token associated with the accessor ID` + tokenRevokeAccessorHelp = `This endpoint will delete the token associated with the accessor ID and all of its child tokens.` tokenRevokeOrphanHelp = `This endpoint will delete the token and orphan its child tokens.` tokenRevokePrefixHelp = `This endpoint will delete all tokens generated under a prefix with their child tokens.` tokenRenewHelp = `This endpoint will renew the given token and prevent expiration.` From 2a35de81dcc25749fdd663e48de68a25ac271092 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Wed, 9 Mar 2016 06:23:31 -0500 Subject: [PATCH 11/20] AccessorID --> Accessor, accessor_id --> accessor --- api/secret.go | 2 +- command/format.go | 2 +- http/logical.go | 4 ++-- http/sys_capabilities.go | 6 +++--- logical/auth.go | 6 +++--- vault/capabilities.go | 10 ++++----- vault/token_store.go | 44 ++++++++++++++++++++-------------------- 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/api/secret.go b/api/secret.go index 5c77da2b7409c..7e10f1ff0cea5 100644 --- a/api/secret.go +++ b/api/secret.go @@ -28,7 +28,7 @@ type Secret struct { // SecretAuth is the structure containing auth information if we have it. type SecretAuth struct { ClientToken string `json:"client_token"` - AccessorID string `json:"accessor_id"` + Accessor string `json:"accessor"` Policies []string `json:"policies"` Metadata map[string]string `json:"metadata"` diff --git a/command/format.go b/command/format.go index 800930b5131cd..93b812312ecf2 100644 --- a/command/format.go +++ b/command/format.go @@ -143,7 +143,7 @@ func (t TableFormatter) OutputSecret(ui cli.Ui, secret, s *api.Secret) error { if s.Auth != nil { input = append(input, fmt.Sprintf("token %s %s", config.Delim, s.Auth.ClientToken)) - input = append(input, fmt.Sprintf("token_accessor %s %s", config.Delim, s.Auth.AccessorID)) + input = append(input, fmt.Sprintf("token_accessor %s %s", config.Delim, s.Auth.Accessor)) input = append(input, fmt.Sprintf("token_duration %s %d", config.Delim, s.Auth.LeaseDuration)) input = append(input, fmt.Sprintf("token_renewable %s %v", config.Delim, s.Auth.Renewable)) input = append(input, fmt.Sprintf("token_policies %s %v", config.Delim, s.Auth.Policies)) diff --git a/http/logical.go b/http/logical.go index 1b30c0781a392..816df95c0c4ce 100644 --- a/http/logical.go +++ b/http/logical.go @@ -124,7 +124,7 @@ func respondLogical(w http.ResponseWriter, r *http.Request, path string, dataOnl if resp.Auth != nil { logicalResp.Auth = &Auth{ ClientToken: resp.Auth.ClientToken, - AccessorID: resp.Auth.AccessorID, + Accessor: resp.Auth.Accessor, Policies: resp.Auth.Policies, Metadata: resp.Auth.Metadata, LeaseDuration: int(resp.Auth.TTL.Seconds()), @@ -219,7 +219,7 @@ type LogicalResponse struct { type Auth struct { ClientToken string `json:"client_token"` - AccessorID string `json:"accessor_id"` + Accessor string `json:"accessor"` Policies []string `json:"policies"` Metadata map[string]string `json:"metadata"` LeaseDuration int `json:"lease_duration"` diff --git a/http/sys_capabilities.go b/http/sys_capabilities.go index 701bad8274e15..93d135ed3f2dd 100644 --- a/http/sys_capabilities.go +++ b/http/sys_capabilities.go @@ -25,7 +25,7 @@ func handleSysCapabilitiesAccessor(core *vault.Core) http.Handler { return } - capabilities, err := core.CapabilitiesAccessor(data.AccessorID, data.Path) + capabilities, err := core.CapabilitiesAccessor(data.Accessor, data.Path) if err != nil { respondErrorStatus(w, err) return @@ -84,6 +84,6 @@ type capabilitiesRequest struct { } type capabilitiesAccessorRequest struct { - AccessorID string `json:"accessor_id"` - Path string `json:"path"` + Accessor string `json:"accessor"` + Path string `json:"path"` } diff --git a/logical/auth.go b/logical/auth.go index 129fecc145a93..1636fb5bdfe09 100644 --- a/logical/auth.go +++ b/logical/auth.go @@ -34,12 +34,12 @@ type Auth struct { // returned. Setting this manually will have no effect. ClientToken string - // AccessorID is the identifier for the ClientToken. This can be used + // Accessor is the identifier for the ClientToken. This can be used // to perform management functionalities (especially revocation) when - // ClientToken in the audit logs are obfuscated. AccessorID can be used + // ClientToken in the audit logs are obfuscated. Accessor can be used // to revoke a ClientToken and to lookup the capabilities of the ClientToken, // both without actually knowing the ClientToken. - AccessorID string + Accessor string } func (a *Auth) GoString() string { diff --git a/vault/capabilities.go b/vault/capabilities.go index ab7dd4f52a6ee..9c0164ed823b4 100644 --- a/vault/capabilities.go +++ b/vault/capabilities.go @@ -13,17 +13,17 @@ func (s *StatusBadRequest) Error() string { } // CapabilitiesAccessor is used to fetch the capabilities of the token -// which associated with the given accessorID on the given path -func (c *Core) CapabilitiesAccessor(accessorID, path string) ([]string, error) { +// which associated with the given accessor on the given path +func (c *Core) CapabilitiesAccessor(accessor, path string) ([]string, error) { if path == "" { return nil, &StatusBadRequest{Err: "missing path"} } - if accessorID == "" { - return nil, &StatusBadRequest{Err: "missing accessor_id"} + if accessor == "" { + return nil, &StatusBadRequest{Err: "missing accessor"} } - token, err := c.tokenStore.lookupByAccessorID(accessorID) + token, err := c.tokenStore.lookupByAccessor(accessor) if err != nil { return nil, err } diff --git a/vault/token_store.go b/vault/token_store.go index 948ca32f98cd0..ea8e852fd60bf 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -131,7 +131,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) Pattern: "lookup-accessor$", Fields: map[string]*framework.FieldSchema{ - "accessor_id": &framework.FieldSchema{ + "accessor": &framework.FieldSchema{ Type: framework.TypeString, Description: "Accessor ID to lookup", }, @@ -167,7 +167,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) Pattern: "revoke-accessor$", Fields: map[string]*framework.FieldSchema{ - "accessor_id": &framework.FieldSchema{ + "accessor": &framework.FieldSchema{ Type: framework.TypeString, Description: "Accessor ID of the token", }, @@ -300,7 +300,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) // TokenEntry is used to represent a given token type TokenEntry struct { ID string // ID of this entry, generally a random UUID - AccessorID string // Accessor ID for this token, a random UUID + Accessor string // Accessor ID for this token, a random UUID Parent string // Parent token, used for revocation trees Policies []string // Which named policies should be used Path string // Used for audit trails, this is something like "auth/user/login" @@ -337,20 +337,20 @@ func (ts *TokenStore) rootToken() (*TokenEntry, error) { return te, nil } -// CreateAccessorID is used to create an identifier for the token ID. +// CreateAccessor is used to create an identifier for the token ID. // An storage index, mapping the accessor ID to the token ID is also created. -func (ts *TokenStore) createAccessorID(entry *TokenEntry) error { - defer metrics.MeasureSince([]string{"token", "createAccessorID"}, time.Now()) +func (ts *TokenStore) createAccessor(entry *TokenEntry) error { + defer metrics.MeasureSince([]string{"token", "createAccessor"}, time.Now()) // Create a random accessor ID accessorUUID, err := uuid.GenerateUUID() if err != nil { return err } - entry.AccessorID = accessorUUID + entry.Accessor = accessorUUID // Create index entry, mapping the Accessor ID to the Token ID - path := lookupPrefix + ts.SaltID(entry.AccessorID) + path := lookupPrefix + ts.SaltID(entry.Accessor) le := &logical.StorageEntry{Key: path, Value: []byte(entry.ID)} if err := ts.view.Put(le); err != nil { return fmt.Errorf("failed to persist accessor ID index entry: %v", err) @@ -371,7 +371,7 @@ func (ts *TokenStore) create(entry *TokenEntry) error { entry.ID = entryUUID } - err := ts.createAccessorID(entry) + err := ts.createAccessor(entry) if err != nil { return err } @@ -533,8 +533,8 @@ func (ts *TokenStore) revokeSalted(saltedId string) error { } // Clear the accessor ID index if any - if entry != nil && entry.AccessorID != "" { - path := lookupPrefix + ts.SaltID(entry.AccessorID) + if entry != nil && entry.Accessor != "" { + path := lookupPrefix + ts.SaltID(entry.Accessor) if ts.view.Delete(path); err != nil { return fmt.Errorf("failed to delete entry: %v", err) } @@ -601,8 +601,8 @@ func (ts *TokenStore) revokeTreeSalted(saltedId string) error { return nil } -func (ts *TokenStore) lookupByAccessorID(accessorID string) (string, error) { - entry, err := ts.view.Get(lookupPrefix + ts.SaltID(accessorID)) +func (ts *TokenStore) lookupByAccessor(accessor string) (string, error) { + entry, err := ts.view.Get(lookupPrefix + ts.SaltID(accessor)) if err != nil { return "", fmt.Errorf("failed to read index using accessor ID: %s", err) } @@ -616,12 +616,12 @@ func (ts *TokenStore) lookupByAccessorID(accessorID string) (string, error) { // handleLookupAccessor handles the auth/token/lookup-accessor path for returning // the properties of the token associated with the accessor ID func (ts *TokenStore) handleLookupAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { - accessorID := data.Get("accessor_id").(string) - if accessorID == "" { - return nil, &StatusBadRequest{Err: "missing accessor_id"} + accessor := data.Get("accessor").(string) + if accessor == "" { + return nil, &StatusBadRequest{Err: "missing accessor"} } - tokenID, err := ts.lookupByAccessorID(accessorID) + tokenID, err := ts.lookupByAccessor(accessor) if err != nil { return nil, err } @@ -661,12 +661,12 @@ func (ts *TokenStore) handleLookupAccessor(req *logical.Request, data *framework // handleRevokeAccessor handles the auth/token/revoke-accessor path for revoking // the token associated with the accessor ID func (ts *TokenStore) handleRevokeAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { - accessorID := data.Get("accessor_id").(string) - if accessorID == "" { - return nil, &StatusBadRequest{Err: "missing accessor_id"} + accessor := data.Get("accessor").(string) + if accessor == "" { + return nil, &StatusBadRequest{Err: "missing accessor"} } - tokenID, err := ts.lookupByAccessorID(accessorID) + tokenID, err := ts.lookupByAccessor(accessor) if err != nil { return nil, err } @@ -853,7 +853,7 @@ func (ts *TokenStore) handleCreateCommon( Renewable: true, }, ClientToken: te.ID, - AccessorID: te.AccessorID, + Accessor: te.Accessor, }, } From c7c9e0b8cf90e4a071df4992a9189494dc6733aa Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Wed, 9 Mar 2016 09:05:04 -0500 Subject: [PATCH 12/20] New prefix for accessor indexes --- vault/token_store.go | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/vault/token_store.go b/vault/token_store.go index ea8e852fd60bf..1e455545f1bc7 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -21,6 +21,10 @@ const ( // primary ID based index lookupPrefix = "id/" + // accessorPrefix is the prefix used to store the index from + // Accessor to Token ID + accessorPrefix = "accessor/" + // parentPrefix is the prefix used to store tokens for their // secondar parent based index parentPrefix = "parent/" @@ -133,7 +137,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) Fields: map[string]*framework.FieldSchema{ "accessor": &framework.FieldSchema{ Type: framework.TypeString, - Description: "Accessor ID to lookup", + Description: "Accessor to lookup", }, }, @@ -169,7 +173,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) Fields: map[string]*framework.FieldSchema{ "accessor": &framework.FieldSchema{ Type: framework.TypeString, - Description: "Accessor ID of the token", + Description: "Accessor of the token", }, }, @@ -300,7 +304,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) // TokenEntry is used to represent a given token type TokenEntry struct { ID string // ID of this entry, generally a random UUID - Accessor string // Accessor ID for this token, a random UUID + Accessor string // Accessor for this token, a random UUID Parent string // Parent token, used for revocation trees Policies []string // Which named policies should be used Path string // Used for audit trails, this is something like "auth/user/login" @@ -338,22 +342,22 @@ func (ts *TokenStore) rootToken() (*TokenEntry, error) { } // CreateAccessor is used to create an identifier for the token ID. -// An storage index, mapping the accessor ID to the token ID is also created. +// An storage index, mapping the accessor to the token ID is also created. func (ts *TokenStore) createAccessor(entry *TokenEntry) error { defer metrics.MeasureSince([]string{"token", "createAccessor"}, time.Now()) - // Create a random accessor ID + // Create a random accessor accessorUUID, err := uuid.GenerateUUID() if err != nil { return err } entry.Accessor = accessorUUID - // Create index entry, mapping the Accessor ID to the Token ID - path := lookupPrefix + ts.SaltID(entry.Accessor) + // Create index entry, mapping the Accessor to the Token ID + path := accessorPrefix + ts.SaltID(entry.Accessor) le := &logical.StorageEntry{Key: path, Value: []byte(entry.ID)} if err := ts.view.Put(le); err != nil { - return fmt.Errorf("failed to persist accessor ID index entry: %v", err) + return fmt.Errorf("failed to persist accessor index entry: %v", err) } return nil } @@ -532,9 +536,9 @@ func (ts *TokenStore) revokeSalted(saltedId string) error { } } - // Clear the accessor ID index if any + // Clear the accessor index if any if entry != nil && entry.Accessor != "" { - path := lookupPrefix + ts.SaltID(entry.Accessor) + path := accessorPrefix + ts.SaltID(entry.Accessor) if ts.view.Delete(path); err != nil { return fmt.Errorf("failed to delete entry: %v", err) } @@ -602,19 +606,19 @@ func (ts *TokenStore) revokeTreeSalted(saltedId string) error { } func (ts *TokenStore) lookupByAccessor(accessor string) (string, error) { - entry, err := ts.view.Get(lookupPrefix + ts.SaltID(accessor)) + entry, err := ts.view.Get(accessorPrefix + ts.SaltID(accessor)) if err != nil { - return "", fmt.Errorf("failed to read index using accessor ID: %s", err) + return "", fmt.Errorf("failed to read index using accessor: %s", err) } if entry == nil { - return "", &StatusBadRequest{Err: "invalid accessor ID"} + return "", &StatusBadRequest{Err: "invalid accessor"} } return string(entry.Value), nil } // handleLookupAccessor handles the auth/token/lookup-accessor path for returning -// the properties of the token associated with the accessor ID +// the properties of the token associated with the accessor func (ts *TokenStore) handleLookupAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { accessor := data.Get("accessor").(string) if accessor == "" { @@ -651,7 +655,7 @@ func (ts *TokenStore) handleLookupAccessor(req *logical.Request, data *framework } // Remove the token ID from the response - if resp.Data != nil && resp.Data["id"] != "" { + if resp.Data != nil { resp.Data["id"] = "" } @@ -659,7 +663,7 @@ func (ts *TokenStore) handleLookupAccessor(req *logical.Request, data *framework } // handleRevokeAccessor handles the auth/token/revoke-accessor path for revoking -// the token associated with the accessor ID +// the token associated with the accessor func (ts *TokenStore) handleRevokeAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { accessor := data.Get("accessor").(string) if accessor == "" { @@ -1070,10 +1074,10 @@ as revocation of tokens. The tokens are renewable if associated with a lease.` tokenCreateHelp = `The token create path is used to create new tokens.` tokenCreateOrphanHelp = `The token create path is used to create new orphan tokens.` tokenLookupHelp = `This endpoint will lookup a token and its properties.` - tokenLookupAccessorHelp = `This endpoint will lookup a token associated with the given accessor ID and its properties. Response will not contain the token ID.` + tokenLookupAccessorHelp = `This endpoint will lookup a token associated with the given accessor and its properties. Response will not contain the token ID.` tokenRevokeHelp = `This endpoint will delete the given token and all of its child tokens.` tokenRevokeSelfHelp = `This endpoint will delete the token used to call it and all of its child tokens.` - tokenRevokeAccessorHelp = `This endpoint will delete the token associated with the accessor ID and all of its child tokens.` + tokenRevokeAccessorHelp = `This endpoint will delete the token associated with the accessor and all of its child tokens.` tokenRevokeOrphanHelp = `This endpoint will delete the token and orphan its child tokens.` tokenRevokePrefixHelp = `This endpoint will delete all tokens generated under a prefix with their child tokens.` tokenRenewHelp = `This endpoint will renew the given token and prevent expiration.` From 928d872ed9d2335c5f4320e896adaa82a7c4632a Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Wed, 9 Mar 2016 09:31:09 -0500 Subject: [PATCH 13/20] Add docs for new token endpoints --- vault/token_store.go | 6 +- website/source/docs/auth/token.html.md | 88 ++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/vault/token_store.go b/vault/token_store.go index 1e455545f1bc7..cd047e8331ec5 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -137,7 +137,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) Fields: map[string]*framework.FieldSchema{ "accessor": &framework.FieldSchema{ Type: framework.TypeString, - Description: "Accessor to lookup", + Description: "Accessor of the token to lookup", }, }, @@ -341,7 +341,7 @@ func (ts *TokenStore) rootToken() (*TokenEntry, error) { return te, nil } -// CreateAccessor is used to create an identifier for the token ID. +// createAccessor is used to create an identifier for the token ID. // An storage index, mapping the accessor to the token ID is also created. func (ts *TokenStore) createAccessor(entry *TokenEntry) error { defer metrics.MeasureSince([]string{"token", "createAccessor"}, time.Now()) @@ -353,7 +353,7 @@ func (ts *TokenStore) createAccessor(entry *TokenEntry) error { } entry.Accessor = accessorUUID - // Create index entry, mapping the Accessor to the Token ID + // Create index entry, mapping the accessor to the token ID path := accessorPrefix + ts.SaltID(entry.Accessor) le := &logical.StorageEntry{Key: path, Value: []byte(entry.ID)} if err := ts.view.Put(le); err != nil { diff --git a/website/source/docs/auth/token.html.md b/website/source/docs/auth/token.html.md index 0f7df70ab97dc..2fabfbf3541bb 100644 --- a/website/source/docs/auth/token.html.md +++ b/website/source/docs/auth/token.html.md @@ -412,3 +412,91 @@ of the header should be "X-Vault-Token" and the value should be the token. +### /auth/token/lookup-accessor +#### POST + +
+
Description
+
+ Fetch the properties of the token associated with the accessor, except the token ID. + This is meant for purposes where there is no access to token ID but there is need + to fetch the properties of a token. +
+ +
Method
+
POST
+ +
URL
+
`/auth/token/lookup-accessor`
+ +
Parameters
+
+
    +
  • + accessor + required + Accessor of the token to lookup. +
  • +
+
+ +
Returns
+
+ + ```javascript + { + "lease_id": "", + "renewable": false, + "lease_duration": 0, + "data": { + "creation_time": 1457533232, + "creation_ttl": 2592000, + "display_name": "token", + "id": "", + "meta": null, + "num_uses": 0, + "orphan": false, + "path": "auth/token/create", + "policies": ["default", "web"], + "ttl": 2591976 + }, + "warnings": null, + "auth": null + } + ``` +
+
+ +### /auth/token/revoke-accessor +#### POST + +
+
Description
+
+ Revoke the token associated with the accessor and all the child tokens. + This is meant for purposes where there is no access to token ID but + there is need to revoke a token and its children. +
+ +
Method
+
POST
+ +
URL
+
`/auth/token/revoke-accessor`
+ +
Parameters
+
+
    +
  • + accessor + required + Accessor of the token. +
  • +
+
+ +
Returns
+
`204` response code. +
+
+ From 16c4b52df34c92e320b8c26109c1cdfb41d3c432 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Wed, 9 Mar 2016 09:48:32 -0500 Subject: [PATCH 14/20] Added docs for /sys/capabilities-accessor --- vault/token_store.go | 44 ++++++++--------- .../http/sys-capabilities-accessor.html.md | 48 +++++++++++++++++++ website/source/layouts/http.erb | 4 ++ 3 files changed, 74 insertions(+), 22 deletions(-) create mode 100644 website/source/docs/http/sys-capabilities-accessor.html.md diff --git a/vault/token_store.go b/vault/token_store.go index cd047e8331ec5..890e6ee4d7140 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -142,11 +142,11 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: t.handleLookupAccessor, + logical.UpdateOperation: t.handleUpdateLookupAccessor, }, - HelpSynopsis: strings.TrimSpace(tokenLookupAccessorHelp), - HelpDescription: strings.TrimSpace(tokenLookupAccessorHelp), + HelpSynopsis: strings.TrimSpace(lookupAccessorHelp), + HelpDescription: strings.TrimSpace(lookupAccessorHelp), }, &framework.Path{ @@ -178,11 +178,11 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: t.handleRevokeAccessor, + logical.UpdateOperation: t.handleUpdateRevokeAccessor, }, - HelpSynopsis: strings.TrimSpace(tokenRevokeAccessorHelp), - HelpDescription: strings.TrimSpace(tokenRevokeAccessorHelp), + HelpSynopsis: strings.TrimSpace(revokeAccessorHelp), + HelpDescription: strings.TrimSpace(revokeAccessorHelp), }, &framework.Path{ @@ -342,7 +342,7 @@ func (ts *TokenStore) rootToken() (*TokenEntry, error) { } // createAccessor is used to create an identifier for the token ID. -// An storage index, mapping the accessor to the token ID is also created. +// A storage index, mapping the accessor to the token ID is also created. func (ts *TokenStore) createAccessor(entry *TokenEntry) error { defer metrics.MeasureSince([]string{"token", "createAccessor"}, time.Now()) @@ -617,9 +617,9 @@ func (ts *TokenStore) lookupByAccessor(accessor string) (string, error) { return string(entry.Value), nil } -// handleLookupAccessor handles the auth/token/lookup-accessor path for returning +// handleUpdateLookupAccessor handles the auth/token/lookup-accessor path for returning // the properties of the token associated with the accessor -func (ts *TokenStore) handleLookupAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { +func (ts *TokenStore) handleUpdateLookupAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { accessor := data.Get("accessor").(string) if accessor == "" { return nil, &StatusBadRequest{Err: "missing accessor"} @@ -662,9 +662,9 @@ func (ts *TokenStore) handleLookupAccessor(req *logical.Request, data *framework return resp, nil } -// handleRevokeAccessor handles the auth/token/revoke-accessor path for revoking +// handleUpdateRevokeAccessor handles the auth/token/revoke-accessor path for revoking // the token associated with the accessor -func (ts *TokenStore) handleRevokeAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { +func (ts *TokenStore) handleUpdateRevokeAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { accessor := data.Get("accessor").(string) if accessor == "" { return nil, &StatusBadRequest{Err: "missing accessor"} @@ -1071,15 +1071,15 @@ const ( Client tokens are used to identify a client and to allow Vault to associate policies and ACLs which are enforced on every request. This backend also allows for generating sub-tokens as well as revocation of tokens. The tokens are renewable if associated with a lease.` - tokenCreateHelp = `The token create path is used to create new tokens.` - tokenCreateOrphanHelp = `The token create path is used to create new orphan tokens.` - tokenLookupHelp = `This endpoint will lookup a token and its properties.` - tokenLookupAccessorHelp = `This endpoint will lookup a token associated with the given accessor and its properties. Response will not contain the token ID.` - tokenRevokeHelp = `This endpoint will delete the given token and all of its child tokens.` - tokenRevokeSelfHelp = `This endpoint will delete the token used to call it and all of its child tokens.` - tokenRevokeAccessorHelp = `This endpoint will delete the token associated with the accessor and all of its child tokens.` - tokenRevokeOrphanHelp = `This endpoint will delete the token and orphan its child tokens.` - tokenRevokePrefixHelp = `This endpoint will delete all tokens generated under a prefix with their child tokens.` - tokenRenewHelp = `This endpoint will renew the given token and prevent expiration.` - tokenRenewSelfHelp = `This endpoint will renew the token used to call it and prevent expiration.` + tokenCreateHelp = `The token create path is used to create new tokens.` + tokenCreateOrphanHelp = `The token create path is used to create new orphan tokens.` + tokenLookupHelp = `This endpoint will lookup a token and its properties.` + lookupAccessorHelp = `This endpoint will lookup a token associated with the given accessor and its properties. Response will not contain the token ID.` + tokenRevokeHelp = `This endpoint will delete the given token and all of its child tokens.` + tokenRevokeSelfHelp = `This endpoint will delete the token used to call it and all of its child tokens.` + revokeAccessorHelp = `This endpoint will delete the token associated with the accessor and all of its child tokens.` + tokenRevokeOrphanHelp = `This endpoint will delete the token and orphan its child tokens.` + tokenRevokePrefixHelp = `This endpoint will delete all tokens generated under a prefix with their child tokens.` + tokenRenewHelp = `This endpoint will renew the given token and prevent expiration.` + tokenRenewSelfHelp = `This endpoint will renew the token used to call it and prevent expiration.` ) diff --git a/website/source/docs/http/sys-capabilities-accessor.html.md b/website/source/docs/http/sys-capabilities-accessor.html.md new file mode 100644 index 0000000000000..4549b203fbb72 --- /dev/null +++ b/website/source/docs/http/sys-capabilities-accessor.html.md @@ -0,0 +1,48 @@ +--- +layout: "http" +page_title: "HTTP API: /sys/capabilities-accessor" +sidebar_current: "docs-http-auth-capabilities-accessor" +description: |- + The `/sys/capabilities-accessor` endpoint is used to fetch the capabilities of the token associated with an accessor, on the given path. +--- + +# /sys/capabilities-accessor + +## POST + +
+
Description
+
+ Returns the capabilities of the token associated with an accessor, on the given path. +
+ +
Method
+
POST
+ +
Parameters
+
+
    +
  • + accessor + required + Accessor of the token. +
  • +
  • + path + required + Path on which the token's capabilities will be checked. +
  • +
+
+ +
Returns
+
+ + ```javascript + { + "capabilities": ["read", "list"] + } + ``` + +
+
diff --git a/website/source/layouts/http.erb b/website/source/layouts/http.erb index b8ea3443e11f7..5b61c1262ac42 100644 --- a/website/source/layouts/http.erb +++ b/website/source/layouts/http.erb @@ -77,6 +77,10 @@ > /sys/capabilities-self + + > + /sys/capabilities-accessor + From a5468237ed154689bc60b4f22282a2561bd14457 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Wed, 9 Mar 2016 11:29:09 -0500 Subject: [PATCH 15/20] Added tests for 'sys/capabilities-accessor' endpoint --- http/sys_capabilities_test.go | 72 +++++++++++++++++++++++++++++++++++ vault/capabilities_test.go | 55 ++++++++++++++++++++++++++ vault/token_store.go | 1 + 3 files changed, 128 insertions(+) diff --git a/http/sys_capabilities_test.go b/http/sys_capabilities_test.go index f192e2b0a63b7..726621ec7f3fa 100644 --- a/http/sys_capabilities_test.go +++ b/http/sys_capabilities_test.go @@ -7,6 +7,78 @@ import ( "github.com/hashicorp/vault/vault" ) +func TestSysCapabilitiesAccessor(t *testing.T) { + core, _, token := vault.TestCoreUnsealed(t) + ln, addr := TestServer(t, core) + defer ln.Close() + TestServerAuth(t, addr, token) + + // Lookup the token properties + resp := testHttpGet(t, token, addr+"/v1/auth/token/lookup/"+token) + var lookupResp map[string]interface{} + testResponseStatus(t, resp, 200) + testResponseBody(t, resp, &lookupResp) + + // Retrieve the accessor from the token properties + lookupData := lookupResp["data"].(map[string]interface{}) + accessor := lookupData["accessor"].(string) + + resp = testHttpPost(t, token, addr+"/v1/sys/capabilities-accessor", map[string]interface{}{ + "accessor": accessor, + "path": "testpath", + }) + + var actual map[string][]string + testResponseStatus(t, resp, 200) + testResponseBody(t, resp, &actual) + + expected := map[string][]string{ + "capabilities": []string{"root"}, + } + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actual, expected) + } + + // Testing for non-root token's accessor + // Create a policy first + resp = testHttpPost(t, token, addr+"/v1/sys/policy/foo", map[string]interface{}{ + "rules": `path "testpath" {capabilities = ["read","sudo"]}`, + }) + testResponseStatus(t, resp, 204) + + // Create a token against the test policy + resp = testHttpPost(t, token, addr+"/v1/auth/token/create", map[string]interface{}{ + "policies": []string{"foo"}, + }) + + var tokenResp map[string]interface{} + testResponseStatus(t, resp, 200) + testResponseBody(t, resp, &tokenResp) + + // Check if desired policies are present in the token + auth := tokenResp["auth"].(map[string]interface{}) + actualPolicies := auth["policies"] + expectedPolicies := []interface{}{"default", "foo"} + if !reflect.DeepEqual(actualPolicies, expectedPolicies) { + t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actualPolicies, expectedPolicies) + } + + // Check the capabilities of non-root token using the accessor + resp = testHttpPost(t, token, addr+"/v1/sys/capabilities-accessor", map[string]interface{}{ + "accessor": auth["accessor"], + "path": "testpath", + }) + testResponseStatus(t, resp, 200) + testResponseBody(t, resp, &actual) + + expected = map[string][]string{ + "capabilities": []string{"sudo", "read"}, + } + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actual, expected) + } +} + func TestSysCapabilities(t *testing.T) { core, _, token := vault.TestCoreUnsealed(t) ln, addr := TestServer(t, core) diff --git a/vault/capabilities_test.go b/vault/capabilities_test.go index 8367dc90bd6fb..e3b0b46fc52da 100644 --- a/vault/capabilities_test.go +++ b/vault/capabilities_test.go @@ -5,6 +5,61 @@ import ( "testing" ) +func TestCapabilitiesAccessor_Basic(t *testing.T) { + c, _, token := TestCoreUnsealed(t) + + // Lookup the token in the store to get root token's accessor + tokenEntry, err := c.tokenStore.Lookup(token) + if err != nil { + t.Fatalf("err: %s", err) + } + accessor := tokenEntry.Accessor + + // Use the accessor to fetch the capabilities + actual, err := c.CapabilitiesAccessor(accessor, "path") + if err != nil { + t.Fatalf("err: %s", err) + } + expected := []string{"root"} + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actual, expected) + } + + // Create a policy + policy, _ := Parse(aclPolicy) + err = c.policyStore.SetPolicy(policy) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Create a token for the policy + ent := &TokenEntry{ + ID: "capabilitiestoken", + Path: "testpath", + Policies: []string{"dev"}, + } + if err := c.tokenStore.create(ent); err != nil { + t.Fatalf("err: %v", err) + } + + // Lookup the token in the store to get token's accessor + tokenEntry, err = c.tokenStore.Lookup("capabilitiestoken") + if err != nil { + t.Fatalf("err: %s", err) + } + accessor = tokenEntry.Accessor + + // Use the accessor to fetch the capabilities + actual, err = c.CapabilitiesAccessor(accessor, "foo/bar") + if err != nil { + t.Fatalf("err: %s", err) + } + expected = []string{"sudo", "read", "create"} + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actual, expected) + } +} + func TestCapabilities_Basic(t *testing.T) { c, _, token := TestCoreUnsealed(t) diff --git a/vault/token_store.go b/vault/token_store.go index 890e6ee4d7140..fc2aa515b21ab 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -984,6 +984,7 @@ func (ts *TokenStore) handleLookup( resp := &logical.Response{ Data: map[string]interface{}{ "id": out.ID, + "accessor": out.Accessor, "policies": out.Policies, "path": out.Path, "meta": out.Meta, From 76900d6d7297b96fdc3eb9e548ae3ad6e0e3360f Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Wed, 9 Mar 2016 12:50:26 -0500 Subject: [PATCH 16/20] Added tests for lookup-accessor and revoke-accessor endpoints --- vault/capabilities_test.go | 4 +- vault/token_store_test.go | 90 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/vault/capabilities_test.go b/vault/capabilities_test.go index e3b0b46fc52da..b560787a28d40 100644 --- a/vault/capabilities_test.go +++ b/vault/capabilities_test.go @@ -5,7 +5,7 @@ import ( "testing" ) -func TestCapabilitiesAccessor_Basic(t *testing.T) { +func TestCapabilitiesAccessor(t *testing.T) { c, _, token := TestCoreUnsealed(t) // Lookup the token in the store to get root token's accessor @@ -60,7 +60,7 @@ func TestCapabilitiesAccessor_Basic(t *testing.T) { } } -func TestCapabilities_Basic(t *testing.T) { +func TestCapabilities(t *testing.T) { c, _, token := TestCoreUnsealed(t) actual, err := c.Capabilities(token, "path") diff --git a/vault/token_store_test.go b/vault/token_store_test.go index 66b7f06e97f6d..e4d8eb7e42de1 100644 --- a/vault/token_store_test.go +++ b/vault/token_store_test.go @@ -18,6 +18,96 @@ func getBackendConfig(c *Core) *logical.BackendConfig { } } +func TestTokenStore_AccessorIndex(t *testing.T) { + _, ts, _, _ := TestCoreWithTokenStore(t) + + ent := &TokenEntry{Path: "test", Policies: []string{"dev", "ops"}} + if err := ts.create(ent); err != nil { + t.Fatalf("err: %s", err) + } + + out, err := ts.Lookup(ent.ID) + if err != nil { + t.Fatalf("err: %s", err) + } + + // Ensure that accessor is created + if out == nil || out.Accessor == "" { + t.Fatalf("bad: %#v", out) + } + + token, err := ts.lookupByAccessor(out.Accessor) + if err != nil { + t.Fatalf("err: %s", err) + } + + // Verify that the value returned from the index matches the token ID + if token != ent.ID { + t.Fatalf("bad: got\n%s\nexpected\n%s\n", token, ent.ID) + } +} + +func TestTokenStore_HandleRequest_LookupAccessor(t *testing.T) { + _, ts, _, root := TestCoreWithTokenStore(t) + testMakeToken(t, ts, root, "tokenid", "", []string{"foo"}) + out, err := ts.Lookup("tokenid") + if err != nil { + t.Fatalf("err: %s", err) + } + if out == nil { + t.Fatalf("err: %s", err) + } + + req := logical.TestRequest(t, logical.UpdateOperation, "lookup-accessor") + req.Data["accessor"] = out.Accessor + + resp, err := ts.HandleRequest(req) + if err != nil { + t.Fatalf("err: %s", err) + } + if resp.Data == nil { + t.Fatalf("response should contain data") + } + + if resp.Data["accessor"].(string) == "" { + t.Fatalf("accessor should not be empty") + } + + // Verify that the lookup-accessor operation does not return the token ID + if resp.Data["id"].(string) != "" { + t.Fatalf("token ID should not be returned") + } +} + +func TestTokenStore_HandleRequest_RevokeAccessor(t *testing.T) { + _, ts, _, root := TestCoreWithTokenStore(t) + testMakeToken(t, ts, root, "tokenid", "", []string{"foo"}) + out, err := ts.Lookup("tokenid") + if err != nil { + t.Fatalf("err: %s", err) + } + if out == nil { + t.Fatalf("err: %s", err) + } + + req := logical.TestRequest(t, logical.UpdateOperation, "revoke-accessor") + req.Data["accessor"] = out.Accessor + + _, err = ts.HandleRequest(req) + if err != nil { + t.Fatalf("err: %s", err) + } + + out, err = ts.Lookup("tokenid") + if err != nil { + t.Fatalf("err: %s", err) + } + + if out != nil { + t.Fatalf("bad:\ngot %#v\nexpected: nil\n", out) + } +} + func TestTokenStore_RootToken(t *testing.T) { _, ts, _, _ := TestCoreWithTokenStore(t) From da9ad9cee3f860a3225164c160d21e55b2af0ece Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Wed, 9 Mar 2016 13:08:37 -0500 Subject: [PATCH 17/20] Provide accessor to revove-accessor in the URL itself --- vault/token_store.go | 12 ++++++------ vault/token_store_test.go | 3 +-- website/source/docs/auth/token.html.md | 12 +++--------- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/vault/token_store.go b/vault/token_store.go index fc2aa515b21ab..13051c09be27d 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -114,7 +114,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, &framework.Path{ - Pattern: "lookup/(?P.+)", + Pattern: "lookup/" + framework.GenericNameRegex("token"), Fields: map[string]*framework.FieldSchema{ "token": &framework.FieldSchema{ @@ -168,7 +168,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, &framework.Path{ - Pattern: "revoke-accessor$", + Pattern: "revoke-accessor/" + framework.GenericNameRegex("accessor"), Fields: map[string]*framework.FieldSchema{ "accessor": &framework.FieldSchema{ @@ -197,7 +197,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, &framework.Path{ - Pattern: "revoke/(?P.+)", + Pattern: "revoke/" + framework.GenericNameRegex("token"), Fields: map[string]*framework.FieldSchema{ "token": &framework.FieldSchema{ @@ -215,7 +215,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, &framework.Path{ - Pattern: "revoke-orphan/(?P.+)", + Pattern: "revoke-orphan/" + framework.GenericNameRegex("token"), Fields: map[string]*framework.FieldSchema{ "token": &framework.FieldSchema{ @@ -233,7 +233,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, &framework.Path{ - Pattern: "revoke-prefix/(?P.+)", + Pattern: "revoke-prefix/" + framework.GenericNameRegex("prefix"), Fields: map[string]*framework.FieldSchema{ "prefix": &framework.FieldSchema{ @@ -273,7 +273,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, &framework.Path{ - Pattern: "renew/(?P.+)", + Pattern: "renew/" + framework.GenericNameRegex("token"), Fields: map[string]*framework.FieldSchema{ "token": &framework.FieldSchema{ diff --git a/vault/token_store_test.go b/vault/token_store_test.go index e4d8eb7e42de1..083aabf7ddced 100644 --- a/vault/token_store_test.go +++ b/vault/token_store_test.go @@ -90,8 +90,7 @@ func TestTokenStore_HandleRequest_RevokeAccessor(t *testing.T) { t.Fatalf("err: %s", err) } - req := logical.TestRequest(t, logical.UpdateOperation, "revoke-accessor") - req.Data["accessor"] = out.Accessor + req := logical.TestRequest(t, logical.UpdateOperation, "revoke-accessor/"+out.Accessor) _, err = ts.HandleRequest(req) if err != nil { diff --git a/website/source/docs/auth/token.html.md b/website/source/docs/auth/token.html.md index 2fabfbf3541bb..1a1a0d0a23a7f 100644 --- a/website/source/docs/auth/token.html.md +++ b/website/source/docs/auth/token.html.md @@ -467,7 +467,7 @@ of the header should be "X-Vault-Token" and the value should be the token. -### /auth/token/revoke-accessor +### /auth/token/revoke-accessor/ #### POST
@@ -482,17 +482,11 @@ of the header should be "X-Vault-Token" and the value should be the token.
POST
URL
-
`/auth/token/revoke-accessor`
+
`/auth/token/revoke-accessor/`
Parameters
-
    -
  • - accessor - required - Accessor of the token. -
  • -
+ None
Returns
From d1d37d5933dddda1352a4ca455a6b044cf1b25f7 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Wed, 9 Mar 2016 13:45:36 -0500 Subject: [PATCH 18/20] fix all the broken tests --- http/logical_test.go | 2 ++ http/sys_generate_root_test.go | 2 ++ vault/core_test.go | 3 +++ vault/token_store.go | 3 ++- vault/token_store_test.go | 6 ++++++ 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/http/logical_test.go b/http/logical_test.go index de124f7a78180..25d2472ebc499 100644 --- a/http/logical_test.go +++ b/http/logical_test.go @@ -140,6 +140,7 @@ func TestLogical_StandbyRedirect(t *testing.T) { testResponseBody(t, resp, &actual) actualDataMap := actual["data"].(map[string]interface{}) delete(actualDataMap, "creation_time") + delete(actualDataMap, "accessor") actual["data"] = actualDataMap delete(actual, "lease_id") if !reflect.DeepEqual(actual, expected) { @@ -180,6 +181,7 @@ func TestLogical_CreateToken(t *testing.T) { testResponseStatus(t, resp, 200) testResponseBody(t, resp, &actual) delete(actual["auth"].(map[string]interface{}), "client_token") + delete(actual["auth"].(map[string]interface{}), "accessor") if !reflect.DeepEqual(actual, expected) { t.Fatalf("bad:\nexpected:\n%#v\nactual:\n%#v", expected, actual) } diff --git a/http/sys_generate_root_test.go b/http/sys_generate_root_test.go index 865c74857a833..fd9f42d88bac7 100644 --- a/http/sys_generate_root_test.go +++ b/http/sys_generate_root_test.go @@ -309,6 +309,7 @@ func TestSysGenerateRoot_Update_OTP(t *testing.T) { testResponseBody(t, resp, &actual) expected["creation_time"] = actual["data"].(map[string]interface{})["creation_time"] + expected["accessor"] = actual["data"].(map[string]interface{})["accessor"] if !reflect.DeepEqual(actual["data"], expected) { t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual["data"]) @@ -389,6 +390,7 @@ func TestSysGenerateRoot_Update_PGP(t *testing.T) { testResponseBody(t, resp, &actual) expected["creation_time"] = actual["data"].(map[string]interface{})["creation_time"] + expected["accessor"] = actual["data"].(map[string]interface{})["accessor"] if !reflect.DeepEqual(actual["data"], expected) { t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual["data"]) diff --git a/vault/core_test.go b/vault/core_test.go index ec545857368e3..0d28eab0c049b 100644 --- a/vault/core_test.go +++ b/vault/core_test.go @@ -787,6 +787,7 @@ func TestCore_HandleLogin_Token(t *testing.T) { } expect := &TokenEntry{ ID: clientToken, + Accessor: te.Accessor, Parent: "", Policies: []string{"foo", "bar", "default"}, Path: "auth/foo/login", @@ -986,6 +987,7 @@ func TestCore_HandleRequest_CreateToken_Lease(t *testing.T) { } expect := &TokenEntry{ ID: clientToken, + Accessor: te.Accessor, Parent: root, Policies: []string{"default", "foo"}, Path: "auth/token/create", @@ -1030,6 +1032,7 @@ func TestCore_HandleRequest_CreateToken_NoDefaultPolicy(t *testing.T) { } expect := &TokenEntry{ ID: clientToken, + Accessor: te.Accessor, Parent: root, Policies: []string{"foo"}, Path: "auth/token/create", diff --git a/vault/token_store.go b/vault/token_store.go index 13051c09be27d..328d6c27aa1fd 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -233,7 +233,8 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, &framework.Path{ - Pattern: "revoke-prefix/" + framework.GenericNameRegex("prefix"), + // Do not use framework.GenericNameRegex as prefix may contain forward slashes + Pattern: "revoke-prefix/(?P.+)", Fields: map[string]*framework.FieldSchema{ "prefix": &framework.FieldSchema{ diff --git a/vault/token_store_test.go b/vault/token_store_test.go index 083aabf7ddced..e2500873be63c 100644 --- a/vault/token_store_test.go +++ b/vault/token_store_test.go @@ -471,6 +471,7 @@ func TestTokenStore_HandleRequest_CreateToken_DisplayName(t *testing.T) { expected := &TokenEntry{ ID: resp.Auth.ClientToken, + Accessor: resp.Auth.Accessor, Parent: root, Policies: []string{"root"}, Path: "auth/token/create", @@ -501,6 +502,7 @@ func TestTokenStore_HandleRequest_CreateToken_NumUses(t *testing.T) { expected := &TokenEntry{ ID: resp.Auth.ClientToken, + Accessor: resp.Auth.Accessor, Parent: root, Policies: []string{"root"}, Path: "auth/token/create", @@ -564,6 +566,7 @@ func TestTokenStore_HandleRequest_CreateToken_NoPolicy(t *testing.T) { expected := &TokenEntry{ ID: resp.Auth.ClientToken, + Accessor: resp.Auth.Accessor, Parent: root, Policies: []string{"root"}, Path: "auth/token/create", @@ -920,6 +923,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { exp := map[string]interface{}{ "id": root, + "accessor": resp.Data["accessor"].(string), "policies": []string{"root"}, "path": "auth/token/root", "meta": map[string]string(nil), @@ -952,6 +956,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { exp = map[string]interface{}{ "id": "client", + "accessor": resp.Data["accessor"], "policies": []string{"default", "foo"}, "path": "auth/token/create", "meta": map[string]string(nil), @@ -1054,6 +1059,7 @@ func TestTokenStore_HandleRequest_LookupSelf(t *testing.T) { exp := map[string]interface{}{ "id": root, + "accessor": resp.Data["accessor"], "policies": []string{"root"}, "path": "auth/token/root", "meta": map[string]string(nil), From 64bc542c8fe9d900585b9b09d506a15ff21d09d7 Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Wed, 9 Mar 2016 14:08:52 -0500 Subject: [PATCH 19/20] Restore old regex expressions for token endpoints --- vault/token_store.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/vault/token_store.go b/vault/token_store.go index 328d6c27aa1fd..21824f2235308 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -114,7 +114,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, &framework.Path{ - Pattern: "lookup/" + framework.GenericNameRegex("token"), + Pattern: "lookup/(?P.+)", Fields: map[string]*framework.FieldSchema{ "token": &framework.FieldSchema{ @@ -168,7 +168,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, &framework.Path{ - Pattern: "revoke-accessor/" + framework.GenericNameRegex("accessor"), + Pattern: "revoke-accessor/(?P.+)", Fields: map[string]*framework.FieldSchema{ "accessor": &framework.FieldSchema{ @@ -197,7 +197,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, &framework.Path{ - Pattern: "revoke/" + framework.GenericNameRegex("token"), + Pattern: "revoke/(?P.+)", Fields: map[string]*framework.FieldSchema{ "token": &framework.FieldSchema{ @@ -215,7 +215,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, &framework.Path{ - Pattern: "revoke-orphan/" + framework.GenericNameRegex("token"), + Pattern: "revoke-orphan/(?P.+)", Fields: map[string]*framework.FieldSchema{ "token": &framework.FieldSchema{ @@ -233,7 +233,6 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, &framework.Path{ - // Do not use framework.GenericNameRegex as prefix may contain forward slashes Pattern: "revoke-prefix/(?P.+)", Fields: map[string]*framework.FieldSchema{ @@ -274,7 +273,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, &framework.Path{ - Pattern: "renew/" + framework.GenericNameRegex("token"), + Pattern: "renew/(?P.+)", Fields: map[string]*framework.FieldSchema{ "token": &framework.FieldSchema{ From b8bd534c532546290a30e589a82f20cae036ca8a Mon Sep 17 00:00:00 2001 From: vishalnayak Date: Wed, 9 Mar 2016 14:54:52 -0500 Subject: [PATCH 20/20] In-URL accessor for auth/token/lookup-accessor endpoint --- vault/token_store.go | 2 +- vault/token_store_test.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/vault/token_store.go b/vault/token_store.go index 21824f2235308..c39a608c9f4dd 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -132,7 +132,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) }, &framework.Path{ - Pattern: "lookup-accessor$", + Pattern: "lookup-accessor/(?P.+)", Fields: map[string]*framework.FieldSchema{ "accessor": &framework.FieldSchema{ diff --git a/vault/token_store_test.go b/vault/token_store_test.go index e2500873be63c..00555de34a636 100644 --- a/vault/token_store_test.go +++ b/vault/token_store_test.go @@ -58,8 +58,7 @@ func TestTokenStore_HandleRequest_LookupAccessor(t *testing.T) { t.Fatalf("err: %s", err) } - req := logical.TestRequest(t, logical.UpdateOperation, "lookup-accessor") - req.Data["accessor"] = out.Accessor + req := logical.TestRequest(t, logical.UpdateOperation, "lookup-accessor/"+out.Accessor) resp, err := ts.HandleRequest(req) if err != nil {