-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Revoke multiple tokens/accessors #2922
Changes from all commits
6d36c84
57f22d3
22811a9
bb88dae
fc1c616
4d33f2e
6fa7596
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -339,8 +339,8 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) | |
Description: "Accessor 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)", | ||
}, | ||
}, | ||
|
||
|
@@ -372,8 +372,8 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) | |
Description: "Token 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)", | ||
}, | ||
}, | ||
|
||
|
@@ -1039,6 +1039,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 { | ||
|
@@ -1325,29 +1339,69 @@ 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 = []string{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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See comments below about using |
||
tokens := make([]string, len(accessors)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This list may not be the length of the accessor list in the case of errors. I would just set it to length zero and append to the slice. |
||
|
||
for idx, accessor := range accessors { | ||
aEntry, err := ts.lookupByAccessor(accessor, true) | ||
if err != nil { | ||
if len(accessors) == 1 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be fine to let this flow through to the error handling blocks. |
||
// backward compatibility with 0.7.3 | ||
return nil, err | ||
} | ||
errs[idx] = err | ||
tokens[idx] = "" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You will need to |
||
} | ||
|
||
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{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While I'm not sure what this should be, I'm sure that it should not be a string map. Possibly the return value should be a slice of the same size as the input with either nulls or error messages. There's no need to return the accessors if the ordering is the same. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I am also not sure about this part, if number of revoked accessors/tokens are small, thats fine, if we are going to revoke millions of tokens at once, response may contain huge unnecessary data millions of nulls or empty strings
How user will determine which accessors are failed, ordering is same, but not all accessors may fail? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When something returns multiple errors, we normally use hashicorp/go-multierror and I think it would work in this case. I think it would remove a lot of the backwards compatibility logic since it would just contain one error in the case of a single accessor. |
||
"accessor": accessors[idx], | ||
"error": errs[idx].Error(), | ||
}) | ||
} | ||
} | ||
|
||
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.`) | ||
} | ||
|
||
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 | ||
|
@@ -1793,24 +1847,48 @@ 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 = []string{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 | ||
revokeErrors := ts.RevokeTrees(tokens) | ||
|
||
response := &logical.Response{} | ||
failedRevokes := make([]map[string]string, 0, len(revokeErrors)) | ||
|
||
for idx, revokeError := range revokeErrors { | ||
if revokeError != nil { | ||
failedRevokes = append(failedRevokes, map[string]string{ | ||
"token": tokens[idx], | ||
"error": revokeError.Error(), | ||
}) | ||
} | ||
} | ||
|
||
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.`) | ||
} | ||
|
||
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 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure we need this method on the response struct. I would much rather formatting of the errors being handled by the caller instead of trying to generalize it here.