From 6d36c84ed7cfc76b40addebd792206adc2a8e1ab Mon Sep 17 00:00:00 2001 From: Ikhtiyor Ahmedov Date: Fri, 23 Jun 2017 22:10:36 +0500 Subject: [PATCH 1/6] revoke multiple accessor/tokens in HTTP API --- logical/response.go | 26 +++++++- vault/token_store.go | 137 +++++++++++++++++++++++++++++--------- vault/token_store_test.go | 118 ++++++++++++++++++++++++++++++++ 3 files changed, 247 insertions(+), 34 deletions(-) diff --git a/logical/response.go b/logical/response.go index 6ee452b6865b..1e392624d065 100644 --- a/logical/response.go +++ b/logical/response.go @@ -65,7 +65,7 @@ func (r *Response) AddWarning(warning string) { // IsError returns true if this response seems to indicate an error. func (r *Response) IsError() bool { - return r != nil && r.Data != nil && len(r.Data) == 1 && r.Data["error"] != nil + return r != nil && r.Data != nil && len(r.Data) > 0 && r.Data["error"] != nil } func (r *Response) Error() error { @@ -81,6 +81,30 @@ func (r *Response) Error() error { return nil } +func (r *Response) SetError(err error, errorData interface{}) { + if r.Data == nil { + r.Data = map[string]interface{}{ + "error_data": errorData, + "error": "contains failed revokes", + } + } else { + r.Data["error_data"] = errorData + r.Data["error"] = err + } +} + +func (r *Response) ErrorData() interface{} { + if r.Data == nil { + return nil + } + + if data, ok := r.Data["error_data"]; ok { + return data + } + + return nil +} + // HelpResponse is used to format a help response func HelpResponse(text string, seeAlso []string) *Response { return &Response{ diff --git a/vault/token_store.go b/vault/token_store.go index 34d2b692ab7a..1018fe59fee8 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -335,12 +335,12 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) Fields: map[string]*framework.FieldSchema{ "urlaccessor": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Accessor of the token (URL parameter)", + Type: framework.TypeCommaStringSlice, + Description: "Accessor(s) of the token (URL parameter)", }, "accessor": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Accessor of the token (request body)", + Type: framework.TypeCommaStringSlice, + Description: "Accessor(s) of the token (request body)", }, }, @@ -368,12 +368,12 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) Fields: map[string]*framework.FieldSchema{ "urltoken": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Token to revoke (URL parameter)", + Type: framework.TypeCommaStringSlice, + Description: "Token(s) to revoke (URL parameter)", }, "token": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Token to revoke (request body)", + Type: framework.TypeCommaStringSlice, + Description: "Token(s) to revoke (request body)", }, }, @@ -1033,6 +1033,20 @@ func (ts *TokenStore) RevokeTree(id string) error { return nil } +// RevokeTrees is used to invalide multiple tokens and all +// child tokens. +func (ts *TokenStore) RevokeTrees(ids []string) []error { + defer metrics.MeasureSince([]string{"token", "revoke-trees"}, time.Now()) + + errs := make([]error, len(ids)) + + for idx, id := range ids { + errs[idx] = ts.RevokeTree(id) + } + + return errs +} + // revokeTreeSalted is used to invalide a given token and all // child tokens using a saltedID. func (ts *TokenStore) revokeTreeSalted(saltedId string) error { @@ -1319,32 +1333,58 @@ func (ts *TokenStore) handleUpdateLookupAccessor(req *logical.Request, data *fra // the token associated with the accessor func (ts *TokenStore) handleUpdateRevokeAccessor(req *logical.Request, data *framework.FieldData) (*logical.Response, error) { var urlaccessor bool - accessor := data.Get("accessor").(string) - if accessor == "" { - accessor = data.Get("urlaccessor").(string) - if accessor == "" { + accessors := data.Get("accessor").([]string) + + if len(accessors) == 0 { + accessors = data.Get("urlaccessor").([]string) + if len(accessors) == 0 { return nil, &logical.StatusBadRequest{Err: "missing accessor"} } urlaccessor = true } - aEntry, err := ts.lookupByAccessor(accessor, true) - if err != nil { - return nil, err + errs := make([]error, len(accessors)) + tokens := make([]string, len(accessors)) + + for idx, accessor := range accessors { + aEntry, err := ts.lookupByAccessor(accessor, true) + if err != nil { + errs[idx] = err + tokens[idx] = "" + } + + tokens[idx] = aEntry.TokenID } - // Revoke the token and its children - if err := ts.RevokeTree(aEntry.TokenID); err != nil { - return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest + revokeErrors := ts.RevokeTrees(tokens) + + response := &logical.Response{} + failedRevokes := make([]map[string]string, 0, len(revokeErrors)) + + for idx, revokeError := range revokeErrors { + if errs[idx] == nil { + errs[idx] = revokeError + } + + if errs[idx] != nil { + failedRevokes = append(failedRevokes, map[string]string{ + "accessor": accessors[idx], + "error": errs[idx].Error(), + }) + } + } + + if len(failedRevokes) > 0 { + response.SetError(fmt.Errorf("contains failed revokes"), failedRevokes) } if urlaccessor { - resp := &logical.Response{} - resp.AddWarning(`Using an accessor in the path is unsafe as the accessor can be logged in many places. Please use POST or PUT with the accessor passed in via the "accessor" parameter.`) - return resp, nil + response.AddWarning(`Using an accessor in the path is unsafe as the accessor can be logged in many places. Please use POST or PUT with the accessor passed in via the "accessor" parameter.`) + } else if len(failedRevokes) == 0 { + return nil, nil } - return nil, nil + return response, nil } // handleCreate handles the auth/token/create path for creation of new orphan @@ -1787,27 +1827,58 @@ func (ts *TokenStore) handleRevokeSelf( func (ts *TokenStore) handleRevokeTree( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { var urltoken bool - id := data.Get("token").(string) - if id == "" { - id = data.Get("urltoken").(string) - if id == "" { + tokens := data.Get("token").([]string) + + if len(tokens) == 0 { + tokens = data.Get("urltoken").([]string) + if len(tokens) == 0 { return logical.ErrorResponse("missing token ID"), logical.ErrInvalidRequest } urltoken = true } - // Revoke the token and its children - if err := ts.RevokeTree(id); err != nil { - return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest + errs := make([]error, len(tokens)) + for idx, token := range tokens { + entry, err := ts.Lookup(token) + if err != nil { + errs[idx] = err + continue + } + + if entry == nil { + errs[idx] = fmt.Errorf("invalid token") + } + } + + revokeErrors := ts.RevokeTrees(tokens) + + response := &logical.Response{} + failedRevokes := make([]map[string]string, 0, len(revokeErrors)) + + for idx, revokeError := range revokeErrors { + if errs[idx] == nil { + errs[idx] = revokeError + } + + if errs[idx] != nil { + failedRevokes = append(failedRevokes, map[string]string{ + "token": tokens[idx], + "error": errs[idx].Error(), + }) + } + } + + if len(failedRevokes) > 0 { + response.SetError(fmt.Errorf("contains failed revokes"), failedRevokes) } if urltoken { - resp := &logical.Response{} - resp.AddWarning(`Using a token in the path is unsafe as the token can be logged in many places. Please use POST or PUT with the token passed in via the "token" parameter.`) - return resp, nil + response.AddWarning(`Using a token in the path is unsafe as the token can be logged in many places. Please use POST or PUT with the token passed in via the "token" parameter.`) + } else if len(failedRevokes) == 0 { + return nil, nil } - return nil, nil + return response, nil } // handleRevokeOrphan handles the auth/token/revoke-orphan/id path for revocation of tokens diff --git a/vault/token_store_test.go b/vault/token_store_test.go index 765487eb40f6..aee89b7f810e 100644 --- a/vault/token_store_test.go +++ b/vault/token_store_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/hashicorp/go-uuid" + "github.com/hashicorp/vault/helper/strutil" "github.com/hashicorp/vault/logical" ) @@ -393,6 +394,62 @@ func TestTokenStore_HandleRequest_RevokeAccessor(t *testing.T) { } } +func TestTokenStore_HandleRequest_RevokeAccessors_Multiple(t *testing.T) { + _, ts, _, root := TestCoreWithTokenStore(t) + tokenIds := []string{"tokenid1", "tokenid2", "tokenid3"} + accessors := make([]string, len(tokenIds)) + + for idx, token := range tokenIds { + testMakeToken(t, ts, root, token, "", []string{"foo"}) + out, err := ts.Lookup(token) + if err != nil { + t.Fatalf("err: %s", err) + } + if out == nil { + t.Fatalf("err: %s", err) + } + + accessors[idx] = out.Accessor + } + + req := logical.TestRequest(t, logical.UpdateOperation, "revoke-accessor") + req.Data = map[string]interface{}{ + "accessor": accessors, + } + + _, err := ts.HandleRequest(req) + if err != nil { + t.Fatalf("err: %s", err) + } + + for _, token := range tokenIds { + out, err := ts.Lookup(token) + if err != nil { + t.Fatalf("err: %s", err) + } + if out != nil { + t.Fatalf("err: %s", err) + } + } + + // revoke again + resp, err := ts.HandleRequest(req) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !resp.IsError() { + t.Fatalf("response should have an error, but no error found") + } + + errorData := resp.ErrorData().([]map[string]string) + for _, errorInfo := range errorData { + if !strutil.StrListContains(accessors, errorInfo["accessor"]) { + t.Fatalf("expected: accessor fail when revoking (%s)", errorInfo["accessor"]) + } + } +} + func TestTokenStore_RootToken(t *testing.T) { _, ts, _, _ := TestCoreWithTokenStore(t) @@ -1264,6 +1321,67 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) { } } +func TestTokenStore_HandleRequest_Revoke_Multiple(t *testing.T) { + _, ts, _, root := TestCoreWithTokenStore(t) + tokens := []string{"token1", "token2"} + tokenChilds := []string{"token1-sub-child", "token2-sub-child"} + + for idx, tokenStr := range tokens { + testMakeToken(t, ts, root, tokenStr, "", []string{"root", "foo"}) + testMakeToken(t, ts, tokenStr, tokenChilds[idx], "", []string{"foo"}) + } + + tokenListStr := strings.Join(tokens, ",") + + req := logical.TestRequest(t, logical.UpdateOperation, "revoke") + req.Data = map[string]interface{}{ + "token": tokenListStr, + } + resp, err := ts.HandleRequest(req) + if err != nil { + t.Fatalf("err: %v %v", err, resp) + } + if resp != nil { + t.Fatalf("bad: %#v", resp) + } + + for idx, tokenStr := range tokens { + out, err := ts.Lookup(tokenStr) + if err != nil { + t.Fatalf("err: %v", err) + } + if out != nil { + t.Fatalf("bad: %v", out) + } + + // Sub-child should not exist + out, err = ts.Lookup(tokenChilds[idx]) + if err != nil { + t.Fatalf("err: %v", err) + } + if out != nil { + t.Fatalf("bad: %v", out) + } + } + + // revoke again + resp, err = ts.HandleRequest(req) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !resp.IsError() { + t.Fatalf("response should have an error, but no error found") + } + + errorData := resp.ErrorData().([]map[string]string) + for _, errorInfo := range errorData { + if !strutil.StrListContains(tokens, errorInfo["token"]) { + t.Fatalf("expected: token fail when revoking (%s)", errorInfo["token"]) + } + } +} + func TestTokenStore_HandleRequest_RevokeOrphan(t *testing.T) { _, ts, _, root := TestCoreWithTokenStore(t) testMakeToken(t, ts, root, "child", "", []string{"root", "foo"}) From 57f22d3798af2c4dc72a35d977e9af65bf02ae12 Mon Sep 17 00:00:00 2001 From: Ikhtiyor Ahmedov Date: Sat, 24 Jun 2017 14:10:09 +0500 Subject: [PATCH 2/6] return failed revokes in error --- logical/response.go | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/logical/response.go b/logical/response.go index 1e392624d065..dae43b5ad78b 100644 --- a/logical/response.go +++ b/logical/response.go @@ -2,6 +2,8 @@ package logical import ( "errors" + "fmt" + "strings" "github.com/hashicorp/vault/helper/wrapping" ) @@ -82,14 +84,34 @@ func (r *Response) Error() error { } func (r *Response) SetError(err error, errorData interface{}) { + var additionalErrorText, errText string = "", "" + switch m := errorData.(type) { + case []map[string]string: + items := make([]string, len(m)) + for idx, errItem := range m { + errItemFields := make([]string, 0, len(errItem)) + for k, v := range errItem { + errItemFields = append(errItemFields, fmt.Sprintf("%s::%s", k, v)) + } + items[idx] = fmt.Sprintf("(%s)", strings.Join(errItemFields, ",")) + } + additionalErrorText = strings.Join(items, ";") + } + + if len(additionalErrorText) != 0 { + errText = fmt.Sprintf("%s:%s", err.Error(), additionalErrorText) + } else { + errText = err.Error() + } + if r.Data == nil { r.Data = map[string]interface{}{ "error_data": errorData, - "error": "contains failed revokes", + "error": errText, } } else { r.Data["error_data"] = errorData - r.Data["error"] = err + r.Data["error"] = errText } } From bb88daef1a6af44ebe9bb6c1854f11df5eb8658a Mon Sep 17 00:00:00 2001 From: Ikhtiyor Ahmedov Date: Sat, 24 Jun 2017 15:01:32 +0500 Subject: [PATCH 3/6] exclude failed tokens --- logical/response.go | 6 +++--- vault/token_store.go | 21 ++------------------- vault/token_store_test.go | 25 ++++++------------------- 3 files changed, 11 insertions(+), 41 deletions(-) diff --git a/logical/response.go b/logical/response.go index dae43b5ad78b..07dc51e97da3 100644 --- a/logical/response.go +++ b/logical/response.go @@ -91,15 +91,15 @@ func (r *Response) SetError(err error, errorData interface{}) { for idx, errItem := range m { errItemFields := make([]string, 0, len(errItem)) for k, v := range errItem { - errItemFields = append(errItemFields, fmt.Sprintf("%s::%s", k, v)) + errItemFields = append(errItemFields, fmt.Sprintf("%s=%s", k, v)) } items[idx] = fmt.Sprintf("(%s)", strings.Join(errItemFields, ",")) } - additionalErrorText = strings.Join(items, ";") + additionalErrorText = strings.Join(items, "\n") } if len(additionalErrorText) != 0 { - errText = fmt.Sprintf("%s:%s", err.Error(), additionalErrorText) + errText = fmt.Sprintf("%s\n%s", err.Error(), additionalErrorText) } else { errText = err.Error() } diff --git a/vault/token_store.go b/vault/token_store.go index 1d4c6e2df3cc..8964225c329f 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -1843,33 +1843,16 @@ func (ts *TokenStore) handleRevokeTree( urltoken = true } - errs := make([]error, len(tokens)) - for idx, token := range tokens { - entry, err := ts.Lookup(token) - if err != nil { - errs[idx] = err - continue - } - - if entry == nil { - errs[idx] = fmt.Errorf("invalid token") - } - } - revokeErrors := ts.RevokeTrees(tokens) response := &logical.Response{} failedRevokes := make([]map[string]string, 0, len(revokeErrors)) for idx, revokeError := range revokeErrors { - if errs[idx] == nil { - errs[idx] = revokeError - } - - if errs[idx] != nil { + if revokeError != nil { failedRevokes = append(failedRevokes, map[string]string{ "token": tokens[idx], - "error": errs[idx].Error(), + "error": revokeError.Error(), }) } } diff --git a/vault/token_store_test.go b/vault/token_store_test.go index 738c49de305b..062443f6505c 100644 --- a/vault/token_store_test.go +++ b/vault/token_store_test.go @@ -1334,7 +1334,10 @@ func TestTokenStore_HandleRequest_Revoke_Multiple(t *testing.T) { testMakeToken(t, ts, tokenStr, tokenChilds[idx], "", []string{"foo"}) } - tokenListStr := strings.Join(tokens, ",") + tokensToRevoke := make([]string, 0, len(tokens)+1) + tokensToRevoke = append(tokens, tokenChilds[0]) + + tokenListStr := strings.Join(tokensToRevoke, ",") req := logical.TestRequest(t, logical.UpdateOperation, "revoke") req.Data = map[string]interface{}{ @@ -1348,7 +1351,8 @@ func TestTokenStore_HandleRequest_Revoke_Multiple(t *testing.T) { t.Fatalf("bad: %#v", resp) } - for idx, tokenStr := range tokens { + // exclude last token, because it doesn't have a child token + for idx, tokenStr := range tokensToRevoke[:len(tokens)] { out, err := ts.Lookup(tokenStr) if err != nil { t.Fatalf("err: %v", err) @@ -1366,23 +1370,6 @@ func TestTokenStore_HandleRequest_Revoke_Multiple(t *testing.T) { t.Fatalf("bad: %v", out) } } - - // revoke again - resp, err = ts.HandleRequest(req) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !resp.IsError() { - t.Fatalf("response should have an error, but no error found") - } - - errorData := resp.ErrorData().([]map[string]string) - for _, errorInfo := range errorData { - if !strutil.StrListContains(tokens, errorInfo["token"]) { - t.Fatalf("expected: token fail when revoking (%s)", errorInfo["token"]) - } - } } func TestTokenStore_HandleRequest_RevokeOrphan(t *testing.T) { From fc1c6168ee9f66d0dedc36166354d533935a3b9f Mon Sep 17 00:00:00 2001 From: Ikhtiyor Ahmedov Date: Mon, 26 Jun 2017 15:25:33 +0500 Subject: [PATCH 4/6] fix test fails, old version rely on len(r.Data) == 1 --- logical/response.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/logical/response.go b/logical/response.go index 07dc51e97da3..ff1d7047ed69 100644 --- a/logical/response.go +++ b/logical/response.go @@ -67,7 +67,7 @@ func (r *Response) AddWarning(warning string) { // IsError returns true if this response seems to indicate an error. func (r *Response) IsError() bool { - return r != nil && r.Data != nil && len(r.Data) > 0 && r.Data["error"] != nil + return r != nil && r.Data != nil && len(r.Data) == 1 && r.Data["error"] != nil } func (r *Response) Error() error { @@ -106,11 +106,9 @@ func (r *Response) SetError(err error, errorData interface{}) { if r.Data == nil { r.Data = map[string]interface{}{ - "error_data": errorData, - "error": errText, + "error": errText, } } else { - r.Data["error_data"] = errorData r.Data["error"] = errText } } From 4d33f2e7dc11d5fbd499798705560fedb0523ff8 Mon Sep 17 00:00:00 2001 From: Ikhtiyor Ahmedov Date: Mon, 26 Jun 2017 15:43:40 +0500 Subject: [PATCH 5/6] fix crash in tests and remove unused function from logical.Response --- logical/response.go | 14 +------------- vault/token_store_test.go | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/logical/response.go b/logical/response.go index ff1d7047ed69..9d4d781a3983 100644 --- a/logical/response.go +++ b/logical/response.go @@ -93,7 +93,7 @@ func (r *Response) SetError(err error, errorData interface{}) { for k, v := range errItem { errItemFields = append(errItemFields, fmt.Sprintf("%s=%s", k, v)) } - items[idx] = fmt.Sprintf("(%s)", strings.Join(errItemFields, ",")) + items[idx] = strings.Join(errItemFields, ",") } additionalErrorText = strings.Join(items, "\n") } @@ -113,18 +113,6 @@ func (r *Response) SetError(err error, errorData interface{}) { } } -func (r *Response) ErrorData() interface{} { - if r.Data == nil { - return nil - } - - if data, ok := r.Data["error_data"]; ok { - return data - } - - return nil -} - // HelpResponse is used to format a help response func HelpResponse(text string, seeAlso []string) *Response { return &Response{ diff --git a/vault/token_store_test.go b/vault/token_store_test.go index 062443f6505c..4fdcf3f24b97 100644 --- a/vault/token_store_test.go +++ b/vault/token_store_test.go @@ -442,10 +442,18 @@ func TestTokenStore_HandleRequest_RevokeAccessors_Multiple(t *testing.T) { t.Fatalf("response should have an error, but no error found") } - errorData := resp.ErrorData().([]map[string]string) - for _, errorInfo := range errorData { - if !strutil.StrListContains(accessors, errorInfo["accessor"]) { - t.Fatalf("expected: accessor fail when revoking (%s)", errorInfo["accessor"]) + errorLines := strings.Split(resp.Error().Error(), "\n") + if len(errorLines) < 2 { + t.Fatalf("expected list of failed revokes") + } + + for _, line := range errorLines[1:] { + fields := strings.Split(line, ",") + for _, value := range fields { + pair := strings.Split(value, "=") + if pair[0] == "accessor" && !strutil.StrListContains(accessors, pair[1]) { + t.Fatalf("expected: accessor fail when revoking (%s)", pair[1]) + } } } } From 6fa759654dba3e7c5d5f448810c6674dd6245227 Mon Sep 17 00:00:00 2001 From: Ikhtiyor Ahmedov Date: Sat, 15 Jul 2017 22:19:25 +0500 Subject: [PATCH 6/6] removed multiple accessor/token from url, backward compatible response while revoking --- vault/token_store.go | 64 ++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/vault/token_store.go b/vault/token_store.go index 8964225c329f..7f7f0eb3a2e3 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -335,8 +335,8 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) Fields: map[string]*framework.FieldSchema{ "urlaccessor": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: "Accessor(s) of the token (URL parameter)", + Type: framework.TypeString, + Description: "Accessor of the token (URL parameter)", }, "accessor": &framework.FieldSchema{ Type: framework.TypeCommaStringSlice, @@ -368,8 +368,8 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) Fields: map[string]*framework.FieldSchema{ "urltoken": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: "Token(s) to revoke (URL parameter)", + Type: framework.TypeString, + Description: "Token to revoke (URL parameter)", }, "token": &framework.FieldSchema{ Type: framework.TypeCommaStringSlice, @@ -1342,7 +1342,7 @@ func (ts *TokenStore) handleUpdateRevokeAccessor(req *logical.Request, data *fra accessors := data.Get("accessor").([]string) if len(accessors) == 0 { - accessors = data.Get("urlaccessor").([]string) + accessors = []string{data.Get("urlaccessor").(string)} if len(accessors) == 0 { return nil, &logical.StatusBadRequest{Err: "missing accessor"} } @@ -1355,6 +1355,10 @@ func (ts *TokenStore) handleUpdateRevokeAccessor(req *logical.Request, data *fra for idx, accessor := range accessors { aEntry, err := ts.lookupByAccessor(accessor, true) if err != nil { + if len(accessors) == 1 { + // backward compatibility with 0.7.3 + return nil, err + } errs[idx] = err tokens[idx] = "" } @@ -1380,17 +1384,27 @@ func (ts *TokenStore) handleUpdateRevokeAccessor(req *logical.Request, data *fra } } - if len(failedRevokes) > 0 { - response.SetError(fmt.Errorf("contains failed revokes"), failedRevokes) - } - if urlaccessor { response.AddWarning(`Using an accessor in the path is unsafe as the accessor can be logged in many places. Please use POST or PUT with the accessor passed in via the "accessor" parameter.`) - } else if len(failedRevokes) == 0 { - return nil, nil } - return response, nil + if len(accessors) == 1 { + // backward compatibility with 0.7.3 + if len(failedRevokes) == 1 { + return logical.ErrorResponse(errs[0].Error()), logical.ErrInvalidRequest + } else if urlaccessor { + return response, nil + } else { + return nil, nil + } + } + + if len(failedRevokes) > 0 { + response.SetError(fmt.Errorf("contains failed revokes"), failedRevokes) + return response, nil + } + + return nil, nil } // handleCreate handles the auth/token/create path for creation of new orphan @@ -1836,7 +1850,7 @@ func (ts *TokenStore) handleRevokeTree( tokens := data.Get("token").([]string) if len(tokens) == 0 { - tokens = data.Get("urltoken").([]string) + tokens = []string{data.Get("urltoken").(string)} if len(tokens) == 0 { return logical.ErrorResponse("missing token ID"), logical.ErrInvalidRequest } @@ -1857,17 +1871,27 @@ func (ts *TokenStore) handleRevokeTree( } } - if len(failedRevokes) > 0 { - response.SetError(fmt.Errorf("contains failed revokes"), failedRevokes) - } - if urltoken { response.AddWarning(`Using a token in the path is unsafe as the token can be logged in many places. Please use POST or PUT with the token passed in via the "token" parameter.`) - } else if len(failedRevokes) == 0 { - return nil, nil } - return response, nil + if len(tokens) == 1 { + // backward compatibility with 0.7.3 + if len(failedRevokes) == 1 { + return logical.ErrorResponse(revokeErrors[0].Error()), logical.ErrInvalidRequest + } else if urltoken { + return response, nil + } else { + return nil, nil + } + } + + if len(failedRevokes) > 0 { + response.SetError(fmt.Errorf("contains failed revokes"), failedRevokes) + return response, nil + } + + return nil, nil } // handleRevokeOrphan handles the auth/token/revoke-orphan/id path for revocation of tokens