token: disallow periods on custom token IDs (#8646)
* token: disallow periods on custom token IDs * docs: update token API docs
This commit is contained in:
parent
1dbc6d3dd0
commit
ec8448ab56
|
@ -817,8 +817,13 @@ func (ts *TokenStore) create(ctx context.Context, entry *logical.TokenEntry) err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if userSelectedID && strings.HasPrefix(entry.ID, "s.") {
|
if userSelectedID {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(entry.ID, "s."):
|
||||||
return fmt.Errorf("custom token ID cannot have the 's.' prefix")
|
return fmt.Errorf("custom token ID cannot have the 's.' prefix")
|
||||||
|
case strings.Contains(entry.ID, "."):
|
||||||
|
return fmt.Errorf("custom token ID cannot have a '.' in the value")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !userSelectedID {
|
if !userSelectedID {
|
||||||
|
|
|
@ -231,57 +231,6 @@ func TestTokenStore_Salting(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenStore_ServiceTokenPrefix(t *testing.T) {
|
|
||||||
c, _, initToken := TestCoreUnsealed(t)
|
|
||||||
ts := c.tokenStore
|
|
||||||
|
|
||||||
// Ensure that a regular service token has a "s." prefix
|
|
||||||
resp, err := ts.HandleRequest(namespace.RootContext(nil), &logical.Request{
|
|
||||||
ClientToken: initToken,
|
|
||||||
Path: "create",
|
|
||||||
Operation: logical.UpdateOperation,
|
|
||||||
})
|
|
||||||
if err != nil || (resp != nil && resp.IsError()) {
|
|
||||||
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(resp.Auth.ClientToken, "s.") {
|
|
||||||
t.Fatalf("token %q does not have a 's.' prefix", resp.Auth.ClientToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that using a custon token ID results in a warning
|
|
||||||
resp, err = ts.HandleRequest(namespace.RootContext(nil), &logical.Request{
|
|
||||||
ClientToken: initToken,
|
|
||||||
Path: "create",
|
|
||||||
Operation: logical.UpdateOperation,
|
|
||||||
Data: map[string]interface{}{
|
|
||||||
"id": "foobar",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil || (resp != nil && resp.IsError()) {
|
|
||||||
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
|
|
||||||
}
|
|
||||||
expectedWarning := "Supplying a custom ID for the token uses the weaker SHA1 hashing instead of the more secure SHA2-256 HMAC for token obfuscation. SHA1 hashed tokens on the wire leads to less secure lookups."
|
|
||||||
if resp.Warnings[0] != expectedWarning {
|
|
||||||
t.Fatalf("expected warning not present")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that custom token ID having a "s." prefix fails
|
|
||||||
resp, err = ts.HandleRequest(namespace.RootContext(nil), &logical.Request{
|
|
||||||
ClientToken: initToken,
|
|
||||||
Path: "create",
|
|
||||||
Operation: logical.UpdateOperation,
|
|
||||||
Data: map[string]interface{}{
|
|
||||||
"id": "s.foobar",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected an error")
|
|
||||||
}
|
|
||||||
if resp.Error().Error() != "custom token ID cannot have the 's.' prefix" {
|
|
||||||
t.Fatalf("expected input error not present in error response")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type TokenEntryOld struct {
|
type TokenEntryOld struct {
|
||||||
ID string
|
ID string
|
||||||
Accessor string
|
Accessor string
|
||||||
|
@ -5633,3 +5582,87 @@ func TestTokenStore_Batch_NoCubbyhole(t *testing.T) {
|
||||||
t.Fatalf("bad: expected error, got %#v", *resp)
|
t.Fatalf("bad: expected error, got %#v", *resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTokenStore_TokenID(t *testing.T) {
|
||||||
|
t.Run("no custom ID provided", func(t *testing.T) {
|
||||||
|
c, _, initToken := TestCoreUnsealed(t)
|
||||||
|
ts := c.tokenStore
|
||||||
|
|
||||||
|
// Ensure that a regular service token has a "s." prefix
|
||||||
|
resp, err := ts.HandleRequest(namespace.RootContext(nil), &logical.Request{
|
||||||
|
ClientToken: initToken,
|
||||||
|
Path: "create",
|
||||||
|
Operation: logical.UpdateOperation,
|
||||||
|
})
|
||||||
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
|
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(resp.Auth.ClientToken, "s.") {
|
||||||
|
t.Fatalf("token %q does not have a 's.' prefix", resp.Auth.ClientToken)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("plain custom ID", func(t *testing.T) {
|
||||||
|
core, _, root := TestCoreUnsealed(t)
|
||||||
|
ts := core.tokenStore
|
||||||
|
|
||||||
|
resp, err := ts.HandleRequest(namespace.RootContext(nil), &logical.Request{
|
||||||
|
ClientToken: root,
|
||||||
|
Path: "create",
|
||||||
|
Operation: logical.UpdateOperation,
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"id": "foobar",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
|
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that using a custom token ID results in a warning
|
||||||
|
expectedWarning := "Supplying a custom ID for the token uses the weaker SHA1 hashing instead of the more secure SHA2-256 HMAC for token obfuscation. SHA1 hashed tokens on the wire leads to less secure lookups."
|
||||||
|
if resp.Warnings[0] != expectedWarning {
|
||||||
|
t.Fatalf("expected warning not present")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("service token prefix in custom ID", func(t *testing.T) {
|
||||||
|
c, _, initToken := TestCoreUnsealed(t)
|
||||||
|
ts := c.tokenStore
|
||||||
|
|
||||||
|
// Ensure that custom token ID having a "s." prefix fails
|
||||||
|
resp, err := ts.HandleRequest(namespace.RootContext(nil), &logical.Request{
|
||||||
|
ClientToken: initToken,
|
||||||
|
Path: "create",
|
||||||
|
Operation: logical.UpdateOperation,
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"id": "s.foobar",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected an error")
|
||||||
|
}
|
||||||
|
if resp.Error().Error() != "custom token ID cannot have the 's.' prefix" {
|
||||||
|
t.Fatalf("expected input error not present in error response")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("period in custom ID", func(t *testing.T) {
|
||||||
|
core, _, root := TestCoreUnsealed(t)
|
||||||
|
ts := core.tokenStore
|
||||||
|
|
||||||
|
resp, err := ts.HandleRequest(namespace.RootContext(nil), &logical.Request{
|
||||||
|
ClientToken: root,
|
||||||
|
Path: "create",
|
||||||
|
Operation: logical.UpdateOperation,
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"id": "foobar.baz",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected an error")
|
||||||
|
}
|
||||||
|
if resp.Error().Error() != "custom token ID cannot have a '.' in the value" {
|
||||||
|
t.Fatalf("expected input error not present in error response")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -67,7 +67,8 @@ during this call.
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `id` `(string: "")` – The ID of the client token. Can only be specified by a
|
- `id` `(string: "")` – The ID of the client token. Can only be specified by a
|
||||||
root token. Otherwise, the token ID is a randomly generated value.
|
root token. The ID provided may not contain a `.` character. Otherwise, the
|
||||||
|
token ID is a randomly generated value.
|
||||||
- `role_name` `(string: "")` – The name of the token role.
|
- `role_name` `(string: "")` – The name of the token role.
|
||||||
- `policies` `(array: "")` – A list of policies for the token. This must be a
|
- `policies` `(array: "")` – A list of policies for the token. This must be a
|
||||||
subset of the policies belonging to the token making the request, unless root.
|
subset of the policies belonging to the token making the request, unless root.
|
||||||
|
|
Loading…
Reference in New Issue