mirror of
https://github.com/hashicorp/vault.git
synced 2025-12-09 11:31:52 +01:00
Batch tokens (#755)
This commit is contained in:
parent
8db6aabee1
commit
a58d313d2b
@ -271,4 +271,5 @@ type TokenCreateRequest struct {
|
|||||||
DisplayName string `json:"display_name"`
|
DisplayName string `json:"display_name"`
|
||||||
NumUses int `json:"num_uses"`
|
NumUses int `json:"num_uses"`
|
||||||
Renewable *bool `json:"renewable,omitempty"`
|
Renewable *bool `json:"renewable,omitempty"`
|
||||||
|
Type string `json:"type"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,46 +73,8 @@ func (c *Sys) DisableAuth(path string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Structures for the requests/resposne are all down here. They aren't
|
// Rather than duplicate, we can use modern Go's type aliasing
|
||||||
// individually documented because the map almost directly to the raw HTTP API
|
type EnableAuthOptions = MountInput
|
||||||
// documentation. Please refer to that documentation for more details.
|
type AuthConfigInput = MountConfigInput
|
||||||
|
type AuthMount = MountOutput
|
||||||
type EnableAuthOptions struct {
|
type AuthConfigOutput = MountConfigOutput
|
||||||
Type string `json:"type"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Config AuthConfigInput `json:"config"`
|
|
||||||
Local bool `json:"local"`
|
|
||||||
PluginName string `json:"plugin_name,omitempty"`
|
|
||||||
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
|
|
||||||
Options map[string]string `json:"options" mapstructure:"options"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuthConfigInput struct {
|
|
||||||
DefaultLeaseTTL string `json:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
|
||||||
MaxLeaseTTL string `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
|
||||||
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
|
||||||
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"`
|
|
||||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
|
|
||||||
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
|
|
||||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuthMount struct {
|
|
||||||
Type string `json:"type" mapstructure:"type"`
|
|
||||||
Description string `json:"description" mapstructure:"description"`
|
|
||||||
Accessor string `json:"accessor" mapstructure:"accessor"`
|
|
||||||
Config AuthConfigOutput `json:"config" mapstructure:"config"`
|
|
||||||
Local bool `json:"local" mapstructure:"local"`
|
|
||||||
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
|
|
||||||
Options map[string]string `json:"options" mapstructure:"options"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuthConfigOutput struct {
|
|
||||||
DefaultLeaseTTL int `json:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
|
||||||
MaxLeaseTTL int `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
|
||||||
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
|
||||||
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"`
|
|
||||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
|
|
||||||
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
|
|
||||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
|
|
||||||
}
|
|
||||||
|
|||||||
@ -132,10 +132,10 @@ type MountInput struct {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Config MountConfigInput `json:"config"`
|
Config MountConfigInput `json:"config"`
|
||||||
Options map[string]string `json:"options"`
|
|
||||||
Local bool `json:"local"`
|
Local bool `json:"local"`
|
||||||
PluginName string `json:"plugin_name,omitempty"`
|
PluginName string `json:"plugin_name,omitempty"`
|
||||||
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
|
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
|
||||||
|
Options map[string]string `json:"options"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MountConfigInput struct {
|
type MountConfigInput struct {
|
||||||
@ -149,6 +149,7 @@ type MountConfigInput struct {
|
|||||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
|
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
|
||||||
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
|
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
|
||||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
|
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
|
||||||
|
TokenType string `json:"token_type,omitempty" mapstructure:"token_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MountOutput struct {
|
type MountOutput struct {
|
||||||
@ -170,4 +171,5 @@ type MountConfigOutput struct {
|
|||||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
|
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
|
||||||
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
|
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
|
||||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
|
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
|
||||||
|
TokenType string `json:"token_type,omitempty" mapstructure:"token_type"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -134,6 +134,7 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config
|
|||||||
Metadata: auth.Metadata,
|
Metadata: auth.Metadata,
|
||||||
EntityID: auth.EntityID,
|
EntityID: auth.EntityID,
|
||||||
RemainingUses: req.ClientTokenRemainingUses,
|
RemainingUses: req.ClientTokenRemainingUses,
|
||||||
|
TokenType: auth.TokenType.String(),
|
||||||
},
|
},
|
||||||
|
|
||||||
Request: AuditRequest{
|
Request: AuditRequest{
|
||||||
@ -304,6 +305,8 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
|
|||||||
ExternalNamespacePolicies: resp.Auth.ExternalNamespacePolicies,
|
ExternalNamespacePolicies: resp.Auth.ExternalNamespacePolicies,
|
||||||
Metadata: resp.Auth.Metadata,
|
Metadata: resp.Auth.Metadata,
|
||||||
NumUses: resp.Auth.NumUses,
|
NumUses: resp.Auth.NumUses,
|
||||||
|
EntityID: resp.Auth.EntityID,
|
||||||
|
TokenType: resp.Auth.TokenType.String(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,16 +337,17 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
|
|||||||
Type: "response",
|
Type: "response",
|
||||||
Error: errString,
|
Error: errString,
|
||||||
Auth: AuditAuth{
|
Auth: AuditAuth{
|
||||||
|
ClientToken: auth.ClientToken,
|
||||||
|
Accessor: auth.Accessor,
|
||||||
DisplayName: auth.DisplayName,
|
DisplayName: auth.DisplayName,
|
||||||
Policies: auth.Policies,
|
Policies: auth.Policies,
|
||||||
TokenPolicies: auth.TokenPolicies,
|
TokenPolicies: auth.TokenPolicies,
|
||||||
IdentityPolicies: auth.IdentityPolicies,
|
IdentityPolicies: auth.IdentityPolicies,
|
||||||
ExternalNamespacePolicies: auth.ExternalNamespacePolicies,
|
ExternalNamespacePolicies: auth.ExternalNamespacePolicies,
|
||||||
Metadata: auth.Metadata,
|
Metadata: auth.Metadata,
|
||||||
ClientToken: auth.ClientToken,
|
|
||||||
Accessor: auth.Accessor,
|
|
||||||
RemainingUses: req.ClientTokenRemainingUses,
|
RemainingUses: req.ClientTokenRemainingUses,
|
||||||
EntityID: auth.EntityID,
|
EntityID: auth.EntityID,
|
||||||
|
TokenType: auth.TokenType.String(),
|
||||||
},
|
},
|
||||||
|
|
||||||
Request: AuditRequest{
|
Request: AuditRequest{
|
||||||
@ -437,6 +441,7 @@ type AuditAuth struct {
|
|||||||
NumUses int `json:"num_uses,omitempty"`
|
NumUses int `json:"num_uses,omitempty"`
|
||||||
RemainingUses int `json:"remaining_uses,omitempty"`
|
RemainingUses int `json:"remaining_uses,omitempty"`
|
||||||
EntityID string `json:"entity_id"`
|
EntityID string `json:"entity_id"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuditSecret struct {
|
type AuditSecret struct {
|
||||||
|
|||||||
@ -37,7 +37,13 @@ func TestFormatJSON_formatRequest(t *testing.T) {
|
|||||||
ExpectedStr string
|
ExpectedStr string
|
||||||
}{
|
}{
|
||||||
"auth, request": {
|
"auth, request": {
|
||||||
&logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}},
|
&logical.Auth{
|
||||||
|
ClientToken: "foo",
|
||||||
|
Accessor: "bar",
|
||||||
|
DisplayName: "testtoken",
|
||||||
|
Policies: []string{"root"},
|
||||||
|
TokenType: logical.TokenTypeService,
|
||||||
|
},
|
||||||
&logical.Request{
|
&logical.Request{
|
||||||
Operation: logical.UpdateOperation,
|
Operation: logical.UpdateOperation,
|
||||||
Path: "/foo",
|
Path: "/foo",
|
||||||
@ -56,7 +62,13 @@ func TestFormatJSON_formatRequest(t *testing.T) {
|
|||||||
expectedResultStr,
|
expectedResultStr,
|
||||||
},
|
},
|
||||||
"auth, request with prefix": {
|
"auth, request with prefix": {
|
||||||
&logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}},
|
&logical.Auth{
|
||||||
|
ClientToken: "foo",
|
||||||
|
Accessor: "bar",
|
||||||
|
DisplayName: "testtoken",
|
||||||
|
Policies: []string{"root"},
|
||||||
|
TokenType: logical.TokenTypeService,
|
||||||
|
},
|
||||||
&logical.Request{
|
&logical.Request{
|
||||||
Operation: logical.UpdateOperation,
|
Operation: logical.UpdateOperation,
|
||||||
Path: "/foo",
|
Path: "/foo",
|
||||||
@ -127,5 +139,5 @@ func TestFormatJSON_formatRequest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const testFormatJSONReqBasicStrFmt = `{"time":"2015-08-05T13:45:46Z","type":"request","auth":{"client_token":"%s","accessor":"bar","display_name":"testtoken","policies":["root"],"metadata":null},"request":{"operation":"update","path":"/foo","data":null,"wrap_ttl":60,"remote_address":"127.0.0.1","headers":{"foo":["bar"]}},"error":"this is an error"}
|
const testFormatJSONReqBasicStrFmt = `{"time":"2015-08-05T13:45:46Z","type":"request","auth":{"client_token":"%s","accessor":"bar","display_name":"testtoken","policies":["root"],"metadata":null,"entity_id":"","token_type":"service"},"request":{"operation":"update","path":"/foo","data":null,"wrap_ttl":60,"remote_address":"127.0.0.1","headers":{"foo":["bar"]}},"error":"this is an error"}
|
||||||
`
|
`
|
||||||
|
|||||||
@ -36,7 +36,13 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
|
|||||||
ExpectedStr string
|
ExpectedStr string
|
||||||
}{
|
}{
|
||||||
"auth, request": {
|
"auth, request": {
|
||||||
&logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}},
|
&logical.Auth{
|
||||||
|
ClientToken: "foo",
|
||||||
|
Accessor: "bar",
|
||||||
|
DisplayName: "testtoken",
|
||||||
|
Policies: []string{"root"},
|
||||||
|
TokenType: logical.TokenTypeService,
|
||||||
|
},
|
||||||
&logical.Request{
|
&logical.Request{
|
||||||
Operation: logical.UpdateOperation,
|
Operation: logical.UpdateOperation,
|
||||||
Path: "/foo",
|
Path: "/foo",
|
||||||
@ -53,11 +59,17 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
|
|||||||
errors.New("this is an error"),
|
errors.New("this is an error"),
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
fmt.Sprintf(`<json:object name="auth"><json:string name="accessor">bar</json:string><json:string name="client_token">%s</json:string><json:string name="display_name">testtoken</json:string><json:string name="entity_id"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:object name="namespace"><json:string name="id">root</json:string><json:string name="path"></json:string></json:object><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:boolean name="policy_override">false</json:boolean><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
|
fmt.Sprintf(`<json:object name="auth"><json:string name="accessor">bar</json:string><json:string name="client_token">%s</json:string><json:string name="display_name">testtoken</json:string><json:string name="entity_id"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array><json:string name="token_type">service</json:string></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:object name="namespace"><json:string name="id">root</json:string><json:string name="path"></json:string></json:object><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:boolean name="policy_override">false</json:boolean><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
|
||||||
fooSalted),
|
fooSalted),
|
||||||
},
|
},
|
||||||
"auth, request with prefix": {
|
"auth, request with prefix": {
|
||||||
&logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}},
|
&logical.Auth{
|
||||||
|
ClientToken: "foo",
|
||||||
|
Accessor: "bar",
|
||||||
|
DisplayName: "testtoken",
|
||||||
|
Policies: []string{"root"},
|
||||||
|
TokenType: logical.TokenTypeService,
|
||||||
|
},
|
||||||
&logical.Request{
|
&logical.Request{
|
||||||
Operation: logical.UpdateOperation,
|
Operation: logical.UpdateOperation,
|
||||||
Path: "/foo",
|
Path: "/foo",
|
||||||
@ -74,7 +86,7 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
|
|||||||
errors.New("this is an error"),
|
errors.New("this is an error"),
|
||||||
"",
|
"",
|
||||||
"@cee: ",
|
"@cee: ",
|
||||||
fmt.Sprintf(`<json:object name="auth"><json:string name="accessor">bar</json:string><json:string name="client_token">%s</json:string><json:string name="display_name">testtoken</json:string><json:string name="entity_id"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:object name="namespace"><json:string name="id">root</json:string><json:string name="path"></json:string></json:object><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:boolean name="policy_override">false</json:boolean><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
|
fmt.Sprintf(`<json:object name="auth"><json:string name="accessor">bar</json:string><json:string name="client_token">%s</json:string><json:string name="display_name">testtoken</json:string><json:string name="entity_id"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array><json:string name="token_type">service</json:string></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:object name="namespace"><json:string name="id">root</json:string><json:string name="path"></json:string></json:object><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:boolean name="policy_override">false</json:boolean><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
|
||||||
fooSalted),
|
fooSalted),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -304,6 +304,15 @@ func (b *backend) pathLoginUpdate(ctx context.Context, req *logical.Request, dat
|
|||||||
BoundCIDRs: tokenBoundCIDRs,
|
BoundCIDRs: tokenBoundCIDRs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch role.TokenType {
|
||||||
|
case "default":
|
||||||
|
auth.TokenType = logical.TokenTypeDefault
|
||||||
|
case "batch":
|
||||||
|
auth.TokenType = logical.TokenTypeBatch
|
||||||
|
case "service":
|
||||||
|
auth.TokenType = logical.TokenTypeService
|
||||||
|
}
|
||||||
|
|
||||||
return &logical.Response{
|
return &logical.Response{
|
||||||
Auth: auth,
|
Auth: auth,
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
@ -84,6 +84,9 @@ type roleStorageEntry struct {
|
|||||||
// SecretIDPrefix is the storage prefix for persisting secret IDs. This
|
// SecretIDPrefix is the storage prefix for persisting secret IDs. This
|
||||||
// differs based on whether the secret IDs are cluster local or not.
|
// differs based on whether the secret IDs are cluster local or not.
|
||||||
SecretIDPrefix string `json:"secret_id_prefix" mapstructure:"secret_id_prefix"`
|
SecretIDPrefix string `json:"secret_id_prefix" mapstructure:"secret_id_prefix"`
|
||||||
|
|
||||||
|
// TokenType is the type of token to generate
|
||||||
|
TokenType string `json:"token_type" mapstructure:"token_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// roleIDStorageEntry represents the reverse mapping from RoleID to Role
|
// roleIDStorageEntry represents the reverse mapping from RoleID to Role
|
||||||
@ -196,6 +199,11 @@ TTL will be set to the value of this parameter.`,
|
|||||||
Description: `If set, the secret IDs generated using this role will be cluster local. This
|
Description: `If set, the secret IDs generated using this role will be cluster local. This
|
||||||
can only be set during role creation and once set, it can't be reset later.`,
|
can only be set during role creation and once set, it can't be reset later.`,
|
||||||
},
|
},
|
||||||
|
"token_type": &framework.FieldSchema{
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Default: "default",
|
||||||
|
Description: `The type of token to generate ("service" or "batch"), or "default" to use the default`,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
ExistenceCheck: b.pathRoleExistenceCheck,
|
ExistenceCheck: b.pathRoleExistenceCheck,
|
||||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
@ -1007,6 +1015,30 @@ func (b *backend) pathRoleCreateUpdate(ctx context.Context, req *logical.Request
|
|||||||
role.TokenMaxTTL = time.Second * time.Duration(data.Get("token_max_ttl").(int))
|
role.TokenMaxTTL = time.Second * time.Duration(data.Get("token_max_ttl").(int))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokenType := role.TokenType
|
||||||
|
if tokenTypeRaw, ok := data.GetOk("token_type"); ok {
|
||||||
|
tokenType = tokenTypeRaw.(string)
|
||||||
|
switch tokenType {
|
||||||
|
case "":
|
||||||
|
tokenType = "default"
|
||||||
|
case "service", "batch", "default":
|
||||||
|
default:
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf("invalid 'token_type' value %q", tokenType)), nil
|
||||||
|
}
|
||||||
|
} else if req.Operation == logical.CreateOperation {
|
||||||
|
tokenType = data.Get("token_type").(string)
|
||||||
|
}
|
||||||
|
role.TokenType = tokenType
|
||||||
|
|
||||||
|
if role.TokenType == "batch" {
|
||||||
|
if role.Period != 0 {
|
||||||
|
return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate periodic tokens"), nil
|
||||||
|
}
|
||||||
|
if role.TokenNumUses != 0 {
|
||||||
|
return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate tokens with limited use count"), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check that the TokenTTL value provided is less than the TokenMaxTTL.
|
// Check that the TokenTTL value provided is less than the TokenMaxTTL.
|
||||||
// Sanitizing the TTL and MaxTTL is not required now and can be performed
|
// Sanitizing the TTL and MaxTTL is not required now and can be performed
|
||||||
// at credential issue time.
|
// at credential issue time.
|
||||||
@ -1061,6 +1093,7 @@ func (b *backend) pathRoleRead(ctx context.Context, req *logical.Request, data *
|
|||||||
"token_num_uses": role.TokenNumUses,
|
"token_num_uses": role.TokenNumUses,
|
||||||
"token_ttl": role.TokenTTL / time.Second,
|
"token_ttl": role.TokenTTL / time.Second,
|
||||||
"local_secret_ids": false,
|
"local_secret_ids": false,
|
||||||
|
"token_type": role.TokenType,
|
||||||
}
|
}
|
||||||
|
|
||||||
if role.SecretIDPrefix == secretIDLocalPrefix {
|
if role.SecretIDPrefix == secretIDLocalPrefix {
|
||||||
|
|||||||
@ -1159,6 +1159,7 @@ func TestAppRole_RoleCRUD(t *testing.T) {
|
|||||||
"secret_id_bound_cidrs": []string{"127.0.0.1/32", "127.0.0.1/16"},
|
"secret_id_bound_cidrs": []string{"127.0.0.1/32", "127.0.0.1/16"},
|
||||||
"bound_cidr_list": []string{"127.0.0.1/32", "127.0.0.1/16"}, // returned for backwards compatibility
|
"bound_cidr_list": []string{"127.0.0.1/32", "127.0.0.1/16"}, // returned for backwards compatibility
|
||||||
"token_bound_cidrs": []string{},
|
"token_bound_cidrs": []string{},
|
||||||
|
"token_type": "default",
|
||||||
}
|
}
|
||||||
|
|
||||||
var expectedStruct roleStorageEntry
|
var expectedStruct roleStorageEntry
|
||||||
@ -1637,6 +1638,7 @@ func TestAppRole_RoleWithTokenBoundCIDRsCRUD(t *testing.T) {
|
|||||||
"token_bound_cidrs": []string{"127.0.0.1/32", "127.0.0.1/16"},
|
"token_bound_cidrs": []string{"127.0.0.1/32", "127.0.0.1/16"},
|
||||||
"secret_id_bound_cidrs": []string{"127.0.0.1/32", "127.0.0.1/16"},
|
"secret_id_bound_cidrs": []string{"127.0.0.1/32", "127.0.0.1/16"},
|
||||||
"bound_cidr_list": []string{"127.0.0.1/32", "127.0.0.1/16"}, // provided for backwards compatibility
|
"bound_cidr_list": []string{"127.0.0.1/32", "127.0.0.1/16"}, // provided for backwards compatibility
|
||||||
|
"token_type": "default",
|
||||||
}
|
}
|
||||||
|
|
||||||
var expectedStruct roleStorageEntry
|
var expectedStruct roleStorageEntry
|
||||||
|
|||||||
@ -30,6 +30,7 @@ type AuthEnableCommand struct {
|
|||||||
flagOptions map[string]string
|
flagOptions map[string]string
|
||||||
flagLocal bool
|
flagLocal bool
|
||||||
flagSealWrap bool
|
flagSealWrap bool
|
||||||
|
flagTokenType string
|
||||||
flagVersion int
|
flagVersion int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,6 +163,12 @@ func (c *AuthEnableCommand) Flags() *FlagSets {
|
|||||||
Usage: "Enable seal wrapping of critical values in the secrets engine.",
|
Usage: "Enable seal wrapping of critical values in the secrets engine.",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
f.StringVar(&StringVar{
|
||||||
|
Name: flagNameTokenType,
|
||||||
|
Target: &c.flagTokenType,
|
||||||
|
Usage: "Sets a forced token type for the mount.",
|
||||||
|
})
|
||||||
|
|
||||||
f.IntVar(&IntVar{
|
f.IntVar(&IntVar{
|
||||||
Name: "version",
|
Name: "version",
|
||||||
Target: &c.flagVersion,
|
Target: &c.flagVersion,
|
||||||
@ -257,6 +264,10 @@ func (c *AuthEnableCommand) Run(args []string) int {
|
|||||||
if fl.Name == flagNamePassthroughRequestHeaders {
|
if fl.Name == flagNamePassthroughRequestHeaders {
|
||||||
authOpts.Config.PassthroughRequestHeaders = c.flagPassthroughRequestHeaders
|
authOpts.Config.PassthroughRequestHeaders = c.flagPassthroughRequestHeaders
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fl.Name == flagNameTokenType {
|
||||||
|
authOpts.Config.TokenType = c.flagTokenType
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if err := client.Sys().EnableAuthWithOptions(authPath, authOpts); err != nil {
|
if err := client.Sys().EnableAuthWithOptions(authPath, authOpts); err != nil {
|
||||||
|
|||||||
@ -143,7 +143,7 @@ func (c *AuthListCommand) detailedMounts(auths map[string]*api.AuthMount) []stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out := []string{"Path | Type | Accessor | Plugin | Default TTL | Max TTL | Replication | Seal Wrap | Options | Description"}
|
out := []string{"Path | Type | Accessor | Plugin | Default TTL | Max TTL | Token Type | Replication | Seal Wrap | Options | Description"}
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
mount := auths[path]
|
mount := auths[path]
|
||||||
|
|
||||||
@ -155,13 +155,14 @@ func (c *AuthListCommand) detailedMounts(auths map[string]*api.AuthMount) []stri
|
|||||||
replication = "local"
|
replication = "local"
|
||||||
}
|
}
|
||||||
|
|
||||||
out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s | %s | %s | %t | %v | %s",
|
out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s | %s | %s | %s | %t | %v | %s",
|
||||||
path,
|
path,
|
||||||
mount.Type,
|
mount.Type,
|
||||||
mount.Accessor,
|
mount.Accessor,
|
||||||
mount.Config.PluginName,
|
mount.Config.PluginName,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
maxTTL,
|
maxTTL,
|
||||||
|
mount.Config.TokenType,
|
||||||
replication,
|
replication,
|
||||||
mount.SealWrap,
|
mount.SealWrap,
|
||||||
mount.Options,
|
mount.Options,
|
||||||
|
|||||||
@ -25,6 +25,7 @@ type AuthTuneCommand struct {
|
|||||||
flagListingVisibility string
|
flagListingVisibility string
|
||||||
flagMaxLeaseTTL time.Duration
|
flagMaxLeaseTTL time.Duration
|
||||||
flagOptions map[string]string
|
flagOptions map[string]string
|
||||||
|
flagTokenType string
|
||||||
flagVersion int
|
flagVersion int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,6 +113,12 @@ func (c *AuthTuneCommand) Flags() *FlagSets {
|
|||||||
"This can be specified multiple times.",
|
"This can be specified multiple times.",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
f.StringVar(&StringVar{
|
||||||
|
Name: flagNameTokenType,
|
||||||
|
Target: &c.flagTokenType,
|
||||||
|
Usage: "Sets a forced token type for the mount.",
|
||||||
|
})
|
||||||
|
|
||||||
f.IntVar(&IntVar{
|
f.IntVar(&IntVar{
|
||||||
Name: "version",
|
Name: "version",
|
||||||
Target: &c.flagVersion,
|
Target: &c.flagVersion,
|
||||||
@ -184,6 +191,10 @@ func (c *AuthTuneCommand) Run(args []string) int {
|
|||||||
if fl.Name == flagNameListingVisibility {
|
if fl.Name == flagNameListingVisibility {
|
||||||
mountConfigInput.ListingVisibility = c.flagListingVisibility
|
mountConfigInput.ListingVisibility = c.flagListingVisibility
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fl.Name == flagNameTokenType {
|
||||||
|
mountConfigInput.TokenType = c.flagTokenType
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Append /auth (since that's where auths live) and a trailing slash to
|
// Append /auth (since that's where auths live) and a trailing slash to
|
||||||
|
|||||||
@ -92,6 +92,8 @@ const (
|
|||||||
flagNameListingVisibility = "listing-visibility"
|
flagNameListingVisibility = "listing-visibility"
|
||||||
// flagNamePassthroughRequestHeaders is the flag name used to set passthrough request headers to the backend
|
// flagNamePassthroughRequestHeaders is the flag name used to set passthrough request headers to the backend
|
||||||
flagNamePassthroughRequestHeaders = "passthrough-request-headers"
|
flagNamePassthroughRequestHeaders = "passthrough-request-headers"
|
||||||
|
// flagNameTokenType is the flag name used to force a specific token type
|
||||||
|
flagNameTokenType = "token-type"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@ -26,6 +26,7 @@ type TokenCreateCommand struct {
|
|||||||
flagNoDefaultPolicy bool
|
flagNoDefaultPolicy bool
|
||||||
flagUseLimit int
|
flagUseLimit int
|
||||||
flagRole string
|
flagRole string
|
||||||
|
flagType string
|
||||||
flagMetadata map[string]string
|
flagMetadata map[string]string
|
||||||
flagPolicies []string
|
flagPolicies []string
|
||||||
|
|
||||||
@ -153,6 +154,13 @@ func (c *TokenCreateCommand) Flags() *FlagSets {
|
|||||||
"must have permission for \"auth/token/create/<role>\".",
|
"must have permission for \"auth/token/create/<role>\".",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
f.StringVar(&StringVar{
|
||||||
|
Name: "type",
|
||||||
|
Target: &c.flagType,
|
||||||
|
Default: "service",
|
||||||
|
Usage: `The type of token to create. Can be "service" or "batch".`,
|
||||||
|
})
|
||||||
|
|
||||||
f.StringMapVar(&StringMapVar{
|
f.StringMapVar(&StringMapVar{
|
||||||
Name: "metadata",
|
Name: "metadata",
|
||||||
Target: &c.flagMetadata,
|
Target: &c.flagMetadata,
|
||||||
@ -213,6 +221,10 @@ func (c *TokenCreateCommand) Run(args []string) int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.flagType == "batch" {
|
||||||
|
c.flagRenewable = false
|
||||||
|
}
|
||||||
|
|
||||||
client, err := c.Client()
|
client, err := c.Client()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.UI.Error(err.Error())
|
c.UI.Error(err.Error())
|
||||||
@ -231,6 +243,7 @@ func (c *TokenCreateCommand) Run(args []string) int {
|
|||||||
Renewable: &c.flagRenewable,
|
Renewable: &c.flagRenewable,
|
||||||
ExplicitMaxTTL: c.flagExplicitMaxTTL.String(),
|
ExplicitMaxTTL: c.flagExplicitMaxTTL.String(),
|
||||||
Period: c.flagPeriod.String(),
|
Period: c.flagPeriod.String(),
|
||||||
|
Type: c.flagType,
|
||||||
}
|
}
|
||||||
|
|
||||||
var secret *api.Secret
|
var secret *api.Secret
|
||||||
|
|||||||
@ -104,7 +104,18 @@ func Canonicalize(nsPath string) string {
|
|||||||
func SplitIDFromString(input string) (string, string) {
|
func SplitIDFromString(input string) (string, string) {
|
||||||
prefix := ""
|
prefix := ""
|
||||||
slashIdx := strings.LastIndex(input, "/")
|
slashIdx := strings.LastIndex(input, "/")
|
||||||
if slashIdx > 0 {
|
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(input, "b."):
|
||||||
|
prefix = "b."
|
||||||
|
input = input[2:]
|
||||||
|
|
||||||
|
case strings.HasPrefix(input, "s."):
|
||||||
|
prefix = "s."
|
||||||
|
input = input[2:]
|
||||||
|
|
||||||
|
case slashIdx > 0:
|
||||||
|
// Leases will never have a b./s. to start
|
||||||
if slashIdx == len(input)-1 {
|
if slashIdx == len(input)-1 {
|
||||||
return input, ""
|
return input, ""
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,6 +48,21 @@ func TestSplitIDFromString(t *testing.T) {
|
|||||||
"",
|
"",
|
||||||
"foo.foo/",
|
"foo.foo/",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"b.foo",
|
||||||
|
"",
|
||||||
|
"b.foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"s.foo",
|
||||||
|
"",
|
||||||
|
"s.foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"t.foo",
|
||||||
|
"foo",
|
||||||
|
"t",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range tcases {
|
for _, c := range tcases {
|
||||||
|
|||||||
@ -5,14 +5,33 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/vault/helper/consts"
|
"github.com/hashicorp/vault/helper/consts"
|
||||||
"github.com/hashicorp/vault/helper/xor"
|
"github.com/hashicorp/vault/helper/xor"
|
||||||
|
vaulthttp "github.com/hashicorp/vault/http"
|
||||||
|
"github.com/hashicorp/vault/physical"
|
||||||
|
"github.com/hashicorp/vault/physical/inmem"
|
||||||
"github.com/hashicorp/vault/vault"
|
"github.com/hashicorp/vault/vault"
|
||||||
"github.com/mitchellh/go-testing-interface"
|
testing "github.com/mitchellh/go-testing-interface"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ReplicatedTestClusters struct {
|
||||||
|
PerfPrimaryCluster *vault.TestCluster
|
||||||
|
PerfSecondaryCluster *vault.TestCluster
|
||||||
|
PerfPrimaryDRCluster *vault.TestCluster
|
||||||
|
PerfSecondaryDRCluster *vault.TestCluster
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ReplicatedTestClusters) Cleanup() {
|
||||||
|
r.PerfPrimaryCluster.Cleanup()
|
||||||
|
r.PerfSecondaryCluster.Cleanup()
|
||||||
|
r.PerfPrimaryDRCluster.Cleanup()
|
||||||
|
r.PerfSecondaryDRCluster.Cleanup()
|
||||||
|
}
|
||||||
|
|
||||||
// Generates a root token on the target cluster.
|
// Generates a root token on the target cluster.
|
||||||
func GenerateRoot(t testing.T, cluster *vault.TestCluster, drToken bool) string {
|
func GenerateRoot(t testing.T, cluster *vault.TestCluster, drToken bool) string {
|
||||||
token, err := GenerateRootWithError(t, cluster, drToken)
|
token, err := GenerateRootWithError(t, cluster, drToken)
|
||||||
@ -127,6 +146,65 @@ func WaitForReplicationState(t testing.T, c *vault.Core, state consts.Replicatio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetClusterAndCore(t testing.T, logger log.Logger) (*vault.TestCluster, *vault.TestClusterCore) {
|
||||||
|
inm, err := inmem.NewTransactionalInmem(nil, logger)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
inmha, err := inmem.NewInmemHA(nil, logger)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
coreConfig := &vault.CoreConfig{
|
||||||
|
Physical: inm,
|
||||||
|
HAPhysical: inmha.(physical.HABackend),
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
||||||
|
HandlerFunc: vaulthttp.Handler,
|
||||||
|
Logger: logger,
|
||||||
|
})
|
||||||
|
cluster.Start()
|
||||||
|
|
||||||
|
cores := cluster.Cores
|
||||||
|
core := cores[0]
|
||||||
|
|
||||||
|
vault.TestWaitActive(t, core.Core)
|
||||||
|
|
||||||
|
return cluster, core
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFourReplicatedClusters(t testing.T) *ReplicatedTestClusters {
|
||||||
|
ret := &ReplicatedTestClusters{}
|
||||||
|
|
||||||
|
logger := log.New(&log.LoggerOptions{
|
||||||
|
Mutex: &sync.Mutex{},
|
||||||
|
Level: log.Trace,
|
||||||
|
})
|
||||||
|
// Set this lower so that state populates quickly to standby nodes
|
||||||
|
vault.HeartbeatInterval = 2 * time.Second
|
||||||
|
|
||||||
|
ret.PerfPrimaryCluster, _ = GetClusterAndCore(t, logger.Named("perf-pri"))
|
||||||
|
|
||||||
|
ret.PerfSecondaryCluster, _ = GetClusterAndCore(t, logger.Named("perf-sec"))
|
||||||
|
|
||||||
|
ret.PerfPrimaryDRCluster, _ = GetClusterAndCore(t, logger.Named("perf-pri-dr"))
|
||||||
|
|
||||||
|
ret.PerfSecondaryDRCluster, _ = GetClusterAndCore(t, logger.Named("perf-sec-dr"))
|
||||||
|
|
||||||
|
SetupFourClusterReplication(t, ret.PerfPrimaryCluster, ret.PerfSecondaryCluster, ret.PerfPrimaryDRCluster, ret.PerfSecondaryDRCluster)
|
||||||
|
|
||||||
|
// Wait until poison pills have been read
|
||||||
|
time.Sleep(45 * time.Second)
|
||||||
|
EnsureCoresUnsealed(t, ret.PerfPrimaryCluster)
|
||||||
|
EnsureCoresUnsealed(t, ret.PerfSecondaryCluster)
|
||||||
|
EnsureCoresUnsealed(t, ret.PerfPrimaryDRCluster)
|
||||||
|
EnsureCoresUnsealed(t, ret.PerfSecondaryDRCluster)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
func SetupFourClusterReplication(t testing.T, perfPrimary, perfSecondary, perfDRSecondary, perfSecondaryDRSecondary *vault.TestCluster) {
|
func SetupFourClusterReplication(t testing.T, perfPrimary, perfSecondary, perfDRSecondary, perfSecondaryDRSecondary *vault.TestCluster) {
|
||||||
// Enable dr primary
|
// Enable dr primary
|
||||||
_, err := perfPrimary.Cores[0].Client.Logical().Write("sys/replication/dr/primary/enable", nil)
|
_, err := perfPrimary.Cores[0].Client.Logical().Write("sys/replication/dr/primary/enable", nil)
|
||||||
@ -137,7 +215,7 @@ func SetupFourClusterReplication(t testing.T, perfPrimary, perfSecondary, perfDR
|
|||||||
WaitForReplicationState(t, perfPrimary.Cores[0].Core, consts.ReplicationDRPrimary)
|
WaitForReplicationState(t, perfPrimary.Cores[0].Core, consts.ReplicationDRPrimary)
|
||||||
|
|
||||||
// Enable performance primary
|
// Enable performance primary
|
||||||
_, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/primary/enable", nil)
|
_, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/performance/primary/enable", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -167,7 +245,7 @@ func SetupFourClusterReplication(t testing.T, perfPrimary, perfSecondary, perfDR
|
|||||||
EnsureCoresUnsealed(t, perfDRSecondary)
|
EnsureCoresUnsealed(t, perfDRSecondary)
|
||||||
|
|
||||||
// get performance token
|
// get performance token
|
||||||
secret, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/primary/secondary-token", map[string]interface{}{
|
secret, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/performance/primary/secondary-token", map[string]interface{}{
|
||||||
"id": "1",
|
"id": "1",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -177,7 +255,7 @@ func SetupFourClusterReplication(t testing.T, perfPrimary, perfSecondary, perfDR
|
|||||||
token = secret.WrapInfo.Token
|
token = secret.WrapInfo.Token
|
||||||
|
|
||||||
// enable performace secondary
|
// enable performace secondary
|
||||||
secret, err = perfSecondary.Cores[0].Client.Logical().Write("sys/replication/secondary/enable", map[string]interface{}{
|
secret, err = perfSecondary.Cores[0].Client.Logical().Write("sys/replication/performance/secondary/enable", map[string]interface{}{
|
||||||
"token": token,
|
"token": token,
|
||||||
"ca_file": perfPrimary.CACertPEMFile,
|
"ca_file": perfPrimary.CACertPEMFile,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -421,9 +421,16 @@ func handleRequestForwarding(core *vault.Core, handler http.Handler) http.Handle
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
path := ns.TrimmedPath(r.URL.Path[len("/v1/"):])
|
path := ns.TrimmedPath(r.URL.Path[len("/v1/"):])
|
||||||
if !perfStandbyAlwaysForwardPaths.HasPath(path) {
|
switch {
|
||||||
|
case !perfStandbyAlwaysForwardPaths.HasPath(path):
|
||||||
handler.ServeHTTP(w, r)
|
handler.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
|
case strings.HasPrefix(path, "auth/token/create/"):
|
||||||
|
isBatch, err := core.IsBatchTokenCreationRequest(r.Context(), path)
|
||||||
|
if err == nil && isBatch {
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-test/deep"
|
||||||
log "github.com/hashicorp/go-hclog"
|
log "github.com/hashicorp/go-hclog"
|
||||||
|
|
||||||
"github.com/hashicorp/vault/helper/consts"
|
"github.com/hashicorp/vault/helper/consts"
|
||||||
@ -58,8 +59,8 @@ func TestLogical(t *testing.T) {
|
|||||||
testResponseBody(t, resp, &actual)
|
testResponseBody(t, resp, &actual)
|
||||||
delete(actual, "lease_id")
|
delete(actual, "lease_id")
|
||||||
expected["request_id"] = actual["request_id"]
|
expected["request_id"] = actual["request_id"]
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if diff := deep.Equal(actual, expected); diff != nil {
|
||||||
t.Fatalf("bad:\nactual:\n%#v\nexpected:\n%#v", actual, expected)
|
t.Fatal(diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DELETE
|
// DELETE
|
||||||
@ -163,6 +164,7 @@ func TestLogical_StandbyRedirect(t *testing.T) {
|
|||||||
"explicit_max_ttl": json.Number("0"),
|
"explicit_max_ttl": json.Number("0"),
|
||||||
"expire_time": nil,
|
"expire_time": nil,
|
||||||
"entity_id": "",
|
"entity_id": "",
|
||||||
|
"type": "service",
|
||||||
},
|
},
|
||||||
"warnings": nilWarnings,
|
"warnings": nilWarnings,
|
||||||
"wrap_info": nil,
|
"wrap_info": nil,
|
||||||
@ -177,8 +179,8 @@ func TestLogical_StandbyRedirect(t *testing.T) {
|
|||||||
actual["data"] = actualDataMap
|
actual["data"] = actualDataMap
|
||||||
expected["request_id"] = actual["request_id"]
|
expected["request_id"] = actual["request_id"]
|
||||||
delete(actual, "lease_id")
|
delete(actual, "lease_id")
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if diff := deep.Equal(actual, expected); diff != nil {
|
||||||
t.Fatalf("bad: got %#v; expected %#v", actual, expected)
|
t.Fatal(diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
//// DELETE to standby
|
//// DELETE to standby
|
||||||
@ -214,6 +216,7 @@ func TestLogical_CreateToken(t *testing.T) {
|
|||||||
"lease_duration": json.Number("0"),
|
"lease_duration": json.Number("0"),
|
||||||
"renewable": false,
|
"renewable": false,
|
||||||
"entity_id": "",
|
"entity_id": "",
|
||||||
|
"token_type": "service",
|
||||||
},
|
},
|
||||||
"warnings": nilWarnings,
|
"warnings": nilWarnings,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-test/deep"
|
||||||
"github.com/hashicorp/vault/vault"
|
"github.com/hashicorp/vault/vault"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,6 +33,8 @@ func TestSysAuth(t *testing.T) {
|
|||||||
"default_lease_ttl": json.Number("0"),
|
"default_lease_ttl": json.Number("0"),
|
||||||
"max_lease_ttl": json.Number("0"),
|
"max_lease_ttl": json.Number("0"),
|
||||||
"plugin_name": "",
|
"plugin_name": "",
|
||||||
|
"token_type": "default-service",
|
||||||
|
"force_no_cache": false,
|
||||||
},
|
},
|
||||||
"local": false,
|
"local": false,
|
||||||
"seal_wrap": false,
|
"seal_wrap": false,
|
||||||
@ -45,6 +48,8 @@ func TestSysAuth(t *testing.T) {
|
|||||||
"default_lease_ttl": json.Number("0"),
|
"default_lease_ttl": json.Number("0"),
|
||||||
"max_lease_ttl": json.Number("0"),
|
"max_lease_ttl": json.Number("0"),
|
||||||
"plugin_name": "",
|
"plugin_name": "",
|
||||||
|
"token_type": "default-service",
|
||||||
|
"force_no_cache": false,
|
||||||
},
|
},
|
||||||
"local": false,
|
"local": false,
|
||||||
"seal_wrap": false,
|
"seal_wrap": false,
|
||||||
@ -98,6 +103,8 @@ func TestSysEnableAuth(t *testing.T) {
|
|||||||
"default_lease_ttl": json.Number("0"),
|
"default_lease_ttl": json.Number("0"),
|
||||||
"max_lease_ttl": json.Number("0"),
|
"max_lease_ttl": json.Number("0"),
|
||||||
"plugin_name": "",
|
"plugin_name": "",
|
||||||
|
"token_type": "default-service",
|
||||||
|
"force_no_cache": false,
|
||||||
},
|
},
|
||||||
"local": false,
|
"local": false,
|
||||||
"seal_wrap": false,
|
"seal_wrap": false,
|
||||||
@ -110,6 +117,8 @@ func TestSysEnableAuth(t *testing.T) {
|
|||||||
"default_lease_ttl": json.Number("0"),
|
"default_lease_ttl": json.Number("0"),
|
||||||
"max_lease_ttl": json.Number("0"),
|
"max_lease_ttl": json.Number("0"),
|
||||||
"plugin_name": "",
|
"plugin_name": "",
|
||||||
|
"force_no_cache": false,
|
||||||
|
"token_type": "default-service",
|
||||||
},
|
},
|
||||||
"local": false,
|
"local": false,
|
||||||
"seal_wrap": false,
|
"seal_wrap": false,
|
||||||
@ -123,6 +132,8 @@ func TestSysEnableAuth(t *testing.T) {
|
|||||||
"default_lease_ttl": json.Number("0"),
|
"default_lease_ttl": json.Number("0"),
|
||||||
"max_lease_ttl": json.Number("0"),
|
"max_lease_ttl": json.Number("0"),
|
||||||
"plugin_name": "",
|
"plugin_name": "",
|
||||||
|
"token_type": "default-service",
|
||||||
|
"force_no_cache": false,
|
||||||
},
|
},
|
||||||
"local": false,
|
"local": false,
|
||||||
"seal_wrap": false,
|
"seal_wrap": false,
|
||||||
@ -135,6 +146,8 @@ func TestSysEnableAuth(t *testing.T) {
|
|||||||
"default_lease_ttl": json.Number("0"),
|
"default_lease_ttl": json.Number("0"),
|
||||||
"max_lease_ttl": json.Number("0"),
|
"max_lease_ttl": json.Number("0"),
|
||||||
"plugin_name": "",
|
"plugin_name": "",
|
||||||
|
"token_type": "default-service",
|
||||||
|
"force_no_cache": false,
|
||||||
},
|
},
|
||||||
"local": false,
|
"local": false,
|
||||||
"seal_wrap": false,
|
"seal_wrap": false,
|
||||||
@ -153,8 +166,8 @@ func TestSysEnableAuth(t *testing.T) {
|
|||||||
expected["data"].(map[string]interface{})[k].(map[string]interface{})["accessor"] = v.(map[string]interface{})["accessor"]
|
expected["data"].(map[string]interface{})[k].(map[string]interface{})["accessor"] = v.(map[string]interface{})["accessor"]
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if diff := deep.Equal(actual, expected); diff != nil {
|
||||||
t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual)
|
t.Fatal(diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,6 +202,8 @@ func TestSysDisableAuth(t *testing.T) {
|
|||||||
"default_lease_ttl": json.Number("0"),
|
"default_lease_ttl": json.Number("0"),
|
||||||
"max_lease_ttl": json.Number("0"),
|
"max_lease_ttl": json.Number("0"),
|
||||||
"plugin_name": "",
|
"plugin_name": "",
|
||||||
|
"token_type": "default-service",
|
||||||
|
"force_no_cache": false,
|
||||||
},
|
},
|
||||||
"description": "token based credentials",
|
"description": "token based credentials",
|
||||||
"type": "token",
|
"type": "token",
|
||||||
@ -202,6 +217,8 @@ func TestSysDisableAuth(t *testing.T) {
|
|||||||
"default_lease_ttl": json.Number("0"),
|
"default_lease_ttl": json.Number("0"),
|
||||||
"max_lease_ttl": json.Number("0"),
|
"max_lease_ttl": json.Number("0"),
|
||||||
"plugin_name": "",
|
"plugin_name": "",
|
||||||
|
"token_type": "default-service",
|
||||||
|
"force_no_cache": false,
|
||||||
},
|
},
|
||||||
"description": "token based credentials",
|
"description": "token based credentials",
|
||||||
"type": "token",
|
"type": "token",
|
||||||
@ -222,8 +239,8 @@ func TestSysDisableAuth(t *testing.T) {
|
|||||||
expected["data"].(map[string]interface{})[k].(map[string]interface{})["accessor"] = v.(map[string]interface{})["accessor"]
|
expected["data"].(map[string]interface{})[k].(map[string]interface{})["accessor"] = v.(map[string]interface{})["accessor"]
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
if diff := deep.Equal(actual, expected); diff != nil {
|
||||||
t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual)
|
t.Fatal(diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,12 +280,14 @@ func TestSysTuneAuth_nonHMACKeys(t *testing.T) {
|
|||||||
"force_no_cache": false,
|
"force_no_cache": false,
|
||||||
"audit_non_hmac_request_keys": []interface{}{"foo"},
|
"audit_non_hmac_request_keys": []interface{}{"foo"},
|
||||||
"audit_non_hmac_response_keys": []interface{}{"bar"},
|
"audit_non_hmac_response_keys": []interface{}{"bar"},
|
||||||
|
"token_type": "default-service",
|
||||||
},
|
},
|
||||||
"default_lease_ttl": json.Number("2764800"),
|
"default_lease_ttl": json.Number("2764800"),
|
||||||
"max_lease_ttl": json.Number("2764800"),
|
"max_lease_ttl": json.Number("2764800"),
|
||||||
"force_no_cache": false,
|
"force_no_cache": false,
|
||||||
"audit_non_hmac_request_keys": []interface{}{"foo"},
|
"audit_non_hmac_request_keys": []interface{}{"foo"},
|
||||||
"audit_non_hmac_response_keys": []interface{}{"bar"},
|
"audit_non_hmac_response_keys": []interface{}{"bar"},
|
||||||
|
"token_type": "default-service",
|
||||||
}
|
}
|
||||||
testResponseBody(t, resp, &actual)
|
testResponseBody(t, resp, &actual)
|
||||||
expected["request_id"] = actual["request_id"]
|
expected["request_id"] = actual["request_id"]
|
||||||
@ -302,10 +321,12 @@ func TestSysTuneAuth_nonHMACKeys(t *testing.T) {
|
|||||||
"default_lease_ttl": json.Number("2764800"),
|
"default_lease_ttl": json.Number("2764800"),
|
||||||
"max_lease_ttl": json.Number("2764800"),
|
"max_lease_ttl": json.Number("2764800"),
|
||||||
"force_no_cache": false,
|
"force_no_cache": false,
|
||||||
|
"token_type": "default-service",
|
||||||
},
|
},
|
||||||
"default_lease_ttl": json.Number("2764800"),
|
"default_lease_ttl": json.Number("2764800"),
|
||||||
"max_lease_ttl": json.Number("2764800"),
|
"max_lease_ttl": json.Number("2764800"),
|
||||||
"force_no_cache": false,
|
"force_no_cache": false,
|
||||||
|
"token_type": "default-service",
|
||||||
}
|
}
|
||||||
testResponseBody(t, resp, &actual)
|
testResponseBody(t, resp, &actual)
|
||||||
expected["request_id"] = actual["request_id"]
|
expected["request_id"] = actual["request_id"]
|
||||||
@ -336,10 +357,12 @@ func TestSysTuneAuth_showUIMount(t *testing.T) {
|
|||||||
"default_lease_ttl": json.Number("2764800"),
|
"default_lease_ttl": json.Number("2764800"),
|
||||||
"max_lease_ttl": json.Number("2764800"),
|
"max_lease_ttl": json.Number("2764800"),
|
||||||
"force_no_cache": false,
|
"force_no_cache": false,
|
||||||
|
"token_type": "default-service",
|
||||||
},
|
},
|
||||||
"default_lease_ttl": json.Number("2764800"),
|
"default_lease_ttl": json.Number("2764800"),
|
||||||
"max_lease_ttl": json.Number("2764800"),
|
"max_lease_ttl": json.Number("2764800"),
|
||||||
"force_no_cache": false,
|
"force_no_cache": false,
|
||||||
|
"token_type": "default-service",
|
||||||
}
|
}
|
||||||
testResponseBody(t, resp, &actual)
|
testResponseBody(t, resp, &actual)
|
||||||
expected["request_id"] = actual["request_id"]
|
expected["request_id"] = actual["request_id"]
|
||||||
@ -370,11 +393,13 @@ func TestSysTuneAuth_showUIMount(t *testing.T) {
|
|||||||
"max_lease_ttl": json.Number("2764800"),
|
"max_lease_ttl": json.Number("2764800"),
|
||||||
"force_no_cache": false,
|
"force_no_cache": false,
|
||||||
"listing_visibility": "unauth",
|
"listing_visibility": "unauth",
|
||||||
|
"token_type": "default-service",
|
||||||
},
|
},
|
||||||
"default_lease_ttl": json.Number("2764800"),
|
"default_lease_ttl": json.Number("2764800"),
|
||||||
"max_lease_ttl": json.Number("2764800"),
|
"max_lease_ttl": json.Number("2764800"),
|
||||||
"force_no_cache": false,
|
"force_no_cache": false,
|
||||||
"listing_visibility": "unauth",
|
"listing_visibility": "unauth",
|
||||||
|
"token_type": "default-service",
|
||||||
}
|
}
|
||||||
testResponseBody(t, resp, &actual)
|
testResponseBody(t, resp, &actual)
|
||||||
expected["request_id"] = actual["request_id"]
|
expected["request_id"] = actual["request_id"]
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-test/deep"
|
||||||
"github.com/hashicorp/vault/helper/pgpkeys"
|
"github.com/hashicorp/vault/helper/pgpkeys"
|
||||||
"github.com/hashicorp/vault/helper/xor"
|
"github.com/hashicorp/vault/helper/xor"
|
||||||
"github.com/hashicorp/vault/vault"
|
"github.com/hashicorp/vault/vault"
|
||||||
@ -333,6 +334,7 @@ func TestSysGenerateRoot_Update_OTP(t *testing.T) {
|
|||||||
"explicit_max_ttl": json.Number("0"),
|
"explicit_max_ttl": json.Number("0"),
|
||||||
"expire_time": nil,
|
"expire_time": nil,
|
||||||
"entity_id": "",
|
"entity_id": "",
|
||||||
|
"type": "service",
|
||||||
}
|
}
|
||||||
|
|
||||||
resp = testHttpGet(t, newRootToken, addr+"/v1/auth/token/lookup-self")
|
resp = testHttpGet(t, newRootToken, addr+"/v1/auth/token/lookup-self")
|
||||||
@ -431,6 +433,7 @@ func TestSysGenerateRoot_Update_PGP(t *testing.T) {
|
|||||||
"explicit_max_ttl": json.Number("0"),
|
"explicit_max_ttl": json.Number("0"),
|
||||||
"expire_time": nil,
|
"expire_time": nil,
|
||||||
"entity_id": "",
|
"entity_id": "",
|
||||||
|
"type": "service",
|
||||||
}
|
}
|
||||||
|
|
||||||
resp = testHttpGet(t, newRootToken, addr+"/v1/auth/token/lookup-self")
|
resp = testHttpGet(t, newRootToken, addr+"/v1/auth/token/lookup-self")
|
||||||
@ -440,7 +443,7 @@ func TestSysGenerateRoot_Update_PGP(t *testing.T) {
|
|||||||
expected["creation_time"] = actual["data"].(map[string]interface{})["creation_time"]
|
expected["creation_time"] = actual["data"].(map[string]interface{})["creation_time"]
|
||||||
expected["accessor"] = actual["data"].(map[string]interface{})["accessor"]
|
expected["accessor"] = actual["data"].(map[string]interface{})["accessor"]
|
||||||
|
|
||||||
if !reflect.DeepEqual(actual["data"], expected) {
|
if diff := deep.Equal(actual["data"], expected); diff != nil {
|
||||||
t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual["data"])
|
t.Fatal(diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,6 +89,9 @@ type Auth struct {
|
|||||||
// change the perceived path of the lease, even though they don't change
|
// change the perceived path of the lease, even though they don't change
|
||||||
// the request path itself.
|
// the request path itself.
|
||||||
CreationPath string `json:"creation_path"`
|
CreationPath string `json:"creation_path"`
|
||||||
|
|
||||||
|
// TokenType is the type of token being requested
|
||||||
|
TokenType TokenType `json:"token_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Auth) GoString() string {
|
func (a *Auth) GoString() string {
|
||||||
|
|||||||
@ -527,7 +527,9 @@ type Auth struct {
|
|||||||
IdentityPolicies []string `sentinel:"" protobuf:"bytes,15,rep,name=identity_policies,json=identityPolicies,proto3" json:"identity_policies,omitempty"`
|
IdentityPolicies []string `sentinel:"" protobuf:"bytes,15,rep,name=identity_policies,json=identityPolicies,proto3" json:"identity_policies,omitempty"`
|
||||||
// Explicit maximum lifetime for the token. Unlike normal TTLs, the maximum
|
// Explicit maximum lifetime for the token. Unlike normal TTLs, the maximum
|
||||||
// TTL is a hard limit and cannot be exceeded, also counts for periodic tokens.
|
// TTL is a hard limit and cannot be exceeded, also counts for periodic tokens.
|
||||||
ExplicitMaxTTL int64 `sentinel:"" protobuf:"varint,16,opt,name=explicit_max_ttl,json=explicitMaxTtl,proto3" json:"explicit_max_ttl,omitempty"`
|
ExplicitMaxTTL int64 `sentinel:"" protobuf:"varint,16,opt,name=explicit_max_ttl,json=explicitMaxTtl,proto3" json:"explicit_max_ttl,omitempty"`
|
||||||
|
// TokenType is the type of token being requested
|
||||||
|
TokenType uint32 `sentinel:"" protobuf:"varint,17,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
@ -670,6 +672,13 @@ func (m *Auth) GetExplicitMaxTTL() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Auth) GetTokenType() uint32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.TokenType
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type TokenEntry struct {
|
type TokenEntry struct {
|
||||||
ID string `sentinel:"" protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
ID string `sentinel:"" protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
Accessor string `sentinel:"" protobuf:"bytes,2,opt,name=accessor,proto3" json:"accessor,omitempty"`
|
Accessor string `sentinel:"" protobuf:"bytes,2,opt,name=accessor,proto3" json:"accessor,omitempty"`
|
||||||
@ -688,6 +697,7 @@ type TokenEntry struct {
|
|||||||
BoundCIDRs []string `sentinel:"" protobuf:"bytes,15,rep,name=bound_cidrs,json=boundCidrs,proto3" json:"bound_cidrs,omitempty"`
|
BoundCIDRs []string `sentinel:"" protobuf:"bytes,15,rep,name=bound_cidrs,json=boundCidrs,proto3" json:"bound_cidrs,omitempty"`
|
||||||
NamespaceID string `sentinel:"" protobuf:"bytes,16,opt,name=namespace_id,json=namespaceID,proto3" json:"namespace_id,omitempty"`
|
NamespaceID string `sentinel:"" protobuf:"bytes,16,opt,name=namespace_id,json=namespaceID,proto3" json:"namespace_id,omitempty"`
|
||||||
CubbyholeID string `sentinel:"" protobuf:"bytes,17,opt,name=cubbyhole_id,json=cubbyholeId,proto3" json:"cubbyhole_id,omitempty"`
|
CubbyholeID string `sentinel:"" protobuf:"bytes,17,opt,name=cubbyhole_id,json=cubbyholeId,proto3" json:"cubbyhole_id,omitempty"`
|
||||||
|
Type uint32 `sentinel:"" protobuf:"varint,18,opt,name=type,proto3" json:"type,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
@ -837,6 +847,13 @@ func (m *TokenEntry) GetCubbyholeID() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *TokenEntry) GetType() uint32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Type
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type LeaseOptions struct {
|
type LeaseOptions struct {
|
||||||
TTL int64 `sentinel:"" protobuf:"varint,1,opt,name=TTL,proto3" json:"TTL,omitempty"`
|
TTL int64 `sentinel:"" protobuf:"varint,1,opt,name=TTL,proto3" json:"TTL,omitempty"`
|
||||||
Renewable bool `sentinel:"" protobuf:"varint,2,opt,name=renewable,proto3" json:"renewable,omitempty"`
|
Renewable bool `sentinel:"" protobuf:"varint,2,opt,name=renewable,proto3" json:"renewable,omitempty"`
|
||||||
@ -3614,159 +3631,161 @@ var _SystemView_serviceDesc = grpc.ServiceDesc{
|
|||||||
func init() { proto.RegisterFile("logical/plugin/pb/backend.proto", fileDescriptor_25821d34acc7c5ef) }
|
func init() { proto.RegisterFile("logical/plugin/pb/backend.proto", fileDescriptor_25821d34acc7c5ef) }
|
||||||
|
|
||||||
var fileDescriptor_25821d34acc7c5ef = []byte{
|
var fileDescriptor_25821d34acc7c5ef = []byte{
|
||||||
// 2462 bytes of a gzipped FileDescriptorProto
|
// 2483 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0x5f, 0x73, 0xdb, 0xc6,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcd, 0x72, 0x1b, 0xc7,
|
||||||
0x11, 0x1f, 0xfe, 0x27, 0x97, 0xff, 0xa4, 0xb3, 0xa2, 0xc2, 0x8c, 0x53, 0x33, 0x48, 0x6d, 0x2b,
|
0x11, 0x2e, 0x00, 0xc4, 0x5f, 0xe3, 0x8f, 0x18, 0xd1, 0xcc, 0x0a, 0x96, 0x23, 0x78, 0x1d, 0x49,
|
||||||
0xae, 0x4d, 0xd9, 0x4a, 0xd3, 0x38, 0xed, 0x24, 0x1d, 0x45, 0x56, 0x1c, 0x35, 0x52, 0xa2, 0x81,
|
0xb4, 0x22, 0x81, 0x12, 0x1d, 0xc7, 0x72, 0x52, 0x76, 0x8a, 0xa6, 0x68, 0x99, 0x31, 0x69, 0xb3,
|
||||||
0xe8, 0xa6, 0xff, 0x66, 0x90, 0x23, 0x70, 0xa2, 0x30, 0x02, 0x01, 0xf4, 0x70, 0x90, 0xc5, 0xa7,
|
0x96, 0x50, 0x9c, 0xbf, 0x2a, 0x78, 0xb0, 0x3b, 0x04, 0xb7, 0xb8, 0xd8, 0xdd, 0xcc, 0xce, 0x52,
|
||||||
0x7e, 0x8b, 0xbe, 0xf4, 0x43, 0xf4, 0xad, 0xd3, 0xb7, 0xbe, 0x75, 0x3a, 0xd3, 0xe7, 0x7e, 0x8d,
|
0xc4, 0x29, 0x6f, 0x91, 0xd7, 0xc8, 0x35, 0x95, 0x4b, 0x6e, 0xa9, 0x54, 0x72, 0xce, 0x6b, 0xe4,
|
||||||
0x7e, 0x86, 0xce, 0xed, 0x1d, 0x40, 0x80, 0xa4, 0x62, 0x67, 0xa6, 0x7d, 0xbb, 0xdb, 0xdd, 0xdb,
|
0x19, 0x52, 0xd3, 0x33, 0xfb, 0x07, 0x80, 0x96, 0x5c, 0x95, 0xdc, 0x66, 0xba, 0x7b, 0x7a, 0x66,
|
||||||
0xbb, 0xdb, 0xfb, 0xed, 0x6f, 0x17, 0x24, 0xdc, 0xf5, 0xc3, 0xa9, 0xe7, 0x50, 0x7f, 0x37, 0xf2,
|
0x7a, 0xbe, 0xfe, 0xba, 0x17, 0x80, 0xbb, 0x5e, 0x30, 0x73, 0x6d, 0xea, 0xed, 0x86, 0x5e, 0x3c,
|
||||||
0x93, 0xa9, 0x17, 0xec, 0x46, 0x93, 0xdd, 0x09, 0x75, 0x2e, 0x59, 0xe0, 0x8e, 0x22, 0x1e, 0x8a,
|
0x73, 0xfd, 0xdd, 0x70, 0xba, 0x3b, 0xa5, 0xf6, 0x25, 0xf3, 0x9d, 0x51, 0xc8, 0x03, 0x11, 0x90,
|
||||||
0x90, 0x94, 0xa3, 0xc9, 0xe0, 0xee, 0x34, 0x0c, 0xa7, 0x3e, 0xdb, 0x45, 0xc9, 0x24, 0x39, 0xdf,
|
0x72, 0x38, 0x1d, 0xdc, 0x9d, 0x05, 0xc1, 0xcc, 0x63, 0xbb, 0x28, 0x99, 0xc6, 0xe7, 0xbb, 0xc2,
|
||||||
0x15, 0xde, 0x8c, 0xc5, 0x82, 0xce, 0x22, 0x65, 0x34, 0xd8, 0x4e, 0xbd, 0x78, 0x2e, 0x0b, 0x84,
|
0x9d, 0xb3, 0x48, 0xd0, 0x79, 0xa8, 0x8c, 0x06, 0xdb, 0x89, 0x17, 0xd7, 0x61, 0xbe, 0x70, 0xc5,
|
||||||
0x27, 0xe6, 0x5a, 0xbe, 0x55, 0xf4, 0xae, 0xa4, 0x66, 0x03, 0x6a, 0x87, 0xb3, 0x48, 0xcc, 0xcd,
|
0x42, 0xcb, 0xb7, 0x8a, 0xde, 0x95, 0xd4, 0xac, 0x43, 0xf5, 0x70, 0x1e, 0x8a, 0x85, 0x39, 0x84,
|
||||||
0x21, 0xd4, 0xbf, 0x60, 0xd4, 0x65, 0x9c, 0x6c, 0x43, 0xfd, 0x02, 0x47, 0x46, 0x69, 0x58, 0xd9,
|
0xda, 0x17, 0x8c, 0x3a, 0x8c, 0x93, 0x6d, 0xa8, 0x5d, 0xe0, 0xc8, 0x28, 0x0d, 0x2b, 0x3b, 0x4d,
|
||||||
0x69, 0x59, 0x7a, 0x66, 0xfe, 0x0e, 0xe0, 0x54, 0xae, 0x39, 0xe4, 0x3c, 0xe4, 0xe4, 0x36, 0x34,
|
0x4b, 0xcf, 0xcc, 0xdf, 0x01, 0x9c, 0xca, 0x35, 0x87, 0x9c, 0x07, 0x9c, 0xdc, 0x86, 0x06, 0xe3,
|
||||||
0x19, 0xe7, 0xb6, 0x98, 0x47, 0xcc, 0x28, 0x0d, 0x4b, 0x3b, 0x5d, 0xab, 0xc1, 0x38, 0x1f, 0xcf,
|
0x7c, 0x22, 0x16, 0x21, 0x33, 0x4a, 0xc3, 0xd2, 0x4e, 0xc7, 0xaa, 0x33, 0xce, 0xc7, 0x8b, 0x90,
|
||||||
0x23, 0x46, 0x7e, 0x00, 0x72, 0x68, 0xcf, 0xe2, 0xa9, 0x51, 0x1e, 0x96, 0xa4, 0x07, 0xc6, 0xf9,
|
0x91, 0x1f, 0x80, 0x1c, 0x4e, 0xe6, 0xd1, 0xcc, 0x28, 0x0f, 0x4b, 0xd2, 0x03, 0xe3, 0xfc, 0x24,
|
||||||
0x49, 0x3c, 0x4d, 0xd7, 0x38, 0xa1, 0xcb, 0x8c, 0xca, 0xb0, 0xb4, 0x53, 0xc1, 0x35, 0x07, 0xa1,
|
0x9a, 0x25, 0x6b, 0xec, 0xc0, 0x61, 0x46, 0x65, 0x58, 0xda, 0xa9, 0xe0, 0x9a, 0x83, 0xc0, 0x61,
|
||||||
0xcb, 0xcc, 0x3f, 0x95, 0xa0, 0x76, 0x4a, 0xc5, 0x45, 0x4c, 0x08, 0x54, 0x79, 0x18, 0x0a, 0xbd,
|
0xe6, 0x9f, 0x4a, 0x50, 0x3d, 0xa5, 0xe2, 0x22, 0x22, 0x04, 0x36, 0x78, 0x10, 0x08, 0xbd, 0x39,
|
||||||
0x39, 0x8e, 0xc9, 0x0e, 0xf4, 0x93, 0x80, 0x26, 0xe2, 0x42, 0xde, 0xc8, 0xa1, 0x82, 0xb9, 0x46,
|
0x8e, 0xc9, 0x0e, 0xf4, 0x62, 0x9f, 0xc6, 0xe2, 0x42, 0xde, 0xc8, 0xa6, 0x82, 0x39, 0x46, 0x19,
|
||||||
0x19, 0xd5, 0xcb, 0x62, 0xf2, 0x1e, 0x74, 0xfd, 0xd0, 0xa1, 0xbe, 0x1d, 0x8b, 0x90, 0xd3, 0xa9,
|
0xd5, 0xcb, 0x62, 0xf2, 0x1e, 0x74, 0xbc, 0xc0, 0xa6, 0xde, 0x24, 0x12, 0x01, 0xa7, 0x33, 0xb9,
|
||||||
0xdc, 0x47, 0xda, 0x75, 0x50, 0x78, 0xa6, 0x64, 0xe4, 0x21, 0x6c, 0xc6, 0x8c, 0xfa, 0xf6, 0x2b,
|
0x8f, 0xb4, 0x6b, 0xa3, 0xf0, 0x4c, 0xc9, 0xc8, 0x43, 0xe8, 0x47, 0x8c, 0x7a, 0x93, 0x57, 0x9c,
|
||||||
0x4e, 0xa3, 0xcc, 0xb0, 0xaa, 0x1c, 0x4a, 0xc5, 0x37, 0x9c, 0x46, 0xda, 0xd6, 0xfc, 0x7b, 0x1d,
|
0x86, 0xa9, 0xe1, 0x86, 0x72, 0x28, 0x15, 0xdf, 0x70, 0x1a, 0x6a, 0x5b, 0xf3, 0x6f, 0x35, 0xa8,
|
||||||
0x1a, 0x16, 0xfb, 0x43, 0xc2, 0x62, 0x41, 0x7a, 0x50, 0xf6, 0x5c, 0xbc, 0x6d, 0xcb, 0x2a, 0x7b,
|
0x5b, 0xec, 0x0f, 0x31, 0x8b, 0x04, 0xe9, 0x42, 0xd9, 0x75, 0xf0, 0xb6, 0x4d, 0xab, 0xec, 0x3a,
|
||||||
0x2e, 0x19, 0x01, 0xb1, 0x58, 0xe4, 0xcb, 0xad, 0xbd, 0x30, 0x38, 0xf0, 0x93, 0x58, 0x30, 0xae,
|
0x64, 0x04, 0xc4, 0x62, 0xa1, 0x27, 0xb7, 0x76, 0x03, 0xff, 0xc0, 0x8b, 0x23, 0xc1, 0xb8, 0xbe,
|
||||||
0xef, 0xbc, 0x46, 0x43, 0xee, 0x40, 0x2b, 0x8c, 0x18, 0x47, 0x19, 0x06, 0xa0, 0x65, 0x2d, 0x04,
|
0xf3, 0x1a, 0x0d, 0xb9, 0x03, 0xcd, 0x20, 0x64, 0x1c, 0x65, 0x18, 0x80, 0xa6, 0x95, 0x09, 0xe4,
|
||||||
0xf2, 0xe2, 0x11, 0x15, 0x17, 0x46, 0x15, 0x15, 0x38, 0x96, 0x32, 0x97, 0x0a, 0x6a, 0xd4, 0x94,
|
0xc5, 0x43, 0x2a, 0x2e, 0x8c, 0x0d, 0x54, 0xe0, 0x58, 0xca, 0x1c, 0x2a, 0xa8, 0x51, 0x55, 0x32,
|
||||||
0x4c, 0x8e, 0x89, 0x09, 0xf5, 0x98, 0x39, 0x9c, 0x09, 0xa3, 0x3e, 0x2c, 0xed, 0xb4, 0xf7, 0x60,
|
0x39, 0x26, 0x26, 0xd4, 0x22, 0x66, 0x73, 0x26, 0x8c, 0xda, 0xb0, 0xb4, 0xd3, 0xda, 0x83, 0x51,
|
||||||
0x14, 0x4d, 0x46, 0x67, 0x28, 0xb1, 0xb4, 0x86, 0xdc, 0x81, 0xaa, 0x8c, 0x8b, 0xd1, 0x40, 0x8b,
|
0x38, 0x1d, 0x9d, 0xa1, 0xc4, 0xd2, 0x1a, 0x72, 0x07, 0x36, 0x64, 0x5c, 0x8c, 0x3a, 0x5a, 0x34,
|
||||||
0xa6, 0xb4, 0xd8, 0x4f, 0xc4, 0x85, 0x85, 0x52, 0xb2, 0x07, 0x0d, 0xf5, 0xa6, 0xb1, 0xd1, 0x1c,
|
0xa4, 0xc5, 0x7e, 0x2c, 0x2e, 0x2c, 0x94, 0x92, 0x3d, 0xa8, 0xab, 0x37, 0x8d, 0x8c, 0xc6, 0xb0,
|
||||||
0x56, 0x76, 0xda, 0x7b, 0x86, 0x34, 0xd0, 0xb7, 0x1c, 0x29, 0x18, 0xc4, 0x87, 0x81, 0xe0, 0x73,
|
0xb2, 0xd3, 0xda, 0x33, 0xa4, 0x81, 0xbe, 0xe5, 0x48, 0xc1, 0x20, 0x3a, 0xf4, 0x05, 0x5f, 0x58,
|
||||||
0x2b, 0x35, 0x24, 0xef, 0x42, 0xc7, 0xf1, 0x3d, 0x16, 0x08, 0x5b, 0x84, 0x97, 0x2c, 0x30, 0x5a,
|
0x89, 0x21, 0x79, 0x17, 0xda, 0xb6, 0xe7, 0x32, 0x5f, 0x4c, 0x44, 0x70, 0xc9, 0x7c, 0xa3, 0x89,
|
||||||
0x78, 0xa2, 0xb6, 0x92, 0x8d, 0xa5, 0x88, 0xec, 0xc1, 0x5b, 0x79, 0x13, 0x9b, 0x3a, 0x0e, 0x8b,
|
0x27, 0x6a, 0x29, 0xd9, 0x58, 0x8a, 0xc8, 0x1e, 0xbc, 0x95, 0x37, 0x99, 0x50, 0xdb, 0x66, 0x51,
|
||||||
0xe3, 0x90, 0x1b, 0x80, 0xb6, 0xb7, 0x72, 0xb6, 0xfb, 0x5a, 0x25, 0xdd, 0xba, 0x5e, 0x1c, 0xf9,
|
0x14, 0x70, 0x03, 0xd0, 0xf6, 0x56, 0xce, 0x76, 0x5f, 0xab, 0xa4, 0x5b, 0xc7, 0x8d, 0x42, 0x8f,
|
||||||
0x74, 0x6e, 0x07, 0x74, 0xc6, 0x8c, 0xb6, 0x72, 0xab, 0x65, 0x5f, 0xd1, 0x19, 0x23, 0x77, 0xa1,
|
0x2e, 0x26, 0x3e, 0x9d, 0x33, 0xa3, 0xa5, 0xdc, 0x6a, 0xd9, 0x57, 0x74, 0xce, 0xc8, 0x5d, 0x68,
|
||||||
0x3d, 0x0b, 0x93, 0x40, 0xd8, 0x51, 0xe8, 0x05, 0xc2, 0xe8, 0xa0, 0x05, 0xa0, 0xe8, 0x54, 0x4a,
|
0xcd, 0x83, 0xd8, 0x17, 0x93, 0x30, 0x70, 0x7d, 0x61, 0xb4, 0xd1, 0x02, 0x50, 0x74, 0x2a, 0x25,
|
||||||
0xc8, 0x3b, 0xa0, 0x66, 0x0a, 0x8c, 0x5d, 0x15, 0x57, 0x94, 0x20, 0x1c, 0xef, 0x41, 0x4f, 0xa9,
|
0xe4, 0x1d, 0x50, 0x33, 0x05, 0xc6, 0x8e, 0x8a, 0x2b, 0x4a, 0x10, 0x8e, 0xf7, 0xa0, 0xab, 0xd4,
|
||||||
0xb3, 0xf3, 0xf4, 0xd0, 0xa4, 0x8b, 0xd2, 0xec, 0x24, 0x4f, 0xa0, 0x85, 0x78, 0xf0, 0x82, 0xf3,
|
0xe9, 0x79, 0xba, 0x68, 0xd2, 0x41, 0x69, 0x7a, 0x92, 0x27, 0xd0, 0x44, 0x3c, 0xb8, 0xfe, 0x79,
|
||||||
0xd0, 0xe8, 0x63, 0xdc, 0x6e, 0xe5, 0xc2, 0x22, 0x31, 0x71, 0x14, 0x9c, 0x87, 0x56, 0xf3, 0x95,
|
0x60, 0xf4, 0x30, 0x6e, 0xb7, 0x72, 0x61, 0x91, 0x98, 0x38, 0xf2, 0xcf, 0x03, 0xab, 0xf1, 0x4a,
|
||||||
0x1e, 0x91, 0x4f, 0xe0, 0xed, 0xc2, 0x7d, 0x39, 0x9b, 0x51, 0x2f, 0xf0, 0x82, 0xa9, 0x9d, 0xc4,
|
0x8f, 0xc8, 0x27, 0xf0, 0x76, 0xe1, 0xbe, 0x9c, 0xcd, 0xa9, 0xeb, 0xbb, 0xfe, 0x6c, 0x12, 0x47,
|
||||||
0x2c, 0x36, 0x36, 0x10, 0xe1, 0x46, 0xee, 0xd6, 0x56, 0x6a, 0xf0, 0x32, 0x66, 0x31, 0x79, 0x1b,
|
0x2c, 0x32, 0x36, 0x11, 0xe1, 0x46, 0xee, 0xd6, 0x56, 0x62, 0xf0, 0x32, 0x62, 0x11, 0x79, 0x1b,
|
||||||
0x5a, 0x2a, 0x41, 0x6d, 0xcf, 0x35, 0x36, 0xf1, 0x48, 0x4d, 0x25, 0x38, 0x72, 0xc9, 0x03, 0xe8,
|
0x9a, 0x2a, 0x41, 0x27, 0xae, 0x63, 0xf4, 0xf1, 0x48, 0x0d, 0x25, 0x38, 0x72, 0xc8, 0x03, 0xe8,
|
||||||
0x47, 0xa1, 0xef, 0x39, 0x73, 0x3b, 0xbc, 0x62, 0x9c, 0x7b, 0x2e, 0x33, 0xc8, 0xb0, 0xb4, 0xd3,
|
0x85, 0x81, 0xe7, 0xda, 0x8b, 0x49, 0x70, 0xc5, 0x38, 0x77, 0x1d, 0x66, 0x90, 0x61, 0x69, 0xa7,
|
||||||
0xb4, 0x7a, 0x4a, 0xfc, 0xb5, 0x96, 0xae, 0x4b, 0x8d, 0x5b, 0x68, 0xb8, 0x92, 0x1a, 0x23, 0x00,
|
0x61, 0x75, 0x95, 0xf8, 0x6b, 0x2d, 0x5d, 0x97, 0x1a, 0xb7, 0xd0, 0x70, 0x25, 0x35, 0x46, 0x00,
|
||||||
0x27, 0x0c, 0x02, 0xe6, 0x20, 0xfc, 0xb6, 0xf0, 0x86, 0x3d, 0x79, 0xc3, 0x83, 0x4c, 0x6a, 0xe5,
|
0x76, 0xe0, 0xfb, 0xcc, 0x46, 0xf8, 0x6d, 0xe1, 0x0d, 0xbb, 0xf2, 0x86, 0x07, 0xa9, 0xd4, 0xca,
|
||||||
0x2c, 0x06, 0x9f, 0x43, 0x27, 0x0f, 0x05, 0xb2, 0x01, 0x95, 0x4b, 0x36, 0xd7, 0xf0, 0x97, 0x43,
|
0x59, 0x0c, 0x3e, 0x87, 0x76, 0x1e, 0x0a, 0x64, 0x13, 0x2a, 0x97, 0x6c, 0xa1, 0xe1, 0x2f, 0x87,
|
||||||
0x32, 0x84, 0xda, 0x15, 0xf5, 0x13, 0x86, 0x90, 0xd7, 0x40, 0x54, 0x4b, 0x2c, 0xa5, 0xf8, 0x59,
|
0x64, 0x08, 0xd5, 0x2b, 0xea, 0xc5, 0x0c, 0x21, 0xaf, 0x81, 0xa8, 0x96, 0x58, 0x4a, 0xf1, 0xb3,
|
||||||
0xf9, 0x59, 0xc9, 0xfc, 0x73, 0x0d, 0xaa, 0x12, 0x7c, 0xe4, 0x43, 0xe8, 0xfa, 0x8c, 0xc6, 0xcc,
|
0xf2, 0xb3, 0x92, 0xf9, 0xd7, 0x2a, 0x6c, 0x48, 0xf0, 0x91, 0x0f, 0xa1, 0xe3, 0x31, 0x1a, 0xb1,
|
||||||
0x0e, 0x23, 0xb9, 0x41, 0x8c, 0xae, 0xda, 0x7b, 0x1b, 0x72, 0xd9, 0xb1, 0x54, 0x7c, 0xad, 0xe4,
|
0x49, 0x10, 0xca, 0x0d, 0x22, 0x74, 0xd5, 0xda, 0xdb, 0x94, 0xcb, 0x8e, 0xa5, 0xe2, 0x6b, 0x25,
|
||||||
0x56, 0xc7, 0xcf, 0xcd, 0x64, 0x4a, 0x7b, 0x81, 0x60, 0x3c, 0xa0, 0xbe, 0x8d, 0xc9, 0xa0, 0x12,
|
0xb7, 0xda, 0x5e, 0x6e, 0x26, 0x53, 0xda, 0xf5, 0x05, 0xe3, 0x3e, 0xf5, 0x26, 0x98, 0x0c, 0x2a,
|
||||||
0xac, 0x93, 0x0a, 0x9f, 0xcb, 0xa4, 0x58, 0xc6, 0x51, 0x65, 0x15, 0x47, 0x03, 0x68, 0x62, 0xec,
|
0xc1, 0xda, 0x89, 0xf0, 0xb9, 0x4c, 0x8a, 0x65, 0x1c, 0x55, 0x56, 0x71, 0x34, 0x80, 0x06, 0xc6,
|
||||||
0x3c, 0x16, 0xeb, 0x64, 0xcf, 0xe6, 0x64, 0x0f, 0x9a, 0x33, 0x26, 0xa8, 0xce, 0x35, 0x99, 0x12,
|
0xce, 0x65, 0x91, 0x4e, 0xf6, 0x74, 0x4e, 0xf6, 0xa0, 0x31, 0x67, 0x82, 0xea, 0x5c, 0x93, 0x29,
|
||||||
0xdb, 0x69, 0xce, 0x8c, 0x4e, 0xb4, 0x42, 0x25, 0x44, 0x66, 0xb7, 0x92, 0x11, 0xf5, 0xd5, 0x8c,
|
0xb1, 0x9d, 0xe4, 0xcc, 0xe8, 0x44, 0x2b, 0x54, 0x42, 0xa4, 0x76, 0x2b, 0x19, 0x51, 0x5b, 0xcd,
|
||||||
0x18, 0x40, 0x33, 0x03, 0x5d, 0x43, 0xbd, 0x70, 0x3a, 0x97, 0x34, 0x1b, 0x31, 0xee, 0x85, 0xae,
|
0x88, 0x01, 0x34, 0x52, 0xd0, 0xd5, 0xd5, 0x0b, 0x27, 0x73, 0x49, 0xb3, 0x21, 0xe3, 0x6e, 0xe0,
|
||||||
0xd1, 0x44, 0xa0, 0xe8, 0x99, 0x24, 0xc9, 0x20, 0x99, 0x29, 0x08, 0xb5, 0x14, 0x49, 0x06, 0xc9,
|
0x18, 0x0d, 0x04, 0x8a, 0x9e, 0x49, 0x92, 0xf4, 0xe3, 0xb9, 0x82, 0x50, 0x53, 0x91, 0xa4, 0x1f,
|
||||||
0x6c, 0x15, 0x31, 0xb0, 0x84, 0x98, 0x1f, 0x41, 0x8d, 0xfa, 0x1e, 0x8d, 0x31, 0x85, 0xe4, 0xcb,
|
0xcf, 0x57, 0x11, 0x03, 0x4b, 0x88, 0xf9, 0x11, 0x54, 0xa9, 0xe7, 0xd2, 0x08, 0x53, 0x48, 0xbe,
|
||||||
0x6a, 0xbe, 0x1f, 0xed, 0x4b, 0xa9, 0xa5, 0x94, 0xe4, 0x03, 0xe8, 0x4e, 0x79, 0x98, 0x44, 0x36,
|
0xac, 0xe6, 0xfb, 0xd1, 0xbe, 0x94, 0x5a, 0x4a, 0x49, 0x3e, 0x80, 0xce, 0x8c, 0x07, 0x71, 0x38,
|
||||||
0x4e, 0x59, 0x6c, 0x74, 0xf0, 0xb6, 0xcb, 0xd6, 0x1d, 0x34, 0xda, 0x57, 0x36, 0x32, 0x03, 0x27,
|
0xc1, 0x29, 0x8b, 0x8c, 0x36, 0xde, 0x76, 0xd9, 0xba, 0x8d, 0x46, 0xfb, 0xca, 0x46, 0x66, 0xe0,
|
||||||
0x61, 0x12, 0xb8, 0xb6, 0xe3, 0xb9, 0x3c, 0x36, 0xba, 0x18, 0x3c, 0x40, 0xd1, 0x81, 0x94, 0xc8,
|
0x34, 0x88, 0x7d, 0x67, 0x62, 0xbb, 0x0e, 0x8f, 0x8c, 0x0e, 0x06, 0x0f, 0x50, 0x74, 0x20, 0x25,
|
||||||
0x14, 0x53, 0x29, 0x90, 0x05, 0xb8, 0x87, 0x36, 0x5d, 0x94, 0x9e, 0xa6, 0x51, 0xfe, 0x31, 0x6c,
|
0x32, 0xc5, 0x54, 0x0a, 0xa4, 0x01, 0xee, 0xa2, 0x4d, 0x07, 0xa5, 0xa7, 0x49, 0x94, 0x7f, 0x0c,
|
||||||
0xa6, 0x45, 0x69, 0x61, 0xd9, 0x47, 0xcb, 0x8d, 0x54, 0x91, 0x19, 0xef, 0xc0, 0x06, 0xbb, 0x96,
|
0xfd, 0xa4, 0x28, 0x65, 0x96, 0x3d, 0xb4, 0xdc, 0x4c, 0x14, 0xa9, 0xf1, 0x0e, 0x6c, 0xb2, 0x6b,
|
||||||
0x14, 0xea, 0x09, 0x7b, 0x46, 0xaf, 0x6d, 0x21, 0x7c, 0x9d, 0x52, 0xbd, 0x54, 0x7e, 0x42, 0xaf,
|
0x49, 0xa1, 0xae, 0x98, 0xcc, 0xe9, 0xf5, 0x44, 0x08, 0x4f, 0xa7, 0x54, 0x37, 0x91, 0x9f, 0xd0,
|
||||||
0xc7, 0xc2, 0x1f, 0xfc, 0x1c, 0xba, 0x85, 0x37, 0x5a, 0x83, 0xd4, 0xad, 0x3c, 0x52, 0x5b, 0x79,
|
0xeb, 0xb1, 0xf0, 0x64, 0xfe, 0xab, 0xdd, 0x31, 0xff, 0xfb, 0x58, 0x8c, 0x9a, 0x28, 0x91, 0xf9,
|
||||||
0x74, 0xfe, 0xb5, 0x0a, 0x80, 0x8f, 0xa5, 0x96, 0x2e, 0x53, 0x7c, 0xfe, 0x05, 0xcb, 0x6b, 0x5e,
|
0x3f, 0xf8, 0x39, 0x74, 0x0a, 0x4f, 0xb8, 0x06, 0xc8, 0x5b, 0x79, 0x20, 0x37, 0xf3, 0xe0, 0xfd,
|
||||||
0x90, 0x72, 0x16, 0x08, 0x8d, 0x36, 0x3d, 0xfb, 0x4e, 0xa0, 0xa5, 0x24, 0x5f, 0xcb, 0x91, 0xfc,
|
0xe7, 0x06, 0x00, 0xbe, 0xa5, 0x5a, 0xba, 0x5c, 0x01, 0xf2, 0x0f, 0x5c, 0x5e, 0xf3, 0xc0, 0x94,
|
||||||
0x23, 0xa8, 0x4a, 0x50, 0x19, 0xf5, 0x05, 0x17, 0x2f, 0x4e, 0x84, 0xf0, 0x53, 0xd0, 0x43, 0xab,
|
0x33, 0x5f, 0x68, 0x30, 0xea, 0xd9, 0x77, 0xe2, 0x30, 0xa9, 0x01, 0xd5, 0x5c, 0x0d, 0x78, 0x04,
|
||||||
0x15, 0xa4, 0x37, 0x56, 0x91, 0x9e, 0x87, 0x50, 0xb3, 0x08, 0xa1, 0xf7, 0xa0, 0xeb, 0x70, 0x86,
|
0x1b, 0x12, 0x73, 0x46, 0x2d, 0xa3, 0xea, 0xec, 0x44, 0x88, 0x4e, 0x85, 0x4c, 0xb4, 0x5a, 0x49,
|
||||||
0x05, 0xc7, 0x96, 0x9d, 0x83, 0x86, 0x58, 0x27, 0x15, 0x8e, 0xbd, 0x19, 0x93, 0xf1, 0x93, 0xd1,
|
0x84, 0xfa, 0x6a, 0x22, 0xe4, 0x11, 0xd6, 0x28, 0x22, 0xec, 0x3d, 0xe8, 0xd8, 0x9c, 0x61, 0x3d,
|
||||||
0x06, 0x54, 0xc9, 0xe1, 0xda, 0xc7, 0x68, 0xaf, 0x7b, 0x0c, 0x55, 0xbe, 0x7d, 0xa6, 0x69, 0x1a,
|
0x9a, 0xc8, 0xc6, 0x42, 0x23, 0xb0, 0x9d, 0x08, 0xc7, 0xee, 0x9c, 0xc9, 0xf8, 0xc9, 0xc7, 0x00,
|
||||||
0xc7, 0x39, 0xa8, 0x77, 0x0b, 0x50, 0x2f, 0xe0, 0xb9, 0xb7, 0x84, 0xe7, 0x25, 0xd0, 0xf5, 0x57,
|
0x54, 0xc9, 0xe1, 0xda, 0xb7, 0x6a, 0xad, 0x7d, 0x2b, 0xac, 0xee, 0x1e, 0xd3, 0x2c, 0x8e, 0xe3,
|
||||||
0x40, 0xf7, 0x2e, 0x74, 0x64, 0x00, 0xe2, 0x88, 0x3a, 0x4c, 0x3a, 0xd8, 0x50, 0x81, 0xc8, 0x64,
|
0x5c, 0x26, 0x74, 0x0a, 0x99, 0x50, 0x80, 0x7b, 0x77, 0x09, 0xee, 0x4b, 0x98, 0xec, 0xad, 0x60,
|
||||||
0x47, 0x2e, 0xa6, 0x68, 0x32, 0x99, 0xcc, 0x2f, 0x42, 0x9f, 0x2d, 0x58, 0xb6, 0x9d, 0xc9, 0x8e,
|
0xf2, 0x5d, 0x68, 0xcb, 0x00, 0x44, 0x21, 0xb5, 0x99, 0x74, 0xb0, 0xa9, 0x02, 0x91, 0xca, 0x8e,
|
||||||
0xdc, 0xc1, 0x47, 0xd0, 0xca, 0x22, 0xfc, 0xbd, 0x80, 0xf3, 0x97, 0x12, 0x74, 0xf2, 0xac, 0x25,
|
0x1c, 0xcc, 0xe0, 0x78, 0x3a, 0x5d, 0x5c, 0x04, 0x1e, 0xcb, 0x48, 0xb8, 0x95, 0xca, 0x8e, 0x1c,
|
||||||
0x17, 0x8f, 0xc7, 0xc7, 0xb8, 0xb8, 0x62, 0xc9, 0xa1, 0xac, 0xf7, 0x9c, 0x05, 0xec, 0x15, 0x9d,
|
0x79, 0x5e, 0x44, 0x15, 0x41, 0x54, 0xe1, 0x78, 0xf0, 0x11, 0x34, 0xd3, 0xa8, 0x7f, 0x2f, 0x30,
|
||||||
0xf8, 0xca, 0x41, 0xd3, 0x5a, 0x08, 0xa4, 0xd6, 0x0b, 0x1c, 0xce, 0x66, 0x29, 0x82, 0x2a, 0xd6,
|
0xfd, 0xb9, 0x04, 0xed, 0x3c, 0xd1, 0xc9, 0xc5, 0xe3, 0xf1, 0x31, 0x2e, 0xae, 0x58, 0x72, 0x28,
|
||||||
0x42, 0x40, 0x3e, 0x06, 0xf0, 0xe2, 0x38, 0x61, 0xea, 0x95, 0xaa, 0x98, 0xd3, 0x83, 0x91, 0x6a,
|
0x5b, 0x04, 0xce, 0x7c, 0xf6, 0x8a, 0x4e, 0x3d, 0xe5, 0xa0, 0x61, 0x65, 0x02, 0xa9, 0x75, 0x7d,
|
||||||
0xfe, 0x46, 0x69, 0xf3, 0x37, 0x1a, 0xa7, 0xcd, 0x9f, 0xd5, 0x42, 0x6b, 0x7c, 0xbe, 0x6d, 0xa8,
|
0x9b, 0xb3, 0x79, 0x82, 0xaa, 0x8a, 0x95, 0x09, 0xc8, 0xc7, 0x00, 0x6e, 0x14, 0xc5, 0x4c, 0xbd,
|
||||||
0xcb, 0xc7, 0x18, 0x1f, 0x23, 0xca, 0x2a, 0x96, 0x9e, 0x99, 0x7f, 0x84, 0xba, 0x6a, 0x13, 0xfe,
|
0xdc, 0x06, 0xd2, 0xc0, 0x60, 0xa4, 0xfa, 0xc5, 0x51, 0xd2, 0x2f, 0x8e, 0xc6, 0x49, 0xbf, 0x68,
|
||||||
0xaf, 0x4c, 0x7c, 0x1b, 0x9a, 0xca, 0xb7, 0xe7, 0xea, 0xbc, 0x68, 0xe0, 0xfc, 0xc8, 0x35, 0xff,
|
0x35, 0xd1, 0x1a, 0x9f, 0x74, 0x1b, 0x6a, 0xf2, 0x81, 0xc6, 0xc7, 0x88, 0xbc, 0x8a, 0xa5, 0x67,
|
||||||
0x55, 0x82, 0xa6, 0xc5, 0xe2, 0x28, 0x0c, 0x62, 0x96, 0x6b, 0x63, 0x4a, 0xaf, 0x6d, 0x63, 0xca,
|
0xe6, 0x1f, 0xa1, 0xa6, 0x3a, 0x8b, 0xff, 0x2b, 0x79, 0xdf, 0x86, 0x86, 0xf2, 0xed, 0x3a, 0x3a,
|
||||||
0x6b, 0xdb, 0x98, 0xb4, 0x39, 0xaa, 0xe4, 0x9a, 0xa3, 0x01, 0x34, 0x39, 0x73, 0x3d, 0xce, 0x1c,
|
0x57, 0xea, 0x38, 0x3f, 0x72, 0xcc, 0x7f, 0x95, 0xa0, 0x61, 0xb1, 0x28, 0x0c, 0xfc, 0x88, 0xe5,
|
||||||
0xa1, 0x1b, 0xa9, 0x6c, 0x2e, 0x75, 0xaf, 0x28, 0x97, 0xf5, 0x37, 0x46, 0x92, 0x6f, 0x59, 0xd9,
|
0x3a, 0x9f, 0xd2, 0x6b, 0x3b, 0x9f, 0xf2, 0xda, 0xce, 0x27, 0xe9, 0xa7, 0x2a, 0xb9, 0x7e, 0x6a,
|
||||||
0x9c, 0x3c, 0xcd, 0x57, 0x7f, 0xd5, 0x57, 0x6d, 0xa9, 0xea, 0xaf, 0x8e, 0xbb, 0x5a, 0xfe, 0xcd,
|
0x00, 0x0d, 0xce, 0x1c, 0x97, 0x33, 0x5b, 0xe8, 0xde, 0x2b, 0x9d, 0x4b, 0xdd, 0x2b, 0xca, 0x65,
|
||||||
0x7f, 0x96, 0x61, 0x63, 0x59, 0xbd, 0x06, 0x04, 0x5b, 0x50, 0x53, 0xf5, 0x41, 0x23, 0x48, 0xac,
|
0xc9, 0x8e, 0xb0, 0x2e, 0x34, 0xad, 0x74, 0x4e, 0x9e, 0xe6, 0x1b, 0x06, 0xd5, 0x8a, 0x6d, 0xa9,
|
||||||
0x54, 0x86, 0xca, 0x12, 0xaf, 0xfc, 0x62, 0x39, 0x47, 0x5f, 0xff, 0xfa, 0xc5, 0xfc, 0x7d, 0x1f,
|
0x86, 0x41, 0x1d, 0x77, 0xb5, 0x63, 0x30, 0xff, 0x51, 0x86, 0xcd, 0x65, 0xf5, 0x1a, 0x10, 0x6c,
|
||||||
0x36, 0xe4, 0x29, 0x23, 0xe6, 0x2e, 0x7a, 0x1e, 0x45, 0x38, 0x7d, 0x2d, 0xcf, 0xba, 0x9e, 0x87,
|
0x41, 0x55, 0x95, 0x14, 0x8d, 0x20, 0xb1, 0x52, 0x4c, 0x2a, 0x4b, 0x5c, 0xf3, 0x8b, 0xe5, 0xbc,
|
||||||
0xb0, 0x99, 0x9a, 0x2e, 0x52, 0xb1, 0x5e, 0xb0, 0x3d, 0x4c, 0x33, 0x72, 0x1b, 0xea, 0xe7, 0x21,
|
0x7d, 0xfd, 0xeb, 0x17, 0x73, 0xfa, 0x7d, 0xd8, 0x94, 0xa7, 0x0c, 0x99, 0x93, 0xb5, 0x49, 0x8a,
|
||||||
0x9f, 0x51, 0xa1, 0x39, 0x47, 0xcf, 0x0a, 0x9c, 0x82, 0xe4, 0xd6, 0x54, 0xb0, 0x48, 0x85, 0xb2,
|
0x84, 0x7a, 0x5a, 0x9e, 0x36, 0x4a, 0x0f, 0xa1, 0x9f, 0x98, 0x66, 0xe9, 0x59, 0x2b, 0xd8, 0x1e,
|
||||||
0xaf, 0x97, 0xb9, 0x9e, 0xf5, 0xdc, 0x48, 0x3a, 0x4d, 0xab, 0x99, 0xf6, 0xda, 0xe6, 0xaf, 0xa1,
|
0x26, 0x59, 0xba, 0x0d, 0xb5, 0xf3, 0x80, 0xcf, 0xa9, 0xd0, 0x3c, 0xa4, 0x67, 0x05, 0x9e, 0x41,
|
||||||
0xbf, 0xd4, 0x66, 0xad, 0x09, 0xe4, 0x62, 0xfb, 0x72, 0x61, 0xfb, 0x82, 0xe7, 0xca, 0x92, 0xe7,
|
0xc2, 0x6b, 0x28, 0x58, 0x24, 0x42, 0xf9, 0x29, 0x20, 0xf3, 0x3f, 0x6d, 0xd3, 0x91, 0x88, 0x1a,
|
||||||
0xdf, 0xc0, 0xe6, 0x17, 0x34, 0x70, 0x7d, 0xa6, 0xfd, 0xef, 0xf3, 0x69, 0x2c, 0x1b, 0x46, 0xdd,
|
0x56, 0x23, 0x69, 0xcf, 0xcd, 0x5f, 0x43, 0x6f, 0xa9, 0x33, 0x5b, 0x13, 0xc8, 0x6c, 0xfb, 0x72,
|
||||||
0xf5, 0xdb, 0x9a, 0xec, 0xbb, 0x56, 0x4b, 0x4b, 0x8e, 0x5c, 0x72, 0x0f, 0x1a, 0x5c, 0x59, 0x6b,
|
0x61, 0xfb, 0x82, 0xe7, 0xca, 0x92, 0xe7, 0xdf, 0x40, 0xff, 0x0b, 0xea, 0x3b, 0x1e, 0xd3, 0xfe,
|
||||||
0xe0, 0xb5, 0x73, 0x7d, 0xa0, 0x95, 0xea, 0xcc, 0x6f, 0x81, 0x14, 0x5c, 0xcb, 0x86, 0x7f, 0x4e,
|
0xf7, 0xf9, 0x2c, 0x92, 0x35, 0x46, 0x7f, 0x28, 0x4c, 0x74, 0x01, 0xe8, 0x58, 0x4d, 0x2d, 0x39,
|
||||||
0x76, 0x24, 0x00, 0x15, 0x28, 0x34, 0xb0, 0x3b, 0x79, 0x1c, 0x59, 0x99, 0x96, 0x0c, 0xa1, 0xc2,
|
0x72, 0xc8, 0x3d, 0xa8, 0x73, 0x65, 0xad, 0x81, 0xd7, 0xca, 0xb5, 0x8e, 0x56, 0xa2, 0x33, 0xbf,
|
||||||
0x38, 0xd7, 0x5b, 0x60, 0x23, 0xb6, 0xf8, 0xbc, 0xb2, 0xa4, 0xca, 0xfc, 0x09, 0x6c, 0x9e, 0x45,
|
0x05, 0x52, 0x70, 0x2d, 0xbf, 0x11, 0x16, 0x64, 0x47, 0x02, 0x50, 0x81, 0x42, 0x03, 0xbb, 0x9d,
|
||||||
0xcc, 0xf1, 0xa8, 0x8f, 0x9f, 0x46, 0x6a, 0x83, 0xbb, 0x50, 0x93, 0x41, 0x4e, 0x73, 0xb6, 0x85,
|
0xc7, 0x91, 0x95, 0x6a, 0xc9, 0x10, 0x2a, 0x8c, 0x73, 0xbd, 0x05, 0xf6, 0x6e, 0xd9, 0x17, 0x99,
|
||||||
0x0b, 0x51, 0xad, 0xe4, 0xe6, 0xb7, 0x60, 0xa8, 0x73, 0x1d, 0x5e, 0x7b, 0xb1, 0x60, 0x81, 0xc3,
|
0x25, 0x55, 0xe6, 0x4f, 0xa0, 0x7f, 0x16, 0x32, 0xdb, 0xa5, 0x1e, 0x7e, 0x4d, 0xa9, 0x0d, 0xee,
|
||||||
0x0e, 0x2e, 0x98, 0x73, 0xf9, 0x3f, 0xbc, 0xf9, 0x15, 0xdc, 0x5e, 0xb7, 0x43, 0x7a, 0xbe, 0xb6,
|
0x42, 0x55, 0x06, 0x39, 0xc9, 0xd9, 0x26, 0x2e, 0x44, 0xb5, 0x92, 0x9b, 0xdf, 0x82, 0xa1, 0xce,
|
||||||
0x23, 0x67, 0xf6, 0xb9, 0xa4, 0x6a, 0xdc, 0xa3, 0x69, 0x01, 0x8a, 0x3e, 0x97, 0x12, 0xf9, 0x8e,
|
0x75, 0x78, 0xed, 0x46, 0x82, 0xf9, 0x36, 0x3b, 0xb8, 0x60, 0xf6, 0xe5, 0xff, 0xf0, 0xe6, 0x57,
|
||||||
0x4c, 0xae, 0x8b, 0x35, 0x25, 0xea, 0x59, 0x1a, 0x8f, 0xca, 0xcd, 0xf1, 0xf8, 0x5b, 0x09, 0x5a,
|
0x70, 0x7b, 0xdd, 0x0e, 0xc9, 0xf9, 0x5a, 0xb6, 0x9c, 0x4d, 0xce, 0x25, 0x7d, 0xe3, 0x1e, 0x0d,
|
||||||
0x67, 0x4c, 0x24, 0x11, 0xde, 0xe5, 0x6d, 0x68, 0x4d, 0x78, 0x78, 0xc9, 0xf8, 0xe2, 0x2a, 0x4d,
|
0x0b, 0x50, 0xf4, 0xb9, 0x94, 0xc8, 0x77, 0x64, 0x72, 0x5d, 0xa4, 0x29, 0x51, 0xcf, 0x92, 0x78,
|
||||||
0x25, 0x38, 0x72, 0xc9, 0x53, 0xa8, 0x1f, 0x84, 0xc1, 0xb9, 0x37, 0xc5, 0x0f, 0xc5, 0xf6, 0xde,
|
0x54, 0x6e, 0x8e, 0xc7, 0x5f, 0x4a, 0xd0, 0x3c, 0x63, 0x22, 0x0e, 0xf1, 0x2e, 0x6f, 0x43, 0x73,
|
||||||
0x6d, 0xc5, 0x2e, 0x7a, 0xed, 0x48, 0xe9, 0x54, 0x59, 0xd5, 0x86, 0x64, 0x08, 0x6d, 0xfd, 0xb9,
|
0xca, 0x83, 0x4b, 0xc6, 0xb3, 0xab, 0x34, 0x94, 0xe0, 0xc8, 0x21, 0x4f, 0xa1, 0x76, 0x10, 0xf8,
|
||||||
0xfd, 0xf2, 0xe5, 0xd1, 0xf3, 0xb4, 0x83, 0xcc, 0x89, 0x06, 0x1f, 0x43, 0x3b, 0xb7, 0xf0, 0x7b,
|
0xe7, 0xee, 0x0c, 0xbf, 0x2d, 0x5b, 0x7b, 0xb7, 0x15, 0xbb, 0xe8, 0xb5, 0x23, 0xa5, 0x53, 0xa5,
|
||||||
0x55, 0x8b, 0x1f, 0x02, 0xe0, 0xee, 0x2a, 0x46, 0x1b, 0xea, 0xaa, 0x7a, 0xa5, 0xbc, 0xda, 0x5d,
|
0x56, 0x1b, 0x92, 0x21, 0xb4, 0xf4, 0x17, 0xfa, 0xcb, 0x97, 0x47, 0xcf, 0x93, 0xa6, 0x33, 0x27,
|
||||||
0x68, 0xc9, 0x8f, 0x15, 0xa5, 0x26, 0x50, 0xcd, 0x7d, 0x57, 0xe3, 0xd8, 0xbc, 0x07, 0x9b, 0x47,
|
0x1a, 0x7c, 0x0c, 0xad, 0xdc, 0xc2, 0xef, 0x55, 0x2d, 0x7e, 0x08, 0x80, 0xbb, 0xab, 0x18, 0x6d,
|
||||||
0xc1, 0x15, 0xf5, 0x3d, 0x97, 0x0a, 0xf6, 0x25, 0x9b, 0x63, 0x08, 0x56, 0x4e, 0x60, 0x9e, 0x41,
|
0xaa, 0xab, 0xea, 0x95, 0xf2, 0x6a, 0x77, 0xa1, 0x29, 0xfb, 0x1b, 0xa5, 0x4e, 0xea, 0x54, 0x29,
|
||||||
0x47, 0x7f, 0xb9, 0xbe, 0xd1, 0x19, 0x3b, 0xfa, 0x8c, 0xdf, 0x9d, 0x44, 0xef, 0x43, 0x5f, 0x3b,
|
0xab, 0x53, 0xe6, 0x3d, 0xe8, 0x1f, 0xf9, 0x57, 0xd4, 0x73, 0x1d, 0x2a, 0xd8, 0x97, 0x6c, 0x81,
|
||||||
0x3d, 0xf6, 0x74, 0x0a, 0xc9, 0x92, 0xce, 0xd9, 0xb9, 0x77, 0xad, 0x5d, 0xeb, 0x99, 0xf9, 0x0c,
|
0x21, 0x58, 0x39, 0x81, 0x79, 0x06, 0x6d, 0xfd, 0xb1, 0xfb, 0x46, 0x67, 0x6c, 0xeb, 0x33, 0x7e,
|
||||||
0x36, 0x72, 0xa6, 0xd9, 0x75, 0x2e, 0xd9, 0x3c, 0x4e, 0xbf, 0xe8, 0xe5, 0x38, 0x8d, 0x40, 0x79,
|
0x77, 0x12, 0xbd, 0x0f, 0x3d, 0xed, 0xf4, 0xd8, 0xd5, 0x29, 0x24, 0xcb, 0x3c, 0x67, 0xe7, 0xee,
|
||||||
0x11, 0x01, 0x13, 0x7a, 0x7a, 0xe5, 0x0b, 0x26, 0x6e, 0xb8, 0xdd, 0x97, 0xd9, 0x41, 0x5e, 0x30,
|
0xb5, 0x76, 0xad, 0x67, 0xe6, 0x33, 0xd8, 0xcc, 0x99, 0xa6, 0xd7, 0xb9, 0x64, 0x8b, 0x28, 0xf9,
|
||||||
0xed, 0xfc, 0x3e, 0xd4, 0x98, 0xbc, 0x69, 0xbe, 0x84, 0xe5, 0x23, 0x60, 0x29, 0xf5, 0x9a, 0x0d,
|
0x11, 0x40, 0x8e, 0x93, 0x08, 0x94, 0xb3, 0x08, 0x98, 0xd0, 0xd5, 0x2b, 0x5f, 0x30, 0x71, 0xc3,
|
||||||
0x9f, 0x65, 0x1b, 0x9e, 0x26, 0x6a, 0xc3, 0x37, 0xf4, 0x65, 0xbe, 0x97, 0x1d, 0xe3, 0x34, 0x11,
|
0xed, 0xbe, 0x4c, 0x0f, 0xf2, 0x82, 0x69, 0xe7, 0xf7, 0xa1, 0xca, 0xe4, 0x4d, 0xf3, 0x25, 0x2c,
|
||||||
0x37, 0xbd, 0xe8, 0x3d, 0xd8, 0xd4, 0x46, 0xcf, 0x99, 0xcf, 0x04, 0xbb, 0xe1, 0x4a, 0xf7, 0x81,
|
0x1f, 0x01, 0x4b, 0xa9, 0xd7, 0x6c, 0xf8, 0x2c, 0xdd, 0xf0, 0x34, 0x56, 0x1b, 0xbe, 0xa1, 0x2f,
|
||||||
0x14, 0xcc, 0x6e, 0x72, 0x77, 0x07, 0x9a, 0xe3, 0xf1, 0x71, 0xa6, 0x2d, 0x72, 0xa3, 0xf9, 0x09,
|
0xf3, 0xbd, 0xf4, 0x18, 0xa7, 0xb1, 0xb8, 0xe9, 0x45, 0xef, 0x41, 0x5f, 0x1b, 0x3d, 0x67, 0x1e,
|
||||||
0x6c, 0x9e, 0x25, 0x6e, 0x78, 0xca, 0xbd, 0x2b, 0xcf, 0x67, 0x53, 0xb5, 0x59, 0xda, 0x6b, 0x96,
|
0x13, 0xec, 0x86, 0x2b, 0xdd, 0x07, 0x52, 0x30, 0xbb, 0xc9, 0xdd, 0x1d, 0x68, 0x8c, 0xc7, 0xc7,
|
||||||
0x72, 0xbd, 0xe6, 0xda, 0x6a, 0x64, 0xee, 0x00, 0x29, 0x2c, 0xcf, 0xde, 0x2d, 0x4e, 0xdc, 0x50,
|
0xa9, 0xb6, 0xc8, 0x8d, 0xe6, 0x27, 0xd0, 0x3f, 0x8b, 0x9d, 0xe0, 0x94, 0xbb, 0x57, 0xae, 0xc7,
|
||||||
0xa7, 0x30, 0x8e, 0xcd, 0x1d, 0xe8, 0x8c, 0xa9, 0xac, 0xf7, 0xae, 0xb2, 0x31, 0xa0, 0x21, 0xd4,
|
0x66, 0x6a, 0xb3, 0xa4, 0xff, 0x2c, 0xe5, 0xfa, 0xcf, 0xb5, 0xd5, 0xc8, 0xdc, 0x01, 0x52, 0x58,
|
||||||
0x5c, 0x9b, 0xa5, 0x53, 0x73, 0x0f, 0xb6, 0x0e, 0xa8, 0x73, 0xe1, 0x05, 0xd3, 0xe7, 0x5e, 0x2c,
|
0x9e, 0xbe, 0x5b, 0x14, 0x3b, 0x81, 0x4e, 0x61, 0x1c, 0x9b, 0x3b, 0xd0, 0x1e, 0x53, 0x59, 0xef,
|
||||||
0x1b, 0x1e, 0xbd, 0x62, 0x00, 0x4d, 0x57, 0x0b, 0xf4, 0x92, 0x6c, 0x6e, 0x3e, 0x86, 0xb7, 0x72,
|
0x1d, 0x65, 0x63, 0x40, 0x5d, 0xa8, 0xb9, 0x36, 0x4b, 0xa6, 0xe6, 0x1e, 0x6c, 0x1d, 0x50, 0xfb,
|
||||||
0x3f, 0x9b, 0x9c, 0x09, 0x9a, 0xc6, 0x63, 0x0b, 0x6a, 0xb1, 0x9c, 0xe1, 0x8a, 0x9a, 0xa5, 0x26,
|
0xc2, 0xf5, 0x67, 0xcf, 0xdd, 0x48, 0x36, 0x3c, 0x7a, 0xc5, 0x00, 0x1a, 0x8e, 0x16, 0xe8, 0x25,
|
||||||
0xe6, 0x57, 0xb0, 0x95, 0x2f, 0xc0, 0xb2, 0xfd, 0x48, 0x2f, 0x8e, 0x8d, 0x41, 0x29, 0xd7, 0x18,
|
0xe9, 0xdc, 0x7c, 0x0c, 0x6f, 0xe5, 0x7e, 0x69, 0x39, 0x13, 0x34, 0x89, 0xc7, 0x16, 0x54, 0x23,
|
||||||
0xe8, 0x98, 0x95, 0x17, 0xf5, 0x64, 0x03, 0x2a, 0xbf, 0xfc, 0x66, 0xac, 0xc1, 0x2e, 0x87, 0xe6,
|
0x39, 0xc3, 0x15, 0x55, 0x4b, 0x4d, 0xcc, 0xaf, 0x60, 0x2b, 0x5f, 0x80, 0x65, 0xfb, 0x91, 0x5c,
|
||||||
0xef, 0xe5, 0xf6, 0x45, 0x7f, 0x6a, 0xfb, 0x42, 0x77, 0x50, 0x7a, 0x93, 0xee, 0x60, 0x0d, 0xde,
|
0x1c, 0x1b, 0x83, 0x52, 0xae, 0x31, 0xd0, 0x31, 0x2b, 0x67, 0xf5, 0x64, 0x13, 0x2a, 0xbf, 0xfc,
|
||||||
0x1e, 0xc3, 0xe6, 0x89, 0x1f, 0x3a, 0x97, 0x87, 0x41, 0x2e, 0x1a, 0x06, 0x34, 0x58, 0x90, 0x0f,
|
0x66, 0xac, 0xc1, 0x2e, 0x87, 0xe6, 0xef, 0xe5, 0xf6, 0x45, 0x7f, 0x6a, 0xfb, 0x42, 0x77, 0x50,
|
||||||
0x46, 0x3a, 0x35, 0x1f, 0x40, 0xff, 0x38, 0x74, 0xa8, 0x7f, 0x12, 0x26, 0x81, 0xc8, 0xa2, 0x80,
|
0x7a, 0x93, 0xee, 0x60, 0x0d, 0xde, 0x1e, 0x43, 0xff, 0xc4, 0x0b, 0xec, 0xcb, 0x43, 0x3f, 0x17,
|
||||||
0xbf, 0x63, 0x69, 0x53, 0x35, 0x31, 0x1f, 0x43, 0x4f, 0x97, 0xe8, 0xe0, 0x3c, 0x4c, 0x99, 0x71,
|
0x0d, 0x03, 0xea, 0xcc, 0xcf, 0x07, 0x23, 0x99, 0x9a, 0x0f, 0xa0, 0x77, 0x1c, 0xd8, 0xd4, 0x3b,
|
||||||
0x51, 0xcc, 0x4b, 0xc5, 0xbe, 0xda, 0x3c, 0x86, 0xfe, 0xc2, 0x5c, 0xf9, 0x7d, 0x00, 0x75, 0xa5,
|
0x09, 0x62, 0x5f, 0xa4, 0x51, 0xc0, 0x9f, 0xbe, 0xb4, 0xa9, 0x9a, 0x98, 0x8f, 0xa1, 0xab, 0x4b,
|
||||||
0xd6, 0x77, 0xeb, 0x67, 0x5f, 0x83, 0xca, 0xd2, 0xd2, 0xea, 0x35, 0x97, 0x9a, 0x41, 0xef, 0x14,
|
0xb4, 0x7f, 0x1e, 0x24, 0xcc, 0x98, 0x15, 0xf3, 0x52, 0xb1, 0xd7, 0x36, 0x8f, 0xa1, 0x97, 0x99,
|
||||||
0x7f, 0x4f, 0x3c, 0x0c, 0xae, 0x94, 0xb3, 0x23, 0x20, 0xea, 0x17, 0x46, 0x9b, 0x05, 0x57, 0x1e,
|
0x2b, 0xbf, 0x0f, 0xa0, 0xa6, 0xd4, 0xfa, 0x6e, 0xbd, 0xf4, 0x03, 0x52, 0x59, 0x5a, 0x5a, 0xbd,
|
||||||
0x0f, 0x03, 0xec, 0x6f, 0x4b, 0xba, 0x85, 0x49, 0x1d, 0x67, 0x8b, 0x52, 0x0b, 0x6b, 0x33, 0x5a,
|
0xe6, 0x52, 0x73, 0xe8, 0x9e, 0xe2, 0x4f, 0x90, 0x87, 0xfe, 0x95, 0x72, 0x76, 0x04, 0x44, 0xfd,
|
||||||
0x16, 0xad, 0x8d, 0x21, 0x2c, 0x7e, 0xad, 0x90, 0xa5, 0x86, 0xb3, 0x59, 0x28, 0x98, 0x4d, 0x5d,
|
0x28, 0x39, 0x61, 0xfe, 0x95, 0xcb, 0x03, 0x1f, 0xfb, 0xdb, 0x92, 0x6e, 0x61, 0x12, 0xc7, 0xe9,
|
||||||
0x37, 0xcd, 0x16, 0x50, 0xa2, 0x7d, 0xd7, 0xe5, 0x7b, 0xff, 0x29, 0x43, 0xe3, 0x33, 0x45, 0xe0,
|
0xa2, 0xc4, 0xc2, 0xea, 0x87, 0xcb, 0xa2, 0xb5, 0x31, 0x84, 0xec, 0x07, 0x0e, 0x59, 0x6a, 0x38,
|
||||||
0xe4, 0x53, 0xe8, 0x16, 0xca, 0x35, 0x79, 0x0b, 0x7f, 0xae, 0x58, 0x6e, 0x0e, 0x06, 0xdb, 0x2b,
|
0x9b, 0x07, 0x82, 0x4d, 0xa8, 0xe3, 0x24, 0xd9, 0x02, 0x4a, 0xb4, 0xef, 0x38, 0x7c, 0xef, 0x3f,
|
||||||
0x62, 0x75, 0xaf, 0x27, 0xd0, 0xc9, 0x17, 0x63, 0x82, 0x85, 0x17, 0x7f, 0x3b, 0x1d, 0xa0, 0xa7,
|
0x65, 0xa8, 0x7f, 0xa6, 0x08, 0x9c, 0x7c, 0x0a, 0x9d, 0x42, 0xb9, 0x26, 0x6f, 0xe1, 0x2f, 0x1c,
|
||||||
0xd5, 0x4a, 0x7d, 0x06, 0x5b, 0xeb, 0xca, 0x24, 0xb9, 0xb3, 0xd8, 0x61, 0xb5, 0x44, 0x0f, 0xde,
|
0xcb, 0xcd, 0xc1, 0x60, 0x7b, 0x45, 0xac, 0xee, 0xf5, 0x04, 0xda, 0xf9, 0x62, 0x4c, 0xb0, 0xf0,
|
||||||
0xb9, 0x49, 0x9b, 0x96, 0xd7, 0xc6, 0x81, 0xcf, 0x68, 0x90, 0x44, 0xf9, 0x13, 0x2c, 0x86, 0xe4,
|
0xe2, 0xcf, 0xad, 0x03, 0xf4, 0xb4, 0x5a, 0xa9, 0xcf, 0x60, 0x6b, 0x5d, 0x99, 0x24, 0x77, 0xb2,
|
||||||
0x29, 0x74, 0x0b, 0x85, 0x42, 0xdd, 0x73, 0xa5, 0x76, 0xe4, 0x97, 0xdc, 0x87, 0x1a, 0x16, 0x27,
|
0x1d, 0x56, 0x4b, 0xf4, 0xe0, 0x9d, 0x9b, 0xb4, 0x49, 0x79, 0xad, 0x1f, 0x78, 0x8c, 0xfa, 0x71,
|
||||||
0xd2, 0x2d, 0x54, 0xc9, 0x41, 0x2f, 0x9b, 0xaa, 0xbd, 0x87, 0x50, 0xc5, 0x5f, 0xd4, 0x72, 0x1b,
|
0x98, 0x3f, 0x41, 0x36, 0x24, 0x4f, 0xa1, 0x53, 0x28, 0x14, 0xea, 0x9e, 0x2b, 0xb5, 0x23, 0xbf,
|
||||||
0xe3, 0x8a, 0xac, 0x72, 0xed, 0xfd, 0xbb, 0x04, 0x8d, 0xf4, 0x57, 0xd6, 0xa7, 0x50, 0x95, 0x35,
|
0xe4, 0x3e, 0x54, 0xb1, 0x38, 0x91, 0x4e, 0xa1, 0x4a, 0x0e, 0xba, 0xe9, 0x54, 0xed, 0x3d, 0x84,
|
||||||
0x80, 0xdc, 0xca, 0xd1, 0x68, 0x5a, 0x3f, 0x06, 0x5b, 0x4b, 0x42, 0xb5, 0xc1, 0x08, 0x2a, 0x2f,
|
0x0d, 0xfc, 0x11, 0x2e, 0xb7, 0x31, 0xae, 0x48, 0x2b, 0xd7, 0xde, 0xbf, 0x4b, 0x50, 0x4f, 0x7e,
|
||||||
0x98, 0x20, 0x24, 0xa7, 0xd4, 0xc5, 0x60, 0x70, 0xab, 0x28, 0xcb, 0xec, 0x4f, 0x93, 0xa2, 0xbd,
|
0x98, 0x7d, 0x0a, 0x1b, 0xb2, 0x06, 0x90, 0x5b, 0x39, 0x1a, 0x4d, 0xea, 0xc7, 0x60, 0x6b, 0x49,
|
||||||
0xe6, 0xf2, 0x82, 0x7d, 0xc6, 0xd2, 0x1f, 0x41, 0x5d, 0xb1, 0xac, 0x0a, 0xca, 0x0a, 0x3f, 0xab,
|
0xa8, 0x36, 0x18, 0x41, 0xe5, 0x05, 0x13, 0x84, 0xe4, 0x94, 0xba, 0x18, 0x0c, 0x6e, 0x15, 0x65,
|
||||||
0xc7, 0x5f, 0xe5, 0xe3, 0xbd, 0x7f, 0x54, 0x01, 0xce, 0xe6, 0xb1, 0x60, 0xb3, 0x5f, 0x79, 0xec,
|
0xa9, 0xfd, 0x69, 0x5c, 0xb4, 0xd7, 0x5c, 0x5e, 0xb0, 0x4f, 0x59, 0xfa, 0x23, 0xa8, 0x29, 0x96,
|
||||||
0x15, 0x79, 0x08, 0xfd, 0xe7, 0xec, 0x9c, 0x26, 0xbe, 0xc0, 0xaf, 0x25, 0xc9, 0x26, 0xb9, 0x98,
|
0x55, 0x41, 0x59, 0xe1, 0x67, 0xf5, 0xf8, 0xab, 0x7c, 0xbc, 0xf7, 0xf7, 0x0d, 0x80, 0xb3, 0x45,
|
||||||
0x60, 0xc3, 0x97, 0x91, 0xf5, 0x7d, 0x68, 0x9f, 0xd0, 0xeb, 0xd7, 0xdb, 0x7d, 0x0a, 0xdd, 0x02,
|
0x24, 0xd8, 0xfc, 0x57, 0x2e, 0x7b, 0x45, 0x1e, 0x42, 0xef, 0x39, 0x3b, 0xa7, 0xb1, 0x27, 0xf0,
|
||||||
0x07, 0xeb, 0x23, 0x2e, 0xb3, 0xba, 0x3e, 0xe2, 0x2a, 0x5b, 0xdf, 0x87, 0x86, 0x66, 0xe6, 0xfc,
|
0x6b, 0x49, 0xb2, 0x49, 0x2e, 0x26, 0xd8, 0xf0, 0xa5, 0x64, 0x7d, 0x1f, 0x5a, 0x27, 0xf4, 0xfa,
|
||||||
0x1e, 0x58, 0xc3, 0x0a, 0x8c, 0xfd, 0x53, 0xe8, 0x2f, 0xf1, 0x72, 0xde, 0x1e, 0x7f, 0x7d, 0x58,
|
0xf5, 0x76, 0x9f, 0x42, 0xa7, 0xc0, 0xc1, 0xfa, 0x88, 0xcb, 0xac, 0xae, 0x8f, 0xb8, 0xca, 0xd6,
|
||||||
0xcb, 0xdb, 0xcf, 0xe4, 0xd7, 0x4e, 0x91, 0x9b, 0xf3, 0x0b, 0x6f, 0x2b, 0x3e, 0x5c, 0x47, 0xde,
|
0xf7, 0xa1, 0xae, 0x99, 0x39, 0xbf, 0x07, 0xd6, 0xb0, 0x02, 0x63, 0xff, 0x14, 0x7a, 0x4b, 0xbc,
|
||||||
0x2f, 0x8a, 0xdf, 0x49, 0xf8, 0x95, 0x68, 0x2c, 0xd3, 0x67, 0x4a, 0xde, 0xa9, 0xa3, 0x75, 0x34,
|
0x9c, 0xb7, 0xc7, 0x5f, 0x24, 0xd6, 0xf2, 0xf6, 0x33, 0xf9, 0xb5, 0x53, 0xe4, 0xe6, 0xfc, 0xc2,
|
||||||
0xfc, 0x04, 0x3a, 0x79, 0x06, 0x5d, 0x49, 0xc1, 0x55, 0x7a, 0x7d, 0x04, 0xb0, 0x20, 0xd1, 0xbc,
|
0xdb, 0x8a, 0x0f, 0xd7, 0x91, 0xf7, 0x8b, 0xe2, 0x77, 0x12, 0x7e, 0x25, 0x1a, 0xcb, 0xf4, 0x99,
|
||||||
0x3d, 0xc2, 0x63, 0x99, 0x5f, 0x3f, 0x04, 0x58, 0x50, 0xa3, 0x42, 0x55, 0x91, 0x59, 0xd5, 0xb2,
|
0x90, 0x77, 0xe2, 0x68, 0x1d, 0x0d, 0x3f, 0x81, 0x76, 0x9e, 0x41, 0x57, 0x52, 0x70, 0x95, 0x5e,
|
||||||
0x65, 0xfa, 0x7c, 0x08, 0xad, 0x8c, 0xce, 0xf2, 0x7b, 0xa0, 0x83, 0x22, 0x3b, 0x7e, 0x36, 0xfa,
|
0x1f, 0x01, 0x64, 0x24, 0x9a, 0xb7, 0x47, 0x78, 0x2c, 0xf3, 0xeb, 0x87, 0x00, 0x19, 0x35, 0x2a,
|
||||||
0xed, 0xa3, 0xa9, 0x27, 0x2e, 0x92, 0xc9, 0xc8, 0x09, 0x67, 0xbb, 0x17, 0x34, 0xbe, 0xf0, 0x9c,
|
0x54, 0x15, 0x99, 0x55, 0x2d, 0x5b, 0xa6, 0xcf, 0x87, 0xd0, 0x4c, 0xe9, 0x2c, 0xbf, 0x07, 0x3a,
|
||||||
0x90, 0x47, 0xbb, 0x57, 0x12, 0x4c, 0xbb, 0x2b, 0x7f, 0x00, 0x4d, 0xea, 0xf8, 0xb1, 0xf7, 0xc1,
|
0x28, 0xb2, 0xe3, 0x67, 0xa3, 0xdf, 0x3e, 0x9a, 0xb9, 0xe2, 0x22, 0x9e, 0x8e, 0xec, 0x60, 0xbe,
|
||||||
0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x4e, 0xbe, 0xe0, 0xab, 0x1c, 0x1a, 0x00, 0x00,
|
0x7b, 0x41, 0xa3, 0x0b, 0xd7, 0x0e, 0x78, 0xb8, 0x7b, 0x25, 0xc1, 0xb4, 0xbb, 0xf2, 0x9f, 0xd1,
|
||||||
|
0xb4, 0x86, 0x1f, 0x7b, 0x1f, 0xfc, 0x37, 0x00, 0x00, 0xff, 0xff, 0x93, 0x15, 0xb9, 0x42, 0x4f,
|
||||||
|
0x1a, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,7 @@ message ProtoError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Paths is the structure of special paths that is used for SpecialPaths.
|
// Paths is the structure of special paths that is used for SpecialPaths.
|
||||||
message Paths {
|
message Paths {
|
||||||
// Root are the paths that require a root token to access
|
// Root are the paths that require a root token to access
|
||||||
repeated string root = 1;
|
repeated string root = 1;
|
||||||
|
|
||||||
@ -199,7 +199,7 @@ message Auth {
|
|||||||
// the range of the specified CIDR(s).
|
// the range of the specified CIDR(s).
|
||||||
repeated string bound_cidrs = 13;
|
repeated string bound_cidrs = 13;
|
||||||
|
|
||||||
// TokenPolicies and IdentityPolicies break down the list in Policies to
|
// TokenPolicies and IdentityPolicies break down the list in Policies to
|
||||||
// help determine where a policy was sourced
|
// help determine where a policy was sourced
|
||||||
repeated string token_policies = 14;
|
repeated string token_policies = 14;
|
||||||
repeated string identity_policies = 15;
|
repeated string identity_policies = 15;
|
||||||
@ -207,6 +207,9 @@ message Auth {
|
|||||||
// Explicit maximum lifetime for the token. Unlike normal TTLs, the maximum
|
// Explicit maximum lifetime for the token. Unlike normal TTLs, the maximum
|
||||||
// TTL is a hard limit and cannot be exceeded, also counts for periodic tokens.
|
// TTL is a hard limit and cannot be exceeded, also counts for periodic tokens.
|
||||||
int64 explicit_max_ttl = 16;
|
int64 explicit_max_ttl = 16;
|
||||||
|
|
||||||
|
// TokenType is the type of token being requested
|
||||||
|
uint32 token_type = 17;
|
||||||
}
|
}
|
||||||
|
|
||||||
message TokenEntry {
|
message TokenEntry {
|
||||||
@ -227,6 +230,7 @@ message TokenEntry {
|
|||||||
repeated string bound_cidrs = 15;
|
repeated string bound_cidrs = 15;
|
||||||
string namespace_id = 16;
|
string namespace_id = 16;
|
||||||
string cubbyhole_id = 17;
|
string cubbyhole_id = 17;
|
||||||
|
uint32 type = 18;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LeaseOptions {
|
message LeaseOptions {
|
||||||
|
|||||||
@ -486,6 +486,7 @@ func LogicalAuthToProtoAuth(a *logical.Auth) (*Auth, error) {
|
|||||||
|
|
||||||
return &Auth{
|
return &Auth{
|
||||||
LeaseOptions: lo,
|
LeaseOptions: lo,
|
||||||
|
TokenType: uint32(a.TokenType),
|
||||||
InternalData: string(buf[:]),
|
InternalData: string(buf[:]),
|
||||||
DisplayName: a.DisplayName,
|
DisplayName: a.DisplayName,
|
||||||
Policies: a.Policies,
|
Policies: a.Policies,
|
||||||
@ -532,6 +533,7 @@ func ProtoAuthToLogicalAuth(a *Auth) (*logical.Auth, error) {
|
|||||||
|
|
||||||
return &logical.Auth{
|
return &logical.Auth{
|
||||||
LeaseOptions: lo,
|
LeaseOptions: lo,
|
||||||
|
TokenType: logical.TokenType(a.TokenType),
|
||||||
InternalData: data,
|
InternalData: data,
|
||||||
DisplayName: a.DisplayName,
|
DisplayName: a.DisplayName,
|
||||||
Policies: a.Policies,
|
Policies: a.Policies,
|
||||||
@ -578,6 +580,7 @@ func LogicalTokenEntryToProtoTokenEntry(t *logical.TokenEntry) *TokenEntry {
|
|||||||
BoundCIDRs: boundCIDRs,
|
BoundCIDRs: boundCIDRs,
|
||||||
NamespaceID: t.NamespaceID,
|
NamespaceID: t.NamespaceID,
|
||||||
CubbyholeID: t.CubbyholeID,
|
CubbyholeID: t.CubbyholeID,
|
||||||
|
Type: uint32(t.Type),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,5 +617,6 @@ func ProtoTokenEntryToLogicalTokenEntry(t *TokenEntry) (*logical.TokenEntry, err
|
|||||||
BoundCIDRs: boundCIDRs,
|
BoundCIDRs: boundCIDRs,
|
||||||
NamespaceID: t.NamespaceID,
|
NamespaceID: t.NamespaceID,
|
||||||
CubbyholeID: t.CubbyholeID,
|
CubbyholeID: t.CubbyholeID,
|
||||||
|
Type: logical.TokenType(t.Type),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -213,6 +213,20 @@ func Test(tt TestT, c TestCase) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokenInfo, err := client.Auth().Token().LookupSelf()
|
||||||
|
if err != nil {
|
||||||
|
tt.Fatal("error looking up token: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var tokenPolicies []string
|
||||||
|
if tokenPoliciesRaw, ok := tokenInfo.Data["policies"]; ok {
|
||||||
|
if tokenPoliciesSliceRaw, ok := tokenPoliciesRaw.([]interface{}); ok {
|
||||||
|
for _, p := range tokenPoliciesSliceRaw {
|
||||||
|
tokenPolicies = append(tokenPolicies, p.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Make requests
|
// Make requests
|
||||||
var revoke []*logical.Request
|
var revoke []*logical.Request
|
||||||
for i, s := range c.Steps {
|
for i, s := range c.Steps {
|
||||||
@ -228,6 +242,12 @@ func Test(tt TestT, c TestCase) {
|
|||||||
}
|
}
|
||||||
if !s.Unauthenticated {
|
if !s.Unauthenticated {
|
||||||
req.ClientToken = client.Token()
|
req.ClientToken = client.Token()
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{
|
||||||
|
ID: req.ClientToken,
|
||||||
|
NamespaceID: namespace.RootNamespaceID,
|
||||||
|
Policies: tokenPolicies,
|
||||||
|
DisplayName: tokenInfo.Data["display_name"].(string),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if s.RemoteAddr != "" {
|
if s.RemoteAddr != "" {
|
||||||
req.Connection = &logical.Connection{RemoteAddr: s.RemoteAddr}
|
req.Connection = &logical.Connection{RemoteAddr: s.RemoteAddr}
|
||||||
|
|||||||
@ -6,8 +6,49 @@ import (
|
|||||||
sockaddr "github.com/hashicorp/go-sockaddr"
|
sockaddr "github.com/hashicorp/go-sockaddr"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type TokenType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TokenTypeDefault means "use the default, if any, that is currently set
|
||||||
|
// on the mount". If not set, results in a Service token.
|
||||||
|
TokenTypeDefault TokenType = iota
|
||||||
|
|
||||||
|
// TokenTypeService is a "normal" Vault token for long-lived services
|
||||||
|
TokenTypeService
|
||||||
|
|
||||||
|
// TokenTypeBatch is a batch token
|
||||||
|
TokenTypeBatch
|
||||||
|
|
||||||
|
// TokenTypeDefaultService, configured on a mount, means that if
|
||||||
|
// TokenTypeDefault is sent back by the mount, create Service tokens
|
||||||
|
TokenTypeDefaultService
|
||||||
|
|
||||||
|
// TokenTypeDefaultBatch, configured on a mount, means that if
|
||||||
|
// TokenTypeDefault is sent back by the mount, create Batch tokens
|
||||||
|
TokenTypeDefaultBatch
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t TokenType) String() string {
|
||||||
|
switch t {
|
||||||
|
case TokenTypeDefault:
|
||||||
|
return "default"
|
||||||
|
case TokenTypeService:
|
||||||
|
return "service"
|
||||||
|
case TokenTypeBatch:
|
||||||
|
return "batch"
|
||||||
|
case TokenTypeDefaultService:
|
||||||
|
return "default-service"
|
||||||
|
case TokenTypeDefaultBatch:
|
||||||
|
return "default-batch"
|
||||||
|
default:
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TokenEntry is used to represent a given token
|
// TokenEntry is used to represent a given token
|
||||||
type TokenEntry struct {
|
type TokenEntry struct {
|
||||||
|
Type TokenType `json:"type" mapstructure:"type" structs:"type" sentinel:""`
|
||||||
|
|
||||||
// ID of this entry, generally a random UUID
|
// ID of this entry, generally a random UUID
|
||||||
ID string `json:"id" mapstructure:"id" structs:"id" sentinel:""`
|
ID string `json:"id" mapstructure:"id" structs:"id" sentinel:""`
|
||||||
|
|
||||||
@ -107,6 +148,9 @@ func (te *TokenEntry) SentinelGet(key string) (interface{}, error) {
|
|||||||
|
|
||||||
case "meta", "metadata":
|
case "meta", "metadata":
|
||||||
return te.Meta, nil
|
return te.Meta, nil
|
||||||
|
|
||||||
|
case "type":
|
||||||
|
return te.Type.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -124,5 +168,6 @@ func (te *TokenEntry) SentinelKeys() []string {
|
|||||||
"creation_time_unix",
|
"creation_time_unix",
|
||||||
"meta",
|
"meta",
|
||||||
"metadata",
|
"metadata",
|
||||||
|
"type",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,7 @@ func LogicalResponseToHTTPResponse(input *Response) *HTTPResponse {
|
|||||||
LeaseDuration: int(input.Auth.TTL.Seconds()),
|
LeaseDuration: int(input.Auth.TTL.Seconds()),
|
||||||
Renewable: input.Auth.Renewable,
|
Renewable: input.Auth.Renewable,
|
||||||
EntityID: input.Auth.EntityID,
|
EntityID: input.Auth.EntityID,
|
||||||
|
TokenType: input.Auth.TokenType.String(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,6 +69,12 @@ func HTTPResponseToLogicalResponse(input *HTTPResponse) *Response {
|
|||||||
}
|
}
|
||||||
logicalResp.Auth.Renewable = input.Auth.Renewable
|
logicalResp.Auth.Renewable = input.Auth.Renewable
|
||||||
logicalResp.Auth.TTL = time.Second * time.Duration(input.Auth.LeaseDuration)
|
logicalResp.Auth.TTL = time.Second * time.Duration(input.Auth.LeaseDuration)
|
||||||
|
switch input.Auth.TokenType {
|
||||||
|
case "service":
|
||||||
|
logicalResp.Auth.TokenType = TokenTypeService
|
||||||
|
case "batch":
|
||||||
|
logicalResp.Auth.TokenType = TokenTypeBatch
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return logicalResp
|
return logicalResp
|
||||||
@ -94,6 +101,7 @@ type HTTPAuth struct {
|
|||||||
LeaseDuration int `json:"lease_duration"`
|
LeaseDuration int `json:"lease_duration"`
|
||||||
Renewable bool `json:"renewable"`
|
Renewable bool `json:"renewable"`
|
||||||
EntityID string `json:"entity_id"`
|
EntityID string `json:"entity_id"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type HTTPWrapInfo struct {
|
type HTTPWrapInfo struct {
|
||||||
|
|||||||
@ -572,7 +572,7 @@ ssl_storage_port: 7001
|
|||||||
#
|
#
|
||||||
# Setting listen_address to 0.0.0.0 is always wrong.
|
# Setting listen_address to 0.0.0.0 is always wrong.
|
||||||
#
|
#
|
||||||
listen_address: 172.17.0.2
|
listen_address: 172.17.0.3
|
||||||
|
|
||||||
# Set listen_address OR listen_interface, not both. Interfaces must correspond
|
# Set listen_address OR listen_interface, not both. Interfaces must correspond
|
||||||
# to a single address, IP aliasing is not supported.
|
# to a single address, IP aliasing is not supported.
|
||||||
|
|||||||
@ -661,6 +661,10 @@ func (c *Core) setupCredentials(ctx context.Context) error {
|
|||||||
if entry.Type == "token" {
|
if entry.Type == "token" {
|
||||||
c.tokenStore = backend.(*TokenStore)
|
c.tokenStore = backend.(*TokenStore)
|
||||||
|
|
||||||
|
// At some point when this isn't beta we may persist this but for
|
||||||
|
// now always set it on mount
|
||||||
|
entry.Config.TokenType = logical.TokenTypeDefaultService
|
||||||
|
|
||||||
// this is loaded *after* the normal mounts, including cubbyhole
|
// this is loaded *after* the normal mounts, including cubbyhole
|
||||||
c.router.tokenStoreSaltFunc = c.tokenStore.Salt
|
c.router.tokenStoreSaltFunc = c.tokenStore.Salt
|
||||||
if !c.IsDRSecondary() {
|
if !c.IsDRSecondary() {
|
||||||
|
|||||||
@ -845,7 +845,11 @@ func (b *AESGCMBarrier) encrypt(path string, term uint32, gcm cipher.AEAD, plain
|
|||||||
case AESGCMVersion1:
|
case AESGCMVersion1:
|
||||||
out = gcm.Seal(out, nonce, plain, nil)
|
out = gcm.Seal(out, nonce, plain, nil)
|
||||||
case AESGCMVersion2:
|
case AESGCMVersion2:
|
||||||
out = gcm.Seal(out, nonce, plain, []byte(path))
|
aad := []byte(nil)
|
||||||
|
if path != "" {
|
||||||
|
aad = []byte(path)
|
||||||
|
}
|
||||||
|
out = gcm.Seal(out, nonce, plain, aad)
|
||||||
default:
|
default:
|
||||||
panic("Unknown AESGCM version")
|
panic("Unknown AESGCM version")
|
||||||
}
|
}
|
||||||
@ -865,7 +869,11 @@ func (b *AESGCMBarrier) decrypt(path string, gcm cipher.AEAD, cipher []byte) ([]
|
|||||||
case AESGCMVersion1:
|
case AESGCMVersion1:
|
||||||
return gcm.Open(out, nonce, raw, nil)
|
return gcm.Open(out, nonce, raw, nil)
|
||||||
case AESGCMVersion2:
|
case AESGCMVersion2:
|
||||||
return gcm.Open(out, nonce, raw, []byte(path))
|
aad := []byte(nil)
|
||||||
|
if path != "" {
|
||||||
|
aad = []byte(path)
|
||||||
|
}
|
||||||
|
return gcm.Open(out, nonce, raw, aad)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("version bytes mis-match")
|
return nil, fmt.Errorf("version bytes mis-match")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1047,6 +1047,7 @@ func (c *Core) sealInitCommon(ctx context.Context, req *logical.Request) (retErr
|
|||||||
// Audit-log the request before going any further
|
// Audit-log the request before going any further
|
||||||
auth := &logical.Auth{
|
auth := &logical.Auth{
|
||||||
ClientToken: req.ClientToken,
|
ClientToken: req.ClientToken,
|
||||||
|
Accessor: req.ClientTokenAccessor,
|
||||||
}
|
}
|
||||||
if te != nil {
|
if te != nil {
|
||||||
auth.IdentityPolicies = identityPolicies[te.NamespaceID]
|
auth.IdentityPolicies = identityPolicies[te.NamespaceID]
|
||||||
@ -1057,6 +1058,7 @@ func (c *Core) sealInitCommon(ctx context.Context, req *logical.Request) (retErr
|
|||||||
auth.Metadata = te.Meta
|
auth.Metadata = te.Meta
|
||||||
auth.DisplayName = te.DisplayName
|
auth.DisplayName = te.DisplayName
|
||||||
auth.EntityID = te.EntityID
|
auth.EntityID = te.EntityID
|
||||||
|
auth.TokenType = te.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
logInput := &audit.LogInput{
|
logInput := &audit.LogInput{
|
||||||
|
|||||||
@ -317,6 +317,7 @@ func TestCore_HandleRequest_Lease(t *testing.T) {
|
|||||||
// Read the key
|
// Read the key
|
||||||
req.Operation = logical.ReadOperation
|
req.Operation = logical.ReadOperation
|
||||||
req.Data = nil
|
req.Data = nil
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = c.HandleRequest(ctx, req)
|
resp, err = c.HandleRequest(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -359,6 +360,7 @@ func TestCore_HandleRequest_Lease_MaxLength(t *testing.T) {
|
|||||||
// Read the key
|
// Read the key
|
||||||
req.Operation = logical.ReadOperation
|
req.Operation = logical.ReadOperation
|
||||||
req.Data = nil
|
req.Data = nil
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = c.HandleRequest(ctx, req)
|
resp, err = c.HandleRequest(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -401,6 +403,7 @@ func TestCore_HandleRequest_Lease_DefaultLength(t *testing.T) {
|
|||||||
// Read the key
|
// Read the key
|
||||||
req.Operation = logical.ReadOperation
|
req.Operation = logical.ReadOperation
|
||||||
req.Data = nil
|
req.Data = nil
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = c.HandleRequest(ctx, req)
|
resp, err = c.HandleRequest(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -409,7 +412,7 @@ func TestCore_HandleRequest_Lease_DefaultLength(t *testing.T) {
|
|||||||
t.Fatalf("bad: %#v", resp)
|
t.Fatalf("bad: %#v", resp)
|
||||||
}
|
}
|
||||||
if resp.Secret.TTL != c.defaultLeaseTTL {
|
if resp.Secret.TTL != c.defaultLeaseTTL {
|
||||||
t.Fatalf("bad: %#v, %d", resp.Secret, c.defaultLeaseTTL)
|
t.Fatalf("bad: %d, %d", resp.Secret.TTL/time.Second, c.defaultLeaseTTL/time.Second)
|
||||||
}
|
}
|
||||||
if resp.Secret.LeaseID == "" {
|
if resp.Secret.LeaseID == "" {
|
||||||
t.Fatalf("bad: %#v", resp.Secret)
|
t.Fatalf("bad: %#v", resp.Secret)
|
||||||
@ -481,7 +484,7 @@ func TestCore_HandleRequest_NoSlash(t *testing.T) {
|
|||||||
// Test a root path is denied if non-root
|
// Test a root path is denied if non-root
|
||||||
func TestCore_HandleRequest_RootPath(t *testing.T) {
|
func TestCore_HandleRequest_RootPath(t *testing.T) {
|
||||||
c, _, root := TestCoreUnsealed(t)
|
c, _, root := TestCoreUnsealed(t)
|
||||||
testMakeTokenViaCore(t, c, root, "child", "", []string{"test"})
|
testMakeServiceTokenViaCore(t, c, root, "child", "", []string{"test"})
|
||||||
|
|
||||||
req := &logical.Request{
|
req := &logical.Request{
|
||||||
Operation: logical.ReadOperation,
|
Operation: logical.ReadOperation,
|
||||||
@ -516,7 +519,7 @@ func TestCore_HandleRequest_RootPath_WithSudo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Child token (non-root) but with 'test' policy should have access
|
// Child token (non-root) but with 'test' policy should have access
|
||||||
testMakeTokenViaCore(t, c, root, "child", "", []string{"test"})
|
testMakeServiceTokenViaCore(t, c, root, "child", "", []string{"test"})
|
||||||
req = &logical.Request{
|
req = &logical.Request{
|
||||||
Operation: logical.ReadOperation,
|
Operation: logical.ReadOperation,
|
||||||
Path: "sys/policy", // root protected!
|
Path: "sys/policy", // root protected!
|
||||||
@ -534,7 +537,7 @@ func TestCore_HandleRequest_RootPath_WithSudo(t *testing.T) {
|
|||||||
// Check that standard permissions work
|
// Check that standard permissions work
|
||||||
func TestCore_HandleRequest_PermissionDenied(t *testing.T) {
|
func TestCore_HandleRequest_PermissionDenied(t *testing.T) {
|
||||||
c, _, root := TestCoreUnsealed(t)
|
c, _, root := TestCoreUnsealed(t)
|
||||||
testMakeTokenViaCore(t, c, root, "child", "", []string{"test"})
|
testMakeServiceTokenViaCore(t, c, root, "child", "", []string{"test"})
|
||||||
|
|
||||||
req := &logical.Request{
|
req := &logical.Request{
|
||||||
Operation: logical.UpdateOperation,
|
Operation: logical.UpdateOperation,
|
||||||
@ -554,7 +557,7 @@ func TestCore_HandleRequest_PermissionDenied(t *testing.T) {
|
|||||||
// Check that standard permissions work
|
// Check that standard permissions work
|
||||||
func TestCore_HandleRequest_PermissionAllowed(t *testing.T) {
|
func TestCore_HandleRequest_PermissionAllowed(t *testing.T) {
|
||||||
c, _, root := TestCoreUnsealed(t)
|
c, _, root := TestCoreUnsealed(t)
|
||||||
testMakeTokenViaCore(t, c, root, "child", "", []string{"test"})
|
testMakeServiceTokenViaCore(t, c, root, "child", "", []string{"test"})
|
||||||
|
|
||||||
// Set the 'test' policy object to permit access to secret/
|
// Set the 'test' policy object to permit access to secret/
|
||||||
req := &logical.Request{
|
req := &logical.Request{
|
||||||
@ -719,6 +722,7 @@ func TestCore_HandleLogin_Token(t *testing.T) {
|
|||||||
TTL: time.Hour * 24,
|
TTL: time.Hour * 24,
|
||||||
CreationTime: te.CreationTime,
|
CreationTime: te.CreationTime,
|
||||||
NamespaceID: namespace.RootNamespaceID,
|
NamespaceID: namespace.RootNamespaceID,
|
||||||
|
Type: logical.TokenTypeService,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(te, expect) {
|
if !reflect.DeepEqual(te, expect) {
|
||||||
@ -884,6 +888,7 @@ func TestCore_HandleRequest_AuditTrail_noHMACKeys(t *testing.T) {
|
|||||||
ClientToken: root,
|
ClientToken: root,
|
||||||
}
|
}
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
if _, err := c.HandleRequest(namespace.RootContext(nil), req); err != nil {
|
if _, err := c.HandleRequest(namespace.RootContext(nil), req); err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
@ -1020,6 +1025,7 @@ func TestCore_HandleRequest_CreateToken_Lease(t *testing.T) {
|
|||||||
CreationTime: te.CreationTime,
|
CreationTime: te.CreationTime,
|
||||||
TTL: time.Hour * 24 * 32,
|
TTL: time.Hour * 24 * 32,
|
||||||
NamespaceID: namespace.RootNamespaceID,
|
NamespaceID: namespace.RootNamespaceID,
|
||||||
|
Type: logical.TokenTypeService,
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(te, expect) {
|
if !reflect.DeepEqual(te, expect) {
|
||||||
t.Fatalf("Bad: %#v expect: %#v", te, expect)
|
t.Fatalf("Bad: %#v expect: %#v", te, expect)
|
||||||
@ -1066,6 +1072,7 @@ func TestCore_HandleRequest_CreateToken_NoDefaultPolicy(t *testing.T) {
|
|||||||
CreationTime: te.CreationTime,
|
CreationTime: te.CreationTime,
|
||||||
TTL: time.Hour * 24 * 32,
|
TTL: time.Hour * 24 * 32,
|
||||||
NamespaceID: namespace.RootNamespaceID,
|
NamespaceID: namespace.RootNamespaceID,
|
||||||
|
Type: logical.TokenTypeService,
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(te, expect) {
|
if !reflect.DeepEqual(te, expect) {
|
||||||
t.Fatalf("Bad: %#v expect: %#v", te, expect)
|
t.Fatalf("Bad: %#v expect: %#v", te, expect)
|
||||||
@ -1837,6 +1844,7 @@ func TestCore_HandleRequest_InternalData(t *testing.T) {
|
|||||||
Path: "foo/test",
|
Path: "foo/test",
|
||||||
ClientToken: root,
|
ClientToken: root,
|
||||||
}
|
}
|
||||||
|
lreq.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
lresp, err := c.HandleRequest(namespace.RootContext(nil), lreq)
|
lresp, err := c.HandleRequest(namespace.RootContext(nil), lreq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -1909,6 +1917,7 @@ func TestCore_RenewSameLease(t *testing.T) {
|
|||||||
// Read the key
|
// Read the key
|
||||||
req.Operation = logical.ReadOperation
|
req.Operation = logical.ReadOperation
|
||||||
req.Data = nil
|
req.Data = nil
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = c.HandleRequest(namespace.RootContext(nil), req)
|
resp, err = c.HandleRequest(namespace.RootContext(nil), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -2078,6 +2087,7 @@ path "secret/*" {
|
|||||||
// Read the key
|
// Read the key
|
||||||
req.Operation = logical.ReadOperation
|
req.Operation = logical.ReadOperation
|
||||||
req.Data = nil
|
req.Data = nil
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = c.HandleRequest(namespace.RootContext(nil), req)
|
resp, err = c.HandleRequest(namespace.RootContext(nil), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
|
|||||||
@ -873,6 +873,26 @@ func (m *ExpirationManager) Renew(ctx context.Context, leaseID string, increment
|
|||||||
le.ExpireTime = resp.Secret.ExpirationTime()
|
le.ExpireTime = resp.Secret.ExpirationTime()
|
||||||
le.LastRenewalTime = time.Now()
|
le.LastRenewalTime = time.Now()
|
||||||
|
|
||||||
|
// If the token it's associated with is a batch token, constrain lease
|
||||||
|
// times
|
||||||
|
if le.ClientTokenType == logical.TokenTypeBatch {
|
||||||
|
te, err := m.tokenStore.Lookup(ctx, le.ClientToken)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if te == nil {
|
||||||
|
return nil, errors.New("cannot renew lease, no valid associated token")
|
||||||
|
}
|
||||||
|
tokenLeaseTimes, err := m.FetchLeaseTimesByToken(ctx, te)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if le.ExpireTime.After(tokenLeaseTimes.ExpireTime) {
|
||||||
|
resp.Secret.TTL = tokenLeaseTimes.ExpireTime.Sub(le.LastRenewalTime)
|
||||||
|
le.ExpireTime = tokenLeaseTimes.ExpireTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
m.pendingLock.Lock()
|
m.pendingLock.Lock()
|
||||||
if err := m.persistEntry(ctx, le); err != nil {
|
if err := m.persistEntry(ctx, le); err != nil {
|
||||||
@ -1012,7 +1032,8 @@ func (m *ExpirationManager) RenewToken(ctx context.Context, req *logical.Request
|
|||||||
func (m *ExpirationManager) Register(ctx context.Context, req *logical.Request, resp *logical.Response) (id string, retErr error) {
|
func (m *ExpirationManager) Register(ctx context.Context, req *logical.Request, resp *logical.Response) (id string, retErr error) {
|
||||||
defer metrics.MeasureSince([]string{"expire", "register"}, time.Now())
|
defer metrics.MeasureSince([]string{"expire", "register"}, time.Now())
|
||||||
|
|
||||||
if req.ClientToken == "" {
|
te := req.TokenEntry()
|
||||||
|
if te == nil {
|
||||||
return "", fmt.Errorf("cannot register a lease with an empty client token")
|
return "", fmt.Errorf("cannot register a lease with an empty client token")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1044,14 +1065,15 @@ func (m *ExpirationManager) Register(ctx context.Context, req *logical.Request,
|
|||||||
}
|
}
|
||||||
|
|
||||||
le := &leaseEntry{
|
le := &leaseEntry{
|
||||||
LeaseID: leaseID,
|
LeaseID: leaseID,
|
||||||
ClientToken: req.ClientToken,
|
ClientToken: req.ClientToken,
|
||||||
Path: req.Path,
|
ClientTokenType: te.Type,
|
||||||
Data: resp.Data,
|
Path: req.Path,
|
||||||
Secret: resp.Secret,
|
Data: resp.Data,
|
||||||
IssueTime: time.Now(),
|
Secret: resp.Secret,
|
||||||
ExpireTime: resp.Secret.ExpirationTime(),
|
IssueTime: time.Now(),
|
||||||
namespace: ns,
|
ExpireTime: resp.Secret.ExpirationTime(),
|
||||||
|
namespace: ns,
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -1078,14 +1100,35 @@ func (m *ExpirationManager) Register(ctx context.Context, req *logical.Request,
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// If the token is a batch token, we want to constrain the maximum lifetime
|
||||||
|
// by the token's lifetime
|
||||||
|
if te.Type == logical.TokenTypeBatch {
|
||||||
|
tokenLeaseTimes, err := m.FetchLeaseTimesByToken(ctx, te)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if le.ExpireTime.After(tokenLeaseTimes.ExpireTime) {
|
||||||
|
le.ExpireTime = tokenLeaseTimes.ExpireTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Encode the entry
|
// Encode the entry
|
||||||
if err := m.persistEntry(ctx, le); err != nil {
|
if err := m.persistEntry(ctx, le); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maintain secondary index by token
|
// Maintain secondary index by token, except for orphan batch tokens
|
||||||
if err := m.createIndexByToken(ctx, le); err != nil {
|
switch {
|
||||||
return "", err
|
case te.Type != logical.TokenTypeBatch:
|
||||||
|
if err := m.createIndexByToken(ctx, le, le.ClientToken); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
case te.Parent != "":
|
||||||
|
// If it's a non-orphan batch token, assign the secondary index to its
|
||||||
|
// parent
|
||||||
|
if err := m.createIndexByToken(ctx, le, te.Parent); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup revocation timer if there is a lease
|
// Setup revocation timer if there is a lease
|
||||||
@ -1101,8 +1144,12 @@ func (m *ExpirationManager) Register(ctx context.Context, req *logical.Request,
|
|||||||
func (m *ExpirationManager) RegisterAuth(ctx context.Context, te *logical.TokenEntry, auth *logical.Auth) error {
|
func (m *ExpirationManager) RegisterAuth(ctx context.Context, te *logical.TokenEntry, auth *logical.Auth) error {
|
||||||
defer metrics.MeasureSince([]string{"expire", "register-auth"}, time.Now())
|
defer metrics.MeasureSince([]string{"expire", "register-auth"}, time.Now())
|
||||||
|
|
||||||
|
if te.Type == logical.TokenTypeBatch {
|
||||||
|
return errors.New("cannot register a lease for a batch token")
|
||||||
|
}
|
||||||
|
|
||||||
if auth.ClientToken == "" {
|
if auth.ClientToken == "" {
|
||||||
return fmt.Errorf("cannot register an auth lease with an empty token")
|
return errors.New("cannot register an auth lease with an empty token")
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.Contains(te.Path, "..") {
|
if strings.Contains(te.Path, "..") {
|
||||||
@ -1152,9 +1199,24 @@ func (m *ExpirationManager) RegisterAuth(ctx context.Context, te *logical.TokenE
|
|||||||
|
|
||||||
// FetchLeaseTimesByToken is a helper function to use token values to compute
|
// FetchLeaseTimesByToken is a helper function to use token values to compute
|
||||||
// the leaseID, rather than pushing that logic back into the token store.
|
// the leaseID, rather than pushing that logic back into the token store.
|
||||||
|
// As a special case, for a batch token it simply returns the information
|
||||||
|
// encoded on it.
|
||||||
func (m *ExpirationManager) FetchLeaseTimesByToken(ctx context.Context, te *logical.TokenEntry) (*leaseEntry, error) {
|
func (m *ExpirationManager) FetchLeaseTimesByToken(ctx context.Context, te *logical.TokenEntry) (*leaseEntry, error) {
|
||||||
defer metrics.MeasureSince([]string{"expire", "fetch-lease-times-by-token"}, time.Now())
|
defer metrics.MeasureSince([]string{"expire", "fetch-lease-times-by-token"}, time.Now())
|
||||||
|
|
||||||
|
if te == nil {
|
||||||
|
return nil, errors.New("cannot fetch lease times for nil token")
|
||||||
|
}
|
||||||
|
|
||||||
|
if te.Type == logical.TokenTypeBatch {
|
||||||
|
issueTime := time.Unix(te.CreationTime, 0)
|
||||||
|
return &leaseEntry{
|
||||||
|
IssueTime: issueTime,
|
||||||
|
ExpireTime: issueTime.Add(te.TTL),
|
||||||
|
ClientTokenType: logical.TokenTypeBatch,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
tokenNS, err := NamespaceByID(ctx, te.NamespaceID, m.core)
|
tokenNS, err := NamespaceByID(ctx, te.NamespaceID, m.core)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1273,6 +1335,10 @@ func (m *ExpirationManager) revokeEntry(ctx context.Context, le *leaseEntry) err
|
|||||||
// Revocation of login tokens is special since we can by-pass the
|
// Revocation of login tokens is special since we can by-pass the
|
||||||
// backend and directly interact with the token store
|
// backend and directly interact with the token store
|
||||||
if le.Auth != nil {
|
if le.Auth != nil {
|
||||||
|
if le.ClientTokenType == logical.TokenTypeBatch {
|
||||||
|
return errors.New("batch tokens cannot be revoked")
|
||||||
|
}
|
||||||
|
|
||||||
if err := m.tokenStore.revokeTree(ctx, le); err != nil {
|
if err := m.tokenStore.revokeTree(ctx, le); err != nil {
|
||||||
return errwrap.Wrapf("failed to revoke token: {{err}}", err)
|
return errwrap.Wrapf("failed to revoke token: {{err}}", err)
|
||||||
}
|
}
|
||||||
@ -1319,6 +1385,10 @@ func (m *ExpirationManager) renewEntry(ctx context.Context, le *leaseEntry, incr
|
|||||||
// renewAuthEntry is used to attempt renew of an auth entry. Only the token
|
// renewAuthEntry is used to attempt renew of an auth entry. Only the token
|
||||||
// store should get the actual token ID intact.
|
// store should get the actual token ID intact.
|
||||||
func (m *ExpirationManager) renewAuthEntry(ctx context.Context, req *logical.Request, le *leaseEntry, increment time.Duration) (*logical.Response, error) {
|
func (m *ExpirationManager) renewAuthEntry(ctx context.Context, req *logical.Request, le *leaseEntry, increment time.Duration) (*logical.Response, error) {
|
||||||
|
if le.ClientTokenType == logical.TokenTypeBatch {
|
||||||
|
return logical.ErrorResponse("batch tokens cannot be renewed"), nil
|
||||||
|
}
|
||||||
|
|
||||||
auth := *le.Auth
|
auth := *le.Auth
|
||||||
auth.IssueTime = le.IssueTime
|
auth.IssueTime = le.IssueTime
|
||||||
auth.Increment = increment
|
auth.Increment = increment
|
||||||
@ -1446,10 +1516,10 @@ func (m *ExpirationManager) deleteEntry(ctx context.Context, le *leaseEntry) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// createIndexByToken creates a secondary index from the token to a lease entry
|
// createIndexByToken creates a secondary index from the token to a lease entry
|
||||||
func (m *ExpirationManager) createIndexByToken(ctx context.Context, le *leaseEntry) error {
|
func (m *ExpirationManager) createIndexByToken(ctx context.Context, le *leaseEntry, token string) error {
|
||||||
tokenNS := namespace.RootNamespace
|
tokenNS := namespace.RootNamespace
|
||||||
saltCtx := namespace.ContextWithNamespace(ctx, namespace.RootNamespace)
|
saltCtx := namespace.ContextWithNamespace(ctx, namespace.RootNamespace)
|
||||||
_, nsID := namespace.SplitIDFromString(le.ClientToken)
|
_, nsID := namespace.SplitIDFromString(token)
|
||||||
if nsID != "" {
|
if nsID != "" {
|
||||||
tokenNS, err := NamespaceByID(ctx, nsID, m.core)
|
tokenNS, err := NamespaceByID(ctx, nsID, m.core)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1460,7 +1530,7 @@ func (m *ExpirationManager) createIndexByToken(ctx context.Context, le *leaseEnt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
saltedID, err := m.tokenStore.SaltID(saltCtx, le.ClientToken)
|
saltedID, err := m.tokenStore.SaltID(saltCtx, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1674,6 +1744,7 @@ func (m *ExpirationManager) emitMetrics() {
|
|||||||
type leaseEntry struct {
|
type leaseEntry struct {
|
||||||
LeaseID string `json:"lease_id"`
|
LeaseID string `json:"lease_id"`
|
||||||
ClientToken string `json:"client_token"`
|
ClientToken string `json:"client_token"`
|
||||||
|
ClientTokenType logical.TokenType `json:"token_type"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Data map[string]interface{} `json:"data"`
|
Data map[string]interface{} `json:"data"`
|
||||||
Secret *logical.Secret `json:"secret"`
|
Secret *logical.Secret `json:"secret"`
|
||||||
@ -1691,24 +1762,29 @@ func (le *leaseEntry) encode() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (le *leaseEntry) renewable() (bool, error) {
|
func (le *leaseEntry) renewable() (bool, error) {
|
||||||
var err error
|
|
||||||
switch {
|
switch {
|
||||||
// If there is no entry, cannot review
|
// If there is no entry, cannot review to renew
|
||||||
case le == nil || le.ExpireTime.IsZero():
|
case le == nil:
|
||||||
err = fmt.Errorf("lease not found or lease is not renewable")
|
return false, fmt.Errorf("lease not found")
|
||||||
|
|
||||||
|
case le.ExpireTime.IsZero():
|
||||||
|
return false, fmt.Errorf("lease is not renewable")
|
||||||
|
|
||||||
|
case le.ClientTokenType == logical.TokenTypeBatch:
|
||||||
|
return false, nil
|
||||||
|
|
||||||
// Determine if the lease is expired
|
// Determine if the lease is expired
|
||||||
case le.ExpireTime.Before(time.Now()):
|
case le.ExpireTime.Before(time.Now()):
|
||||||
err = fmt.Errorf("lease expired")
|
return false, fmt.Errorf("lease expired")
|
||||||
|
|
||||||
// Determine if the lease is renewable
|
// Determine if the lease is renewable
|
||||||
case le.Secret != nil && !le.Secret.Renewable:
|
case le.Secret != nil && !le.Secret.Renewable:
|
||||||
err = fmt.Errorf("lease is not renewable")
|
return false, fmt.Errorf("lease is not renewable")
|
||||||
|
|
||||||
case le.Auth != nil && !le.Auth.Renewable:
|
case le.Auth != nil && !le.Auth.Renewable:
|
||||||
err = fmt.Errorf("lease is not renewable")
|
return false, fmt.Errorf("lease is not renewable")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -180,6 +180,7 @@ func TestExpiration_Tidy(t *testing.T) {
|
|||||||
Path: "invalid/lease/" + fmt.Sprintf("%d", i+1),
|
Path: "invalid/lease/" + fmt.Sprintf("%d", i+1),
|
||||||
ClientToken: "invalidtoken",
|
ClientToken: "invalidtoken",
|
||||||
}
|
}
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: "invalidtoken", NamespaceID: "root"})
|
||||||
resp := &logical.Response{
|
resp := &logical.Response{
|
||||||
Secret: &logical.Secret{
|
Secret: &logical.Secret{
|
||||||
LeaseOptions: logical.LeaseOptions{
|
LeaseOptions: logical.LeaseOptions{
|
||||||
@ -396,6 +397,7 @@ func TestExpiration_Restore(t *testing.T) {
|
|||||||
Path: path,
|
Path: path,
|
||||||
ClientToken: "foobar",
|
ClientToken: "foobar",
|
||||||
}
|
}
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||||
resp := &logical.Response{
|
resp := &logical.Response{
|
||||||
Secret: &logical.Secret{
|
Secret: &logical.Secret{
|
||||||
LeaseOptions: logical.LeaseOptions{
|
LeaseOptions: logical.LeaseOptions{
|
||||||
@ -452,6 +454,7 @@ func TestExpiration_Register(t *testing.T) {
|
|||||||
Path: "prod/aws/foo",
|
Path: "prod/aws/foo",
|
||||||
ClientToken: "foobar",
|
ClientToken: "foobar",
|
||||||
}
|
}
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||||
resp := &logical.Response{
|
resp := &logical.Response{
|
||||||
Secret: &logical.Secret{
|
Secret: &logical.Secret{
|
||||||
LeaseOptions: logical.LeaseOptions{
|
LeaseOptions: logical.LeaseOptions{
|
||||||
@ -539,7 +542,7 @@ func TestExpiration_RegisterAuth_NoLease(t *testing.T) {
|
|||||||
NamespaceID: namespace.RootNamespaceID,
|
NamespaceID: namespace.RootNamespaceID,
|
||||||
}
|
}
|
||||||
resp, err := exp.RenewToken(namespace.TestContext(), &logical.Request{}, te, 0)
|
resp, err := exp.RenewToken(namespace.TestContext(), &logical.Request{}, te, 0)
|
||||||
if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease not found or lease is not renewable")) {
|
if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease is not renewable")) {
|
||||||
t.Fatalf("bad: err:%v resp:%#v", err, resp)
|
t.Fatalf("bad: err:%v resp:%#v", err, resp)
|
||||||
}
|
}
|
||||||
if resp == nil {
|
if resp == nil {
|
||||||
@ -578,6 +581,7 @@ func TestExpiration_Revoke(t *testing.T) {
|
|||||||
Path: "prod/aws/foo",
|
Path: "prod/aws/foo",
|
||||||
ClientToken: "foobar",
|
ClientToken: "foobar",
|
||||||
}
|
}
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||||
resp := &logical.Response{
|
resp := &logical.Response{
|
||||||
Secret: &logical.Secret{
|
Secret: &logical.Secret{
|
||||||
LeaseOptions: logical.LeaseOptions{
|
LeaseOptions: logical.LeaseOptions{
|
||||||
@ -624,6 +628,7 @@ func TestExpiration_RevokeOnExpire(t *testing.T) {
|
|||||||
Path: "prod/aws/foo",
|
Path: "prod/aws/foo",
|
||||||
ClientToken: "foobar",
|
ClientToken: "foobar",
|
||||||
}
|
}
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||||
resp := &logical.Response{
|
resp := &logical.Response{
|
||||||
Secret: &logical.Secret{
|
Secret: &logical.Secret{
|
||||||
LeaseOptions: logical.LeaseOptions{
|
LeaseOptions: logical.LeaseOptions{
|
||||||
@ -687,6 +692,7 @@ func TestExpiration_RevokePrefix(t *testing.T) {
|
|||||||
Path: path,
|
Path: path,
|
||||||
ClientToken: "foobar",
|
ClientToken: "foobar",
|
||||||
}
|
}
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||||
resp := &logical.Response{
|
resp := &logical.Response{
|
||||||
Secret: &logical.Secret{
|
Secret: &logical.Secret{
|
||||||
LeaseOptions: logical.LeaseOptions{
|
LeaseOptions: logical.LeaseOptions{
|
||||||
@ -755,6 +761,7 @@ func TestExpiration_RevokeByToken(t *testing.T) {
|
|||||||
Path: path,
|
Path: path,
|
||||||
ClientToken: "foobarbaz",
|
ClientToken: "foobarbaz",
|
||||||
}
|
}
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: "foobarbaz", NamespaceID: "root"})
|
||||||
resp := &logical.Response{
|
resp := &logical.Response{
|
||||||
Secret: &logical.Secret{
|
Secret: &logical.Secret{
|
||||||
LeaseOptions: logical.LeaseOptions{
|
LeaseOptions: logical.LeaseOptions{
|
||||||
@ -843,6 +850,7 @@ func TestExpiration_RevokeByToken_Blocking(t *testing.T) {
|
|||||||
Path: path,
|
Path: path,
|
||||||
ClientToken: "foobarbaz",
|
ClientToken: "foobarbaz",
|
||||||
}
|
}
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: "foobarbaz", NamespaceID: "root"})
|
||||||
resp := &logical.Response{
|
resp := &logical.Response{
|
||||||
Secret: &logical.Secret{
|
Secret: &logical.Secret{
|
||||||
LeaseOptions: logical.LeaseOptions{
|
LeaseOptions: logical.LeaseOptions{
|
||||||
@ -1145,6 +1153,7 @@ func TestExpiration_Renew(t *testing.T) {
|
|||||||
Path: "prod/aws/foo",
|
Path: "prod/aws/foo",
|
||||||
ClientToken: "foobar",
|
ClientToken: "foobar",
|
||||||
}
|
}
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||||
resp := &logical.Response{
|
resp := &logical.Response{
|
||||||
Secret: &logical.Secret{
|
Secret: &logical.Secret{
|
||||||
LeaseOptions: logical.LeaseOptions{
|
LeaseOptions: logical.LeaseOptions{
|
||||||
@ -1215,6 +1224,7 @@ func TestExpiration_Renew_NotRenewable(t *testing.T) {
|
|||||||
Path: "prod/aws/foo",
|
Path: "prod/aws/foo",
|
||||||
ClientToken: "foobar",
|
ClientToken: "foobar",
|
||||||
}
|
}
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||||
resp := &logical.Response{
|
resp := &logical.Response{
|
||||||
Secret: &logical.Secret{
|
Secret: &logical.Secret{
|
||||||
LeaseOptions: logical.LeaseOptions{
|
LeaseOptions: logical.LeaseOptions{
|
||||||
@ -1265,6 +1275,7 @@ func TestExpiration_Renew_RevokeOnExpire(t *testing.T) {
|
|||||||
Path: "prod/aws/foo",
|
Path: "prod/aws/foo",
|
||||||
ClientToken: "foobar",
|
ClientToken: "foobar",
|
||||||
}
|
}
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"})
|
||||||
resp := &logical.Response{
|
resp := &logical.Response{
|
||||||
Secret: &logical.Secret{
|
Secret: &logical.Secret{
|
||||||
LeaseOptions: logical.LeaseOptions{
|
LeaseOptions: logical.LeaseOptions{
|
||||||
@ -1407,7 +1418,7 @@ func TestExpiration_revokeEntry_token(t *testing.T) {
|
|||||||
if err := exp.persistEntry(namespace.TestContext(), le); err != nil {
|
if err := exp.persistEntry(namespace.TestContext(), le); err != nil {
|
||||||
t.Fatalf("error persisting entry: %v", err)
|
t.Fatalf("error persisting entry: %v", err)
|
||||||
}
|
}
|
||||||
if err := exp.createIndexByToken(namespace.TestContext(), le); err != nil {
|
if err := exp.createIndexByToken(namespace.TestContext(), le, le.ClientToken); err != nil {
|
||||||
t.Fatalf("error creating secondary index: %v", err)
|
t.Fatalf("error creating secondary index: %v", err)
|
||||||
}
|
}
|
||||||
exp.updatePending(le, le.Secret.LeaseTotal())
|
exp.updatePending(le, le.Secret.LeaseTotal())
|
||||||
@ -1802,6 +1813,7 @@ func TestExpiration_RevokeForce(t *testing.T) {
|
|||||||
Path: "badrenew/creds",
|
Path: "badrenew/creds",
|
||||||
ClientToken: root,
|
ClientToken: root,
|
||||||
}
|
}
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
|
|
||||||
resp, err := core.HandleRequest(namespace.TestContext(), req)
|
resp, err := core.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1850,6 +1862,7 @@ func TestExpiration_RevokeForceSingle(t *testing.T) {
|
|||||||
Path: "badrenew/creds",
|
Path: "badrenew/creds",
|
||||||
ClientToken: root,
|
ClientToken: root,
|
||||||
}
|
}
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
|
|
||||||
resp, err := core.HandleRequest(namespace.TestContext(), req)
|
resp, err := core.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
420
vault/external_tests/token/batch_token_test.go
Normal file
420
vault/external_tests/token/batch_token_test.go
Normal file
@ -0,0 +1,420 @@
|
|||||||
|
package token
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/vault/api"
|
||||||
|
"github.com/hashicorp/vault/builtin/credential/approle"
|
||||||
|
vaulthttp "github.com/hashicorp/vault/http"
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
"github.com/hashicorp/vault/vault"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBatchTokens(t *testing.T) {
|
||||||
|
coreConfig := &vault.CoreConfig{
|
||||||
|
LogicalBackends: map[string]logical.Factory{
|
||||||
|
"kv": vault.LeasedPassthroughBackendFactory,
|
||||||
|
},
|
||||||
|
CredentialBackends: map[string]logical.Factory{
|
||||||
|
"approle": approle.Factory,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
||||||
|
HandlerFunc: vaulthttp.Handler,
|
||||||
|
})
|
||||||
|
cluster.Start()
|
||||||
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
|
core := cluster.Cores[0].Core
|
||||||
|
vault.TestWaitActive(t, core)
|
||||||
|
client := cluster.Cores[0].Client
|
||||||
|
rootToken := client.Token()
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Set up a KV path
|
||||||
|
err = client.Sys().Mount("kv", &api.MountInput{
|
||||||
|
Type: "kv",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Logical().Write("kv/foo", map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
"ttl": "5m",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the test policy
|
||||||
|
err = client.Sys().PutPolicy("test", `
|
||||||
|
path "kv/*" {
|
||||||
|
capabilities = ["read"]
|
||||||
|
}`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount the auth backend
|
||||||
|
err = client.Sys().EnableAuthWithOptions("approle", &api.EnableAuthOptions{
|
||||||
|
Type: "approle",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tune the mount
|
||||||
|
if err = client.Sys().TuneMount("auth/approle", api.MountConfigInput{
|
||||||
|
DefaultLeaseTTL: "5s",
|
||||||
|
MaxLeaseTTL: "5s",
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create role
|
||||||
|
resp, err := client.Logical().Write("auth/approle/role/test", map[string]interface{}{
|
||||||
|
"policies": "test",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get role_id
|
||||||
|
resp, err = client.Logical().Read("auth/approle/role/test/role-id")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resp == nil {
|
||||||
|
t.Fatal("expected a response for fetching the role-id")
|
||||||
|
}
|
||||||
|
roleID := resp.Data["role_id"]
|
||||||
|
|
||||||
|
// Get secret_id
|
||||||
|
resp, err = client.Logical().Write("auth/approle/role/test/secret-id", map[string]interface{}{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resp == nil {
|
||||||
|
t.Fatal("expected a response for fetching the secret-id")
|
||||||
|
}
|
||||||
|
secretID := resp.Data["secret_id"]
|
||||||
|
|
||||||
|
// Login
|
||||||
|
testLogin := func(mountTuneType, roleType string, batch bool) string {
|
||||||
|
t.Helper()
|
||||||
|
if err = client.Sys().TuneMount("auth/approle", api.MountConfigInput{
|
||||||
|
TokenType: mountTuneType,
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = client.Logical().Write("auth/approle/role/test", map[string]interface{}{
|
||||||
|
"token_type": roleType,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = client.Logical().Write("auth/approle/login", map[string]interface{}{
|
||||||
|
"role_id": roleID,
|
||||||
|
"secret_id": secretID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resp == nil {
|
||||||
|
t.Fatal("expected a response for login")
|
||||||
|
}
|
||||||
|
if resp.Auth == nil {
|
||||||
|
t.Fatal("expected auth object from response")
|
||||||
|
}
|
||||||
|
if resp.Auth.ClientToken == "" {
|
||||||
|
t.Fatal("expected a client token")
|
||||||
|
}
|
||||||
|
if batch && !strings.HasPrefix(resp.Auth.ClientToken, "b.") {
|
||||||
|
t.Fatal("expected a batch token")
|
||||||
|
}
|
||||||
|
if !batch && strings.HasPrefix(resp.Auth.ClientToken, "b.") {
|
||||||
|
t.Fatal("expected a non-batch token")
|
||||||
|
}
|
||||||
|
return resp.Auth.ClientToken
|
||||||
|
}
|
||||||
|
testLogin("service", "default", false)
|
||||||
|
testLogin("service", "batch", false)
|
||||||
|
testLogin("service", "service", false)
|
||||||
|
testLogin("batch", "default", true)
|
||||||
|
testLogin("batch", "batch", true)
|
||||||
|
testLogin("batch", "service", true)
|
||||||
|
testLogin("default-service", "default", false)
|
||||||
|
testLogin("default-service", "batch", true)
|
||||||
|
testLogin("default-service", "service", false)
|
||||||
|
testLogin("default-batch", "default", true)
|
||||||
|
testLogin("default-batch", "batch", true)
|
||||||
|
testLogin("default-batch", "service", false)
|
||||||
|
|
||||||
|
finalToken := testLogin("batch", "batch", true)
|
||||||
|
|
||||||
|
client.SetToken(finalToken)
|
||||||
|
resp, err = client.Logical().Read("kv/foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resp.Data["foo"].(string) != "bar" {
|
||||||
|
t.Fatal("bad")
|
||||||
|
}
|
||||||
|
if resp.LeaseID == "" {
|
||||||
|
t.Fatal("expected lease")
|
||||||
|
}
|
||||||
|
if !resp.Renewable {
|
||||||
|
t.Fatal("expected renewable")
|
||||||
|
}
|
||||||
|
if resp.LeaseDuration > 5 {
|
||||||
|
t.Fatalf("lease duration too big: %d", resp.LeaseDuration)
|
||||||
|
}
|
||||||
|
leaseID := resp.LeaseID
|
||||||
|
|
||||||
|
lastDuration := resp.LeaseDuration
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
resp, err = client.Sys().Renew(leaseID, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resp.LeaseDuration >= lastDuration {
|
||||||
|
t.Fatal("expected duration to go down")
|
||||||
|
}
|
||||||
|
lastDuration = resp.LeaseDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
client.SetToken(rootToken)
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
resp, err = client.Logical().Write("sys/leases/lookup", map[string]interface{}{
|
||||||
|
"lease_id": leaseID,
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBatchToken_ParentLeaseRevoke(t *testing.T) {
|
||||||
|
coreConfig := &vault.CoreConfig{
|
||||||
|
LogicalBackends: map[string]logical.Factory{
|
||||||
|
"kv": vault.LeasedPassthroughBackendFactory,
|
||||||
|
},
|
||||||
|
CredentialBackends: map[string]logical.Factory{
|
||||||
|
"approle": approle.Factory,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
||||||
|
HandlerFunc: vaulthttp.Handler,
|
||||||
|
})
|
||||||
|
cluster.Start()
|
||||||
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
|
core := cluster.Cores[0].Core
|
||||||
|
vault.TestWaitActive(t, core)
|
||||||
|
client := cluster.Cores[0].Client
|
||||||
|
rootToken := client.Token()
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Set up a KV path
|
||||||
|
err = client.Sys().Mount("kv", &api.MountInput{
|
||||||
|
Type: "kv",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Logical().Write("kv/foo", map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
"ttl": "5m",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the test policy
|
||||||
|
err = client.Sys().PutPolicy("test", `
|
||||||
|
path "kv/*" {
|
||||||
|
capabilities = ["read"]
|
||||||
|
}`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a second root token
|
||||||
|
secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
|
||||||
|
Policies: []string{"root"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rootToken2 := secret.Auth.ClientToken
|
||||||
|
|
||||||
|
// Use this new token to create a batch token
|
||||||
|
client.SetToken(rootToken2)
|
||||||
|
secret, err = client.Auth().Token().Create(&api.TokenCreateRequest{
|
||||||
|
Policies: []string{"test"},
|
||||||
|
Type: "batch",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
batchToken := secret.Auth.ClientToken
|
||||||
|
client.SetToken(batchToken)
|
||||||
|
_, err = client.Auth().Token().LookupSelf()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if secret.Auth.ClientToken[0:2] != "b." {
|
||||||
|
t.Fatal(secret.Auth.ClientToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a lease with the batch token
|
||||||
|
resp, err := client.Logical().Read("kv/foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resp.Data["foo"].(string) != "bar" {
|
||||||
|
t.Fatal("bad")
|
||||||
|
}
|
||||||
|
if resp.LeaseID == "" {
|
||||||
|
t.Fatal("expected lease")
|
||||||
|
}
|
||||||
|
leaseID := resp.LeaseID
|
||||||
|
|
||||||
|
// Check the lease
|
||||||
|
resp, err = client.Logical().Write("sys/leases/lookup", map[string]interface{}{
|
||||||
|
"lease_id": leaseID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revoke the parent
|
||||||
|
client.SetToken(rootToken2)
|
||||||
|
err = client.Auth().Token().RevokeSelf("")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the batch token is not usable anymore
|
||||||
|
client.SetToken(rootToken)
|
||||||
|
_, err = client.Auth().Token().Lookup(batchToken)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the lease has been revoked
|
||||||
|
resp, err = client.Logical().Write("sys/leases/lookup", map[string]interface{}{
|
||||||
|
"lease_id": leaseID,
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTokenStore_Roles_Batch(t *testing.T) {
|
||||||
|
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
|
||||||
|
HandlerFunc: vaulthttp.Handler,
|
||||||
|
})
|
||||||
|
cluster.Start()
|
||||||
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
|
core := cluster.Cores[0].Core
|
||||||
|
vault.TestWaitActive(t, core)
|
||||||
|
client := cluster.Cores[0].Client
|
||||||
|
rootToken := client.Token()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var secret *api.Secret
|
||||||
|
|
||||||
|
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
||||||
|
"bound_cidrs": []string{},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{
|
||||||
|
Policies: []string{"default"},
|
||||||
|
Type: "batch",
|
||||||
|
}, "testrole")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
client.SetToken(secret.Auth.ClientToken)
|
||||||
|
_, err = client.Auth().Token().LookupSelf()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if secret.Auth.ClientToken[0:2] == "b." {
|
||||||
|
t.Fatal(secret.Auth.ClientToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test batch
|
||||||
|
client.SetToken(rootToken)
|
||||||
|
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
||||||
|
"token_type": "batch",
|
||||||
|
})
|
||||||
|
// Orphan not set so we should error
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
||||||
|
"token_type": "batch",
|
||||||
|
"orphan": true,
|
||||||
|
})
|
||||||
|
// Renewable set so we should error
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
||||||
|
"token_type": "batch",
|
||||||
|
"orphan": true,
|
||||||
|
"renewable": false,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{
|
||||||
|
Policies: []string{"default"},
|
||||||
|
Type: "service",
|
||||||
|
}, "testrole")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
client.SetToken(secret.Auth.ClientToken)
|
||||||
|
_, err = client.Auth().Token().LookupSelf()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if secret.Auth.ClientToken[0:2] != "b." {
|
||||||
|
t.Fatal(secret.Auth.ClientToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back to normal
|
||||||
|
client.SetToken(rootToken)
|
||||||
|
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
||||||
|
"token_type": "service",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{
|
||||||
|
Policies: []string{"default"},
|
||||||
|
Type: "batch",
|
||||||
|
}, "testrole")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
client.SetToken(secret.Auth.ClientToken)
|
||||||
|
_, err = client.Auth().Token().LookupSelf()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if secret.Auth.ClientToken[0:2] == "b." {
|
||||||
|
t.Fatal(secret.Auth.ClientToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -223,6 +223,7 @@ func (c *Core) StepDown(httpCtx context.Context, req *logical.Request) (retErr e
|
|||||||
// Audit-log the request before going any further
|
// Audit-log the request before going any further
|
||||||
auth := &logical.Auth{
|
auth := &logical.Auth{
|
||||||
ClientToken: req.ClientToken,
|
ClientToken: req.ClientToken,
|
||||||
|
Accessor: req.ClientTokenAccessor,
|
||||||
}
|
}
|
||||||
if te != nil {
|
if te != nil {
|
||||||
auth.IdentityPolicies = identityPolicies[te.NamespaceID]
|
auth.IdentityPolicies = identityPolicies[te.NamespaceID]
|
||||||
@ -233,6 +234,7 @@ func (c *Core) StepDown(httpCtx context.Context, req *logical.Request) (retErr e
|
|||||||
auth.Metadata = te.Meta
|
auth.Metadata = te.Meta
|
||||||
auth.DisplayName = te.DisplayName
|
auth.DisplayName = te.DisplayName
|
||||||
auth.EntityID = te.EntityID
|
auth.EntityID = te.EntityID
|
||||||
|
auth.TokenType = te.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
logInput := &audit.LogInput{
|
logInput := &audit.LogInput{
|
||||||
|
|||||||
@ -601,6 +601,9 @@ func mountInfo(entry *MountEntry) map[string]interface{} {
|
|||||||
if rawVal, ok := entry.synthesizedConfigCache.Load("passthrough_request_headers"); ok {
|
if rawVal, ok := entry.synthesizedConfigCache.Load("passthrough_request_headers"); ok {
|
||||||
entryConfig["passthrough_request_headers"] = rawVal.([]string)
|
entryConfig["passthrough_request_headers"] = rawVal.([]string)
|
||||||
}
|
}
|
||||||
|
if entry.Table == credentialTableType {
|
||||||
|
entryConfig["token_type"] = entry.Config.TokenType.String()
|
||||||
|
}
|
||||||
|
|
||||||
info["config"] = entryConfig
|
info["config"] = entryConfig
|
||||||
|
|
||||||
@ -951,6 +954,10 @@ func (b *SystemBackend) handleTuneReadCommon(ctx context.Context, path string) (
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mountEntry.Table == credentialTableType {
|
||||||
|
resp.Data["token_type"] = mountEntry.Config.TokenType.String()
|
||||||
|
}
|
||||||
|
|
||||||
if rawVal, ok := mountEntry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok {
|
if rawVal, ok := mountEntry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok {
|
||||||
resp.Data["audit_non_hmac_request_keys"] = rawVal.([]string)
|
resp.Data["audit_non_hmac_request_keys"] = rawVal.([]string)
|
||||||
}
|
}
|
||||||
@ -1192,6 +1199,44 @@ func (b *SystemBackend) handleTuneWriteCommon(ctx context.Context, path string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rawVal, ok := data.GetOk("token_type"); ok {
|
||||||
|
if !strings.HasPrefix(path, "auth/") {
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf("'token_type' can only be modified on auth mounts")), logical.ErrInvalidRequest
|
||||||
|
}
|
||||||
|
if mountEntry.Type == "token" || mountEntry.Type == "ns_token" {
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf("'token_type' cannot be set for 'token' or 'ns_token' auth mounts")), logical.ErrInvalidRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenType := logical.TokenTypeDefaultService
|
||||||
|
ttString := rawVal.(string)
|
||||||
|
|
||||||
|
switch ttString {
|
||||||
|
case "", "default-service":
|
||||||
|
case "default-batch":
|
||||||
|
tokenType = logical.TokenTypeDefaultBatch
|
||||||
|
case "service":
|
||||||
|
tokenType = logical.TokenTypeService
|
||||||
|
case "batch":
|
||||||
|
tokenType = logical.TokenTypeBatch
|
||||||
|
default:
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf(
|
||||||
|
"invalid value for 'token_type'")), logical.ErrInvalidRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal := mountEntry.Config.TokenType
|
||||||
|
mountEntry.Config.TokenType = tokenType
|
||||||
|
|
||||||
|
// Update the mount table
|
||||||
|
if err := b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local); err != nil {
|
||||||
|
mountEntry.Config.TokenType = oldVal
|
||||||
|
return handleError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Core.logger.IsInfo() {
|
||||||
|
b.Core.logger.Info("mount tuning of token_type successful", "path", path, "token_type", ttString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if rawVal, ok := data.GetOk("passthrough_request_headers"); ok {
|
if rawVal, ok := data.GetOk("passthrough_request_headers"); ok {
|
||||||
headers := rawVal.([]string)
|
headers := rawVal.([]string)
|
||||||
|
|
||||||
@ -1467,37 +1512,10 @@ func (b *SystemBackend) handleAuthTable(ctx context.Context, req *logical.Reques
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
info := map[string]interface{}{
|
info := mountInfo(entry)
|
||||||
"type": entry.Type,
|
|
||||||
"description": entry.Description,
|
|
||||||
"accessor": entry.Accessor,
|
|
||||||
"local": entry.Local,
|
|
||||||
"seal_wrap": entry.SealWrap,
|
|
||||||
"options": entry.Options,
|
|
||||||
}
|
|
||||||
entryConfig := map[string]interface{}{
|
|
||||||
"default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()),
|
|
||||||
"max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()),
|
|
||||||
"plugin_name": entry.Config.PluginName,
|
|
||||||
}
|
|
||||||
if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok {
|
|
||||||
entryConfig["audit_non_hmac_request_keys"] = rawVal.([]string)
|
|
||||||
}
|
|
||||||
if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_response_keys"); ok {
|
|
||||||
entryConfig["audit_non_hmac_response_keys"] = rawVal.([]string)
|
|
||||||
}
|
|
||||||
// Even though empty value is valid for ListingVisibility, we can ignore
|
|
||||||
// this case during mount since there's nothing to unset/hide.
|
|
||||||
if len(entry.Config.ListingVisibility) > 0 {
|
|
||||||
entryConfig["listing_visibility"] = entry.Config.ListingVisibility
|
|
||||||
}
|
|
||||||
if rawVal, ok := entry.synthesizedConfigCache.Load("passthrough_request_headers"); ok {
|
|
||||||
entryConfig["passthrough_request_headers"] = rawVal.([]string)
|
|
||||||
}
|
|
||||||
|
|
||||||
info["config"] = entryConfig
|
|
||||||
resp.Data[entry.Path] = info
|
resp.Data[entry.Path] = info
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1569,6 +1587,20 @@ func (b *SystemBackend) handleEnableAuth(ctx context.Context, req *logical.Reque
|
|||||||
logical.ErrInvalidRequest
|
logical.ErrInvalidRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch apiConfig.TokenType {
|
||||||
|
case "", "default-service":
|
||||||
|
config.TokenType = logical.TokenTypeDefaultService
|
||||||
|
case "default-batch":
|
||||||
|
config.TokenType = logical.TokenTypeDefaultBatch
|
||||||
|
case "service":
|
||||||
|
config.TokenType = logical.TokenTypeService
|
||||||
|
case "batch":
|
||||||
|
config.TokenType = logical.TokenTypeBatch
|
||||||
|
default:
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf(
|
||||||
|
"invalid value for 'token_type'")), logical.ErrInvalidRequest
|
||||||
|
}
|
||||||
|
|
||||||
switch logicalType {
|
switch logicalType {
|
||||||
case "":
|
case "":
|
||||||
return logical.ErrorResponse(
|
return logical.ErrorResponse(
|
||||||
@ -3581,6 +3613,10 @@ This path responds to the following HTTP methods.
|
|||||||
"A list of headers to whitelist and pass from the request to the backend.",
|
"A list of headers to whitelist and pass from the request to the backend.",
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
|
"token_type": {
|
||||||
|
"The type of token to issue (service or batch).",
|
||||||
|
"",
|
||||||
|
},
|
||||||
"raw": {
|
"raw": {
|
||||||
"Write, Read, and Delete data directly in the Storage backend.",
|
"Write, Read, and Delete data directly in the Storage backend.",
|
||||||
"",
|
"",
|
||||||
|
|||||||
@ -1010,6 +1010,10 @@ func (b *SystemBackend) mountPaths() []*framework.Path {
|
|||||||
Type: framework.TypeCommaStringSlice,
|
Type: framework.TypeCommaStringSlice,
|
||||||
Description: strings.TrimSpace(sysHelp["passthrough_request_headers"][0]),
|
Description: strings.TrimSpace(sysHelp["passthrough_request_headers"][0]),
|
||||||
},
|
},
|
||||||
|
"token_type": &framework.FieldSchema{
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Description: strings.TrimSpace(sysHelp["token_type"][0]),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatih/structs"
|
"github.com/fatih/structs"
|
||||||
|
"github.com/go-test/deep"
|
||||||
hclog "github.com/hashicorp/go-hclog"
|
hclog "github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/vault/audit"
|
"github.com/hashicorp/vault/audit"
|
||||||
"github.com/hashicorp/vault/helper/builtinplugins"
|
"github.com/hashicorp/vault/helper/builtinplugins"
|
||||||
@ -447,7 +448,7 @@ func TestSystemBackend_PathCapabilities(t *testing.T) {
|
|||||||
rootCheckFunc(t, resp)
|
rootCheckFunc(t, resp)
|
||||||
|
|
||||||
// Create a non-root token
|
// Create a non-root token
|
||||||
testMakeTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"test"})
|
testMakeServiceTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"test"})
|
||||||
|
|
||||||
nonRootCheckFunc := func(t *testing.T, resp *logical.Response) {
|
nonRootCheckFunc := func(t *testing.T, resp *logical.Response) {
|
||||||
expected1 := []string{"create", "sudo", "update"}
|
expected1 := []string{"create", "sudo", "update"}
|
||||||
@ -549,7 +550,7 @@ func testCapabilities(t *testing.T, endpoint string) {
|
|||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
testMakeTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"test"})
|
testMakeServiceTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"test"})
|
||||||
req = logical.TestRequest(t, logical.UpdateOperation, endpoint)
|
req = logical.TestRequest(t, logical.UpdateOperation, endpoint)
|
||||||
if endpoint == "capabilities-self" {
|
if endpoint == "capabilities-self" {
|
||||||
req.ClientToken = "tokenid"
|
req.ClientToken = "tokenid"
|
||||||
@ -605,7 +606,7 @@ func TestSystemBackend_CapabilitiesAccessor_BC(t *testing.T) {
|
|||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
testMakeTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"test"})
|
testMakeServiceTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"test"})
|
||||||
|
|
||||||
te, err = core.tokenStore.Lookup(namespace.TestContext(), "tokenid")
|
te, err = core.tokenStore.Lookup(namespace.TestContext(), "tokenid")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -696,6 +697,7 @@ func TestSystemBackend_leases(t *testing.T) {
|
|||||||
// Read a key with a LeaseID
|
// Read a key with a LeaseID
|
||||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -742,6 +744,7 @@ func TestSystemBackend_leases_list(t *testing.T) {
|
|||||||
// Read a key with a LeaseID
|
// Read a key with a LeaseID
|
||||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -790,6 +793,7 @@ func TestSystemBackend_leases_list(t *testing.T) {
|
|||||||
// Generate multiple leases
|
// Generate multiple leases
|
||||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -800,6 +804,7 @@ func TestSystemBackend_leases_list(t *testing.T) {
|
|||||||
|
|
||||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -839,6 +844,7 @@ func TestSystemBackend_leases_list(t *testing.T) {
|
|||||||
// Read a key with a LeaseID
|
// Read a key with a LeaseID
|
||||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/bar")
|
req = logical.TestRequest(t, logical.ReadOperation, "secret/bar")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -886,6 +892,7 @@ func TestSystemBackend_renew(t *testing.T) {
|
|||||||
// Read a key with a LeaseID
|
// Read a key with a LeaseID
|
||||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -922,6 +929,7 @@ func TestSystemBackend_renew(t *testing.T) {
|
|||||||
// Read a key with a LeaseID
|
// Read a key with a LeaseID
|
||||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -976,7 +984,7 @@ func TestSystemBackend_renew(t *testing.T) {
|
|||||||
if resp2.Data == nil {
|
if resp2.Data == nil {
|
||||||
t.Fatal("nil data")
|
t.Fatal("nil data")
|
||||||
}
|
}
|
||||||
if resp.Secret.TTL != 180*time.Second {
|
if resp.Secret.TTL != time.Second*180 {
|
||||||
t.Fatalf("bad lease duration: %v", resp.Secret.TTL)
|
t.Fatalf("bad lease duration: %v", resp.Secret.TTL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -990,7 +998,7 @@ func TestSystemBackend_renew_invalidID(t *testing.T) {
|
|||||||
if err != logical.ErrInvalidRequest {
|
if err != logical.ErrInvalidRequest {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
if resp.Data["error"] != "lease not found or lease is not renewable" {
|
if resp.Data["error"] != "lease not found" {
|
||||||
t.Fatalf("bad: %v", resp)
|
t.Fatalf("bad: %v", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1001,7 +1009,7 @@ func TestSystemBackend_renew_invalidID(t *testing.T) {
|
|||||||
if err != logical.ErrInvalidRequest {
|
if err != logical.ErrInvalidRequest {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
if resp.Data["error"] != "lease not found or lease is not renewable" {
|
if resp.Data["error"] != "lease not found" {
|
||||||
t.Fatalf("bad: %v", resp)
|
t.Fatalf("bad: %v", resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1015,7 +1023,7 @@ func TestSystemBackend_renew_invalidID_origUrl(t *testing.T) {
|
|||||||
if err != logical.ErrInvalidRequest {
|
if err != logical.ErrInvalidRequest {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
if resp.Data["error"] != "lease not found or lease is not renewable" {
|
if resp.Data["error"] != "lease not found" {
|
||||||
t.Fatalf("bad: %v", resp)
|
t.Fatalf("bad: %v", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1026,7 +1034,7 @@ func TestSystemBackend_renew_invalidID_origUrl(t *testing.T) {
|
|||||||
if err != logical.ErrInvalidRequest {
|
if err != logical.ErrInvalidRequest {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
if resp.Data["error"] != "lease not found or lease is not renewable" {
|
if resp.Data["error"] != "lease not found" {
|
||||||
t.Fatalf("bad: %v", resp)
|
t.Fatalf("bad: %v", resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1050,6 +1058,7 @@ func TestSystemBackend_revoke(t *testing.T) {
|
|||||||
// Read a key with a LeaseID
|
// Read a key with a LeaseID
|
||||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -1074,13 +1083,14 @@ func TestSystemBackend_revoke(t *testing.T) {
|
|||||||
if err != logical.ErrInvalidRequest {
|
if err != logical.ErrInvalidRequest {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
if resp3.Data["error"] != "lease not found or lease is not renewable" {
|
if resp3.Data["error"] != "lease not found" {
|
||||||
t.Fatalf("bad: %v", resp)
|
t.Fatalf("bad: %v", *resp3)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read a key with a LeaseID
|
// Read a key with a LeaseID
|
||||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -1103,6 +1113,7 @@ func TestSystemBackend_revoke(t *testing.T) {
|
|||||||
// Read a key with a LeaseID
|
// Read a key with a LeaseID
|
||||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -1192,6 +1203,7 @@ func TestSystemBackend_revokePrefix(t *testing.T) {
|
|||||||
// Read a key with a LeaseID
|
// Read a key with a LeaseID
|
||||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -1216,8 +1228,8 @@ func TestSystemBackend_revokePrefix(t *testing.T) {
|
|||||||
if err != logical.ErrInvalidRequest {
|
if err != logical.ErrInvalidRequest {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
if resp3.Data["error"] != "lease not found or lease is not renewable" {
|
if resp3.Data["error"] != "lease not found" {
|
||||||
t.Fatalf("bad: %v", resp)
|
t.Fatalf("bad: %v", *resp3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1240,6 +1252,7 @@ func TestSystemBackend_revokePrefix_origUrl(t *testing.T) {
|
|||||||
// Read a key with a LeaseID
|
// Read a key with a LeaseID
|
||||||
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
|
req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -1264,8 +1277,8 @@ func TestSystemBackend_revokePrefix_origUrl(t *testing.T) {
|
|||||||
if err != logical.ErrInvalidRequest {
|
if err != logical.ErrInvalidRequest {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
if resp3.Data["error"] != "lease not found or lease is not renewable" {
|
if resp3.Data["error"] != "lease not found" {
|
||||||
t.Fatalf("bad: %v", resp)
|
t.Fatalf("bad: %#v", *resp3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1415,14 +1428,16 @@ func TestSystemBackend_authTable(t *testing.T) {
|
|||||||
"default_lease_ttl": int64(0),
|
"default_lease_ttl": int64(0),
|
||||||
"max_lease_ttl": int64(0),
|
"max_lease_ttl": int64(0),
|
||||||
"plugin_name": "",
|
"plugin_name": "",
|
||||||
|
"force_no_cache": false,
|
||||||
|
"token_type": "default-service",
|
||||||
},
|
},
|
||||||
"local": false,
|
"local": false,
|
||||||
"seal_wrap": false,
|
"seal_wrap": false,
|
||||||
"options": map[string]string(nil),
|
"options": map[string]string(nil),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(resp.Data, exp) {
|
if diff := deep.Equal(resp.Data, exp); diff != nil {
|
||||||
t.Fatalf("got: %#v expect: %#v", resp.Data, exp)
|
t.Fatal(diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1466,7 +1481,9 @@ func TestSystemBackend_enableAuth(t *testing.T) {
|
|||||||
"config": map[string]interface{}{
|
"config": map[string]interface{}{
|
||||||
"default_lease_ttl": int64(2100),
|
"default_lease_ttl": int64(2100),
|
||||||
"max_lease_ttl": int64(2700),
|
"max_lease_ttl": int64(2700),
|
||||||
|
"force_no_cache": false,
|
||||||
"plugin_name": "",
|
"plugin_name": "",
|
||||||
|
"token_type": "default-service",
|
||||||
},
|
},
|
||||||
"local": true,
|
"local": true,
|
||||||
"seal_wrap": true,
|
"seal_wrap": true,
|
||||||
@ -1480,14 +1497,16 @@ func TestSystemBackend_enableAuth(t *testing.T) {
|
|||||||
"default_lease_ttl": int64(0),
|
"default_lease_ttl": int64(0),
|
||||||
"max_lease_ttl": int64(0),
|
"max_lease_ttl": int64(0),
|
||||||
"plugin_name": "",
|
"plugin_name": "",
|
||||||
|
"force_no_cache": false,
|
||||||
|
"token_type": "default-service",
|
||||||
},
|
},
|
||||||
"local": false,
|
"local": false,
|
||||||
"seal_wrap": false,
|
"seal_wrap": false,
|
||||||
"options": map[string]string(nil),
|
"options": map[string]string(nil),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(resp.Data, exp) {
|
if diff := deep.Equal(resp.Data, exp); diff != nil {
|
||||||
t.Fatalf("got: %#v expect: %#v", resp.Data, exp)
|
t.Fatal(diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2300,6 +2319,7 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) {
|
|||||||
"max_lease_ttl": int64(0),
|
"max_lease_ttl": int64(0),
|
||||||
"force_no_cache": false,
|
"force_no_cache": false,
|
||||||
"plugin_name": "",
|
"plugin_name": "",
|
||||||
|
"token_type": "default-service",
|
||||||
},
|
},
|
||||||
"type": "token",
|
"type": "token",
|
||||||
"description": "token based credentials",
|
"description": "token based credentials",
|
||||||
@ -2309,8 +2329,8 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(resp.Data, exp) {
|
if diff := deep.Equal(resp.Data, exp); diff != nil {
|
||||||
t.Fatalf("got: %#v \n\n expect: %#v", resp.Data, exp)
|
t.Fatal(diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mount-tune an auth mount
|
// Mount-tune an auth mount
|
||||||
@ -2391,7 +2411,7 @@ func TestSystemBackend_InternalUIMount(t *testing.T) {
|
|||||||
t.Fatalf("Bad Response: %#v", resp)
|
t.Fatalf("Bad Response: %#v", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
testMakeTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"secret"})
|
testMakeServiceTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"secret"})
|
||||||
|
|
||||||
req = logical.TestRequest(t, logical.ReadOperation, "internal/ui/mounts/kv")
|
req = logical.TestRequest(t, logical.ReadOperation, "internal/ui/mounts/kv")
|
||||||
req.ClientToken = "tokenid"
|
req.ClientToken = "tokenid"
|
||||||
|
|||||||
@ -226,6 +226,7 @@ type MountConfig struct {
|
|||||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
||||||
ListingVisibility ListingVisibilityType `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
ListingVisibility ListingVisibilityType `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
||||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"`
|
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"`
|
||||||
|
TokenType logical.TokenType `json:"token_type" structs:"token_type" mapstructure:"token_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIMountConfig is an embedded struct of api.MountConfigInput
|
// APIMountConfig is an embedded struct of api.MountConfigInput
|
||||||
@ -238,6 +239,7 @@ type APIMountConfig struct {
|
|||||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" structs:"audit_non_hmac_response_keys" mapstructure:"audit_non_hmac_response_keys"`
|
||||||
ListingVisibility ListingVisibilityType `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
ListingVisibility ListingVisibilityType `json:"listing_visibility,omitempty" structs:"listing_visibility" mapstructure:"listing_visibility"`
|
||||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"`
|
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" structs:"passthrough_request_headers" mapstructure:"passthrough_request_headers"`
|
||||||
|
TokenType string `json:"token_type" structs:"token_type" mapstructure:"token_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone returns a deep copy of the mount entry
|
// Clone returns a deep copy of the mount entry
|
||||||
|
|||||||
@ -295,6 +295,7 @@ func TestCore_Unmount_Cleanup(t *testing.T) {
|
|||||||
Path: "test/foo",
|
Path: "test/foo",
|
||||||
ClientToken: root,
|
ClientToken: root,
|
||||||
}
|
}
|
||||||
|
r.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err := c.HandleRequest(namespace.TestContext(), r)
|
resp, err := c.HandleRequest(namespace.TestContext(), r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
@ -415,6 +416,7 @@ func TestCore_Remount_Cleanup(t *testing.T) {
|
|||||||
Path: "test/foo",
|
Path: "test/foo",
|
||||||
ClientToken: root,
|
ClientToken: root,
|
||||||
}
|
}
|
||||||
|
r.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}})
|
||||||
resp, err := c.HandleRequest(namespace.TestContext(), r)
|
resp, err := c.HandleRequest(namespace.TestContext(), r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
|
|||||||
@ -269,19 +269,22 @@ func (c *Core) checkToken(ctx context.Context, req *logical.Request, unauth bool
|
|||||||
// whether a particular resource exists. Then we can mark it as an update
|
// whether a particular resource exists. Then we can mark it as an update
|
||||||
// or creation as appropriate.
|
// or creation as appropriate.
|
||||||
if req.Operation == logical.CreateOperation || req.Operation == logical.UpdateOperation {
|
if req.Operation == logical.CreateOperation || req.Operation == logical.UpdateOperation {
|
||||||
checkExists, resourceExists, err := c.router.RouteExistenceCheck(ctx, req)
|
existsResp, checkExists, resourceExists, err := c.router.RouteExistenceCheck(ctx, req)
|
||||||
switch err {
|
switch err {
|
||||||
case logical.ErrUnsupportedPath:
|
case logical.ErrUnsupportedPath:
|
||||||
// fail later via bad path to avoid confusing items in the log
|
// fail later via bad path to avoid confusing items in the log
|
||||||
checkExists = false
|
checkExists = false
|
||||||
case nil:
|
case nil:
|
||||||
// Continue on
|
if existsResp != nil && existsResp.IsError() {
|
||||||
|
return nil, te, existsResp.Error()
|
||||||
|
}
|
||||||
|
// Otherwise, continue on
|
||||||
default:
|
default:
|
||||||
c.logger.Error("failed to run existence check", "error", err)
|
c.logger.Error("failed to run existence check", "error", err)
|
||||||
if _, ok := err.(errutil.UserError); ok {
|
if _, ok := err.(errutil.UserError); ok {
|
||||||
return nil, nil, err
|
return nil, te, err
|
||||||
} else {
|
} else {
|
||||||
return nil, nil, ErrInternalError
|
return nil, te, ErrInternalError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,6 +319,7 @@ func (c *Core) checkToken(ctx context.Context, req *logical.Request, unauth bool
|
|||||||
auth.ExternalNamespacePolicies = identityPolicies
|
auth.ExternalNamespacePolicies = identityPolicies
|
||||||
// Store the entity ID in the request object
|
// Store the entity ID in the request object
|
||||||
req.EntityID = te.EntityID
|
req.EntityID = te.EntityID
|
||||||
|
auth.TokenType = te.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the standard non-root ACLs. Return the token entry if it's not
|
// Check the standard non-root ACLs. Return the token entry if it's not
|
||||||
@ -744,6 +748,19 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp
|
|||||||
return nil, auth, retErr
|
return nil, auth, retErr
|
||||||
}
|
}
|
||||||
resp.Secret.LeaseID = leaseID
|
resp.Secret.LeaseID = leaseID
|
||||||
|
|
||||||
|
// Get the actual time of the lease
|
||||||
|
le, err := c.expiration.FetchLeaseTimes(ctx, leaseID)
|
||||||
|
if err != nil {
|
||||||
|
c.logger.Error("failed to fetch updated lease time", "request_path", req.Path, "error", err)
|
||||||
|
retErr = multierror.Append(retErr, ErrInternalError)
|
||||||
|
return nil, auth, retErr
|
||||||
|
}
|
||||||
|
// We round here because the clock will have already started
|
||||||
|
// ticking, so we'll end up always returning 299 instead of 300 or
|
||||||
|
// 26399 instead of 26400, say, even if it's just a few
|
||||||
|
// microseconds. This provides a nicer UX.
|
||||||
|
resp.Secret.TTL = le.ExpireTime.Sub(time.Now()).Round(time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -777,14 +794,18 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp.Auth.TokenPolicies = policyutil.SanitizePolicies(resp.Auth.Policies, policyutil.DoNotAddDefaultPolicy)
|
resp.Auth.TokenPolicies = policyutil.SanitizePolicies(resp.Auth.Policies, policyutil.DoNotAddDefaultPolicy)
|
||||||
if err := c.expiration.RegisterAuth(ctx, &logical.TokenEntry{
|
switch resp.Auth.TokenType {
|
||||||
Path: resp.Auth.CreationPath,
|
case logical.TokenTypeBatch:
|
||||||
NamespaceID: ns.ID,
|
case logical.TokenTypeService:
|
||||||
}, resp.Auth); err != nil {
|
if err := c.expiration.RegisterAuth(ctx, &logical.TokenEntry{
|
||||||
c.tokenStore.revokeOrphan(ctx, te.ID)
|
Path: resp.Auth.CreationPath,
|
||||||
c.logger.Error("failed to register token lease", "request_path", req.Path, "error", err)
|
NamespaceID: ns.ID,
|
||||||
retErr = multierror.Append(retErr, ErrInternalError)
|
}, resp.Auth); err != nil {
|
||||||
return nil, auth, retErr
|
c.tokenStore.revokeOrphan(ctx, te.ID)
|
||||||
|
c.logger.Error("failed to register token lease", "request_path", req.Path, "error", err)
|
||||||
|
retErr = multierror.Append(retErr, ErrInternalError)
|
||||||
|
return nil, auth, retErr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We do these later since it's not meaningful for backends/expmgr to
|
// We do these later since it's not meaningful for backends/expmgr to
|
||||||
@ -1031,7 +1052,14 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registerFunc, funcGetErr := getAuthRegisterFunc(c)
|
var registerFunc RegisterAuthFunc
|
||||||
|
var funcGetErr error
|
||||||
|
// Batch tokens should not be forwarded to perf standby
|
||||||
|
if auth.TokenType == logical.TokenTypeBatch {
|
||||||
|
registerFunc = c.RegisterAuth
|
||||||
|
} else {
|
||||||
|
registerFunc, funcGetErr = getAuthRegisterFunc(c)
|
||||||
|
}
|
||||||
if funcGetErr != nil {
|
if funcGetErr != nil {
|
||||||
retErr = multierror.Append(retErr, funcGetErr)
|
retErr = multierror.Append(retErr, funcGetErr)
|
||||||
return nil, auth, retErr
|
return nil, auth, retErr
|
||||||
@ -1083,6 +1111,7 @@ func (c *Core) RegisterAuth(ctx context.Context, tokenTTL time.Duration, path st
|
|||||||
Policies: auth.TokenPolicies,
|
Policies: auth.TokenPolicies,
|
||||||
NamespaceID: ns.ID,
|
NamespaceID: ns.ID,
|
||||||
ExplicitMaxTTL: auth.ExplicitMaxTTL,
|
ExplicitMaxTTL: auth.ExplicitMaxTTL,
|
||||||
|
Type: auth.TokenType,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.tokenStore.create(ctx, &te); err != nil {
|
if err := c.tokenStore.create(ctx, &te); err != nil {
|
||||||
@ -1095,11 +1124,17 @@ func (c *Core) RegisterAuth(ctx context.Context, tokenTTL time.Duration, path st
|
|||||||
auth.Accessor = te.Accessor
|
auth.Accessor = te.Accessor
|
||||||
auth.TTL = te.TTL
|
auth.TTL = te.TTL
|
||||||
|
|
||||||
// Register with the expiration manager
|
switch auth.TokenType {
|
||||||
if err := c.expiration.RegisterAuth(ctx, &te, auth); err != nil {
|
case logical.TokenTypeBatch:
|
||||||
c.tokenStore.revokeOrphan(ctx, te.ID)
|
// Ensure it's not marked renewable since it isn't
|
||||||
c.logger.Error("failed to register token lease", "request_path", path, "error", err)
|
auth.Renewable = false
|
||||||
return ErrInternalError
|
case logical.TokenTypeService:
|
||||||
|
// Register with the expiration manager
|
||||||
|
if err := c.expiration.RegisterAuth(ctx, &te, auth); err != nil {
|
||||||
|
c.tokenStore.revokeOrphan(ctx, te.ID)
|
||||||
|
c.logger.Error("failed to register token lease", "request_path", path, "error", err)
|
||||||
|
return ErrInternalError
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -468,9 +468,9 @@ func (r *Router) Route(ctx context.Context, req *logical.Request) (*logical.Resp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RouteExistenceCheck is used to route a given existence check request
|
// RouteExistenceCheck is used to route a given existence check request
|
||||||
func (r *Router) RouteExistenceCheck(ctx context.Context, req *logical.Request) (bool, bool, error) {
|
func (r *Router) RouteExistenceCheck(ctx context.Context, req *logical.Request) (*logical.Response, bool, bool, error) {
|
||||||
_, ok, exists, err := r.routeCommon(ctx, req, true)
|
resp, ok, exists, err := r.routeCommon(ctx, req, true)
|
||||||
return ok, exists, err
|
return resp, ok, exists, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenceCheck bool) (*logical.Response, bool, bool, error) {
|
func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenceCheck bool) (*logical.Response, bool, bool, error) {
|
||||||
@ -547,11 +547,17 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc
|
|||||||
return nil, false, false, nil
|
return nil, false, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.TokenEntry() == nil {
|
te := req.TokenEntry()
|
||||||
|
|
||||||
|
if te == nil {
|
||||||
return nil, false, false, fmt.Errorf("nil token entry")
|
return nil, false, false, fmt.Errorf("nil token entry")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch req.TokenEntry().NamespaceID {
|
if te.Type != logical.TokenTypeService {
|
||||||
|
return logical.ErrorResponse(`cubbyhole operations are only supported by "service" type tokens`), false, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch te.NamespaceID {
|
||||||
case namespace.RootNamespaceID:
|
case namespace.RootNamespaceID:
|
||||||
// In order for the token store to revoke later, we need to have the same
|
// In order for the token store to revoke later, we need to have the same
|
||||||
// salted ID, so we double-salt what's going to the cubbyhole backend
|
// salted ID, so we double-salt what's going to the cubbyhole backend
|
||||||
@ -562,10 +568,10 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc
|
|||||||
req.ClientToken = re.SaltID(salt.SaltID(req.ClientToken))
|
req.ClientToken = re.SaltID(salt.SaltID(req.ClientToken))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if req.TokenEntry().CubbyholeID == "" {
|
if te.CubbyholeID == "" {
|
||||||
return nil, false, false, fmt.Errorf("empty cubbyhole id")
|
return nil, false, false, fmt.Errorf("empty cubbyhole id")
|
||||||
}
|
}
|
||||||
req.ClientToken = req.TokenEntry().CubbyholeID
|
req.ClientToken = te.CubbyholeID
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -644,25 +650,45 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc
|
|||||||
return nil, ok, exists, err
|
return nil, ok, exists, err
|
||||||
} else {
|
} else {
|
||||||
resp, err := re.backend.HandleRequest(ctx, req)
|
resp, err := re.backend.HandleRequest(ctx, req)
|
||||||
// When a token gets renewed, the request hits this path and reaches
|
|
||||||
// token store. Token store delegates the renewal to the expiration
|
|
||||||
// manager. Expiration manager in-turn creates a different logical
|
|
||||||
// request and forwards the request to the auth backend that had
|
|
||||||
// initially authenticated the login request. The forwarding to auth
|
|
||||||
// backend will make this code path hit for the second time for the
|
|
||||||
// same renewal request. The accessors in the Alias structs should be
|
|
||||||
// of the auth backend and not of the token store. Therefore, avoiding
|
|
||||||
// the overwriting of accessors by having a check for path prefix
|
|
||||||
// having "renew". This gets applied for "renew" and "renew-self"
|
|
||||||
// requests.
|
|
||||||
if resp != nil &&
|
if resp != nil &&
|
||||||
resp.Auth != nil &&
|
resp.Auth != nil {
|
||||||
!strings.HasPrefix(req.Path, "renew") {
|
// When a token gets renewed, the request hits this path and
|
||||||
if resp.Auth.Alias != nil {
|
// reaches token store. Token store delegates the renewal to the
|
||||||
resp.Auth.Alias.MountAccessor = re.mountEntry.Accessor
|
// expiration manager. Expiration manager in-turn creates a
|
||||||
|
// different logical request and forwards the request to the auth
|
||||||
|
// backend that had initially authenticated the login request. The
|
||||||
|
// forwarding to auth backend will make this code path hit for the
|
||||||
|
// second time for the same renewal request. The accessors in the
|
||||||
|
// Alias structs should be of the auth backend and not of the token
|
||||||
|
// store. Therefore, avoiding the overwriting of accessors by
|
||||||
|
// having a check for path prefix having "renew". This gets applied
|
||||||
|
// for "renew" and "renew-self" requests.
|
||||||
|
if !strings.HasPrefix(req.Path, "renew") {
|
||||||
|
if resp.Auth.Alias != nil {
|
||||||
|
resp.Auth.Alias.MountAccessor = re.mountEntry.Accessor
|
||||||
|
}
|
||||||
|
for _, alias := range resp.Auth.GroupAliases {
|
||||||
|
alias.MountAccessor = re.mountEntry.Accessor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, alias := range resp.Auth.GroupAliases {
|
|
||||||
alias.MountAccessor = re.mountEntry.Accessor
|
switch re.mountEntry.Type {
|
||||||
|
case "token", "ns_token":
|
||||||
|
// Nothing; we respect what the token store is telling us and
|
||||||
|
// we don't allow tuning
|
||||||
|
default:
|
||||||
|
switch re.mountEntry.Config.TokenType {
|
||||||
|
case logical.TokenTypeService, logical.TokenTypeBatch:
|
||||||
|
resp.Auth.TokenType = re.mountEntry.Config.TokenType
|
||||||
|
case logical.TokenTypeDefault, logical.TokenTypeDefaultService:
|
||||||
|
if resp.Auth.TokenType == logical.TokenTypeDefault {
|
||||||
|
resp.Auth.TokenType = logical.TokenTypeService
|
||||||
|
}
|
||||||
|
case logical.TokenTypeDefaultBatch:
|
||||||
|
if resp.Auth.TokenType == logical.TokenTypeDefault {
|
||||||
|
resp.Auth.TokenType = logical.TokenTypeBatch
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package vault
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -13,6 +14,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
"github.com/hashicorp/errwrap"
|
"github.com/hashicorp/errwrap"
|
||||||
log "github.com/hashicorp/go-hclog"
|
log "github.com/hashicorp/go-hclog"
|
||||||
sockaddr "github.com/hashicorp/go-sockaddr"
|
sockaddr "github.com/hashicorp/go-sockaddr"
|
||||||
@ -31,6 +33,7 @@ import (
|
|||||||
"github.com/hashicorp/vault/helper/strutil"
|
"github.com/hashicorp/vault/helper/strutil"
|
||||||
"github.com/hashicorp/vault/logical"
|
"github.com/hashicorp/vault/logical"
|
||||||
"github.com/hashicorp/vault/logical/framework"
|
"github.com/hashicorp/vault/logical/framework"
|
||||||
|
"github.com/hashicorp/vault/logical/plugin/pb"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -183,6 +186,12 @@ func (ts *TokenStore) paths() []*framework.Path {
|
|||||||
Type: framework.TypeCommaStringSlice,
|
Type: framework.TypeCommaStringSlice,
|
||||||
Description: `Comma separated string or JSON list of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.`,
|
Description: `Comma separated string or JSON list of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.`,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"token_type": &framework.FieldSchema{
|
||||||
|
Type: framework.TypeString,
|
||||||
|
Default: "service",
|
||||||
|
Description: "The type of token to generate, service or batch",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
@ -473,6 +482,8 @@ type TokenStore struct {
|
|||||||
|
|
||||||
core *Core
|
core *Core
|
||||||
|
|
||||||
|
batchTokenEncryptor BarrierEncryptor
|
||||||
|
|
||||||
baseBarrierView *BarrierView
|
baseBarrierView *BarrierView
|
||||||
idBarrierView *BarrierView
|
idBarrierView *BarrierView
|
||||||
accessorBarrierView *BarrierView
|
accessorBarrierView *BarrierView
|
||||||
@ -515,6 +526,7 @@ func NewTokenStore(ctx context.Context, logger log.Logger, core *Core, config *l
|
|||||||
t := &TokenStore{
|
t := &TokenStore{
|
||||||
activeContext: ctx,
|
activeContext: ctx,
|
||||||
core: core,
|
core: core,
|
||||||
|
batchTokenEncryptor: core.barrier,
|
||||||
baseBarrierView: view,
|
baseBarrierView: view,
|
||||||
idBarrierView: view.SubView(idPrefix),
|
idBarrierView: view.SubView(idPrefix),
|
||||||
accessorBarrierView: view.SubView(accessorPrefix),
|
accessorBarrierView: view.SubView(accessorPrefix),
|
||||||
@ -630,6 +642,9 @@ type tsRoleEntry struct {
|
|||||||
|
|
||||||
// The set of CIDRs that tokens generated using this role will be bound to
|
// The set of CIDRs that tokens generated using this role will be bound to
|
||||||
BoundCIDRs []*sockaddr.SockAddrMarshaler `json:"bound_cidrs"`
|
BoundCIDRs []*sockaddr.SockAddrMarshaler `json:"bound_cidrs"`
|
||||||
|
|
||||||
|
// The type of token this role should issue
|
||||||
|
TokenType logical.TokenType `json:"token_type" mapstructure:"token_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type accessorEntry struct {
|
type accessorEntry struct {
|
||||||
@ -673,6 +688,7 @@ func (ts *TokenStore) rootToken(ctx context.Context) (*logical.TokenEntry, error
|
|||||||
DisplayName: "root",
|
DisplayName: "root",
|
||||||
CreationTime: time.Now().Unix(),
|
CreationTime: time.Now().Unix(),
|
||||||
NamespaceID: namespace.RootNamespaceID,
|
NamespaceID: namespace.RootNamespaceID,
|
||||||
|
Type: logical.TokenTypeService,
|
||||||
}
|
}
|
||||||
if err := ts.create(ctx, te); err != nil {
|
if err := ts.create(ctx, te); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -771,14 +787,6 @@ func (ts *TokenStore) createAccessor(ctx context.Context, entry *logical.TokenEn
|
|||||||
// a newly generated ID if not provided.
|
// a newly generated ID if not provided.
|
||||||
func (ts *TokenStore) create(ctx context.Context, entry *logical.TokenEntry) error {
|
func (ts *TokenStore) create(ctx context.Context, entry *logical.TokenEntry) error {
|
||||||
defer metrics.MeasureSince([]string{"token", "create"}, time.Now())
|
defer metrics.MeasureSince([]string{"token", "create"}, time.Now())
|
||||||
// Generate an ID if necessary
|
|
||||||
if entry.ID == "" {
|
|
||||||
var err error
|
|
||||||
entry.ID, err = base62.Random(TokenLength, true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenNS, err := NamespaceByID(ctx, entry.NamespaceID, ts.core)
|
tokenNS, err := NamespaceByID(ctx, entry.NamespaceID, ts.core)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -788,30 +796,99 @@ func (ts *TokenStore) create(ctx context.Context, entry *logical.TokenEntry) err
|
|||||||
return namespace.ErrNoNamespace
|
return namespace.ErrNoNamespace
|
||||||
}
|
}
|
||||||
|
|
||||||
if tokenNS.ID != namespace.RootNamespaceID {
|
entry.Policies = policyutil.SanitizePolicies(entry.Policies, policyutil.DoNotAddDefaultPolicy)
|
||||||
entry.ID = fmt.Sprintf("%s.%s", entry.ID, tokenNS.ID)
|
|
||||||
if entry.CubbyholeID == "" {
|
switch entry.Type {
|
||||||
cubbyholeID, err := base62.Random(TokenLength, true)
|
case logical.TokenTypeDefault, logical.TokenTypeService:
|
||||||
|
// In case it was default, force to service
|
||||||
|
entry.Type = logical.TokenTypeService
|
||||||
|
|
||||||
|
// Generate an ID if necessary
|
||||||
|
userSelectedID := true
|
||||||
|
if entry.ID == "" {
|
||||||
|
userSelectedID = false
|
||||||
|
var err error
|
||||||
|
entry.ID, err = base62.Random(TokenLength, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
entry.CubbyholeID = cubbyholeID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tokenNS.ID != namespace.RootNamespaceID {
|
||||||
|
entry.ID = fmt.Sprintf("%s.%s", entry.ID, tokenNS.ID)
|
||||||
|
if entry.CubbyholeID == "" {
|
||||||
|
cubbyholeID, err := base62.Random(TokenLength, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
entry.CubbyholeID = cubbyholeID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user didn't specifically pick the ID, e.g. because they were
|
||||||
|
// sudo/root, check for collision; otherwise trust the process
|
||||||
|
if userSelectedID {
|
||||||
|
exist, _ := ts.lookupInternal(ctx, entry.ID, false, true)
|
||||||
|
if exist != nil {
|
||||||
|
return fmt.Errorf("cannot create a token with a duplicate ID")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ts.createAccessor(ctx, entry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts.storeCommon(ctx, entry, true)
|
||||||
|
|
||||||
|
case logical.TokenTypeBatch:
|
||||||
|
// Ensure fields we don't support/care about are nilled, proto marshal,
|
||||||
|
// encrypt, skip persistence
|
||||||
|
entry.ID = ""
|
||||||
|
pEntry := &pb.TokenEntry{
|
||||||
|
Parent: entry.Parent,
|
||||||
|
Policies: entry.Policies,
|
||||||
|
Path: entry.Path,
|
||||||
|
Meta: entry.Meta,
|
||||||
|
DisplayName: entry.DisplayName,
|
||||||
|
CreationTime: entry.CreationTime,
|
||||||
|
TTL: int64(entry.TTL),
|
||||||
|
Role: entry.Role,
|
||||||
|
EntityID: entry.EntityID,
|
||||||
|
NamespaceID: entry.NamespaceID,
|
||||||
|
Type: uint32(entry.Type),
|
||||||
|
}
|
||||||
|
|
||||||
|
boundCIDRs := make([]string, len(entry.BoundCIDRs))
|
||||||
|
for i, cidr := range entry.BoundCIDRs {
|
||||||
|
boundCIDRs[i] = cidr.String()
|
||||||
|
}
|
||||||
|
pEntry.BoundCIDRs = boundCIDRs
|
||||||
|
|
||||||
|
mEntry, err := proto.Marshal(pEntry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
eEntry, err := ts.batchTokenEncryptor.Encrypt(ctx, "", mEntry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bEntry := base64.RawURLEncoding.EncodeToString(eEntry)
|
||||||
|
entry.ID = fmt.Sprintf("b.%s", bEntry)
|
||||||
|
|
||||||
|
if tokenNS.ID != namespace.RootNamespaceID {
|
||||||
|
entry.ID = fmt.Sprintf("%s.%s", entry.ID, tokenNS.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("cannot create a token of type %d", entry.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
exist, _ := ts.lookupInternal(ctx, entry.ID, false, true)
|
return errors.New("unreachable code")
|
||||||
if exist != nil {
|
|
||||||
return fmt.Errorf("cannot create a token with a duplicate ID")
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.Policies = policyutil.SanitizePolicies(entry.Policies, policyutil.DoNotAddDefaultPolicy)
|
|
||||||
|
|
||||||
err = ts.createAccessor(ctx, entry)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ts.storeCommon(ctx, entry, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store is used to store an updated token entry without writing the
|
// Store is used to store an updated token entry without writing the
|
||||||
@ -975,6 +1052,11 @@ func (ts *TokenStore) Lookup(ctx context.Context, id string) (*logical.TokenEntr
|
|||||||
return nil, fmt.Errorf("cannot lookup blank token")
|
return nil, fmt.Errorf("cannot lookup blank token")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it starts with "b-" it's a batch token
|
||||||
|
if len(id) > 2 && id[0:2] == "b." {
|
||||||
|
return ts.lookupBatchToken(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
lock := locksutil.LockForKey(ts.tokenLocks, id)
|
lock := locksutil.LockForKey(ts.tokenLocks, id)
|
||||||
lock.RLock()
|
lock.RLock()
|
||||||
defer lock.RUnlock()
|
defer lock.RUnlock()
|
||||||
@ -982,8 +1064,8 @@ func (ts *TokenStore) Lookup(ctx context.Context, id string) (*logical.TokenEntr
|
|||||||
return ts.lookupInternal(ctx, id, false, false)
|
return ts.lookupInternal(ctx, id, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookupTainted is used to find a token that may or maynot be tainted given its
|
// lookupTainted is used to find a token that may or may not be tainted given
|
||||||
// ID. It acquires a read lock, then calls lookupInternal.
|
// its ID. It acquires a read lock, then calls lookupInternal.
|
||||||
func (ts *TokenStore) lookupTainted(ctx context.Context, id string) (*logical.TokenEntry, error) {
|
func (ts *TokenStore) lookupTainted(ctx context.Context, id string) (*logical.TokenEntry, error) {
|
||||||
defer metrics.MeasureSince([]string{"token", "lookup"}, time.Now())
|
defer metrics.MeasureSince([]string{"token", "lookup"}, time.Now())
|
||||||
if id == "" {
|
if id == "" {
|
||||||
@ -997,6 +1079,48 @@ func (ts *TokenStore) lookupTainted(ctx context.Context, id string) (*logical.To
|
|||||||
return ts.lookupInternal(ctx, id, false, true)
|
return ts.lookupInternal(ctx, id, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ts *TokenStore) lookupBatchToken(ctx context.Context, id string) (*logical.TokenEntry, error) {
|
||||||
|
// Strip the b. from the front and namespace ID from the back
|
||||||
|
bEntry, _ := namespace.SplitIDFromString(id[2:])
|
||||||
|
|
||||||
|
eEntry, err := base64.RawURLEncoding.DecodeString(bEntry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mEntry, err := ts.batchTokenEncryptor.Decrypt(ctx, "", eEntry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pEntry := new(pb.TokenEntry)
|
||||||
|
if err := proto.Unmarshal(mEntry, pEntry); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
te, err := pb.ProtoTokenEntryToLogicalTokenEntry(pEntry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if time.Now().After(time.Unix(te.CreationTime, 0).Add(te.TTL)) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if te.Parent != "" {
|
||||||
|
pte, err := ts.Lookup(ctx, te.Parent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if pte == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
te.ID = id
|
||||||
|
return te, nil
|
||||||
|
}
|
||||||
|
|
||||||
// lookupInternal is used to find a token given its (possibly salted) ID. If
|
// lookupInternal is used to find a token given its (possibly salted) ID. If
|
||||||
// tainted is true, entries that are in some revocation state (currently,
|
// tainted is true, entries that are in some revocation state (currently,
|
||||||
// indicated by num uses < 0), the entry will be returned anyways
|
// indicated by num uses < 0), the entry will be returned anyways
|
||||||
@ -1006,6 +1130,11 @@ func (ts *TokenStore) lookupInternal(ctx context.Context, id string, salted, tai
|
|||||||
return nil, errwrap.Wrapf("failed to find namespace in context: {{err}}", err)
|
return nil, errwrap.Wrapf("failed to find namespace in context: {{err}}", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it starts with "b." it's a batch token
|
||||||
|
if len(id) > 2 && id[0:2] == "b." {
|
||||||
|
return ts.lookupBatchToken(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
var raw *logical.StorageEntry
|
var raw *logical.StorageEntry
|
||||||
lookupID := id
|
lookupID := id
|
||||||
|
|
||||||
@ -1063,6 +1192,11 @@ func (ts *TokenStore) lookupInternal(ctx context.Context, id string, salted, tai
|
|||||||
entry.NamespaceID = namespace.RootNamespaceID
|
entry.NamespaceID = namespace.RootNamespaceID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This will be the upgrade case
|
||||||
|
if entry.Type == logical.TokenTypeDefault {
|
||||||
|
entry.Type = logical.TokenTypeService
|
||||||
|
}
|
||||||
|
|
||||||
persistNeeded := false
|
persistNeeded := false
|
||||||
|
|
||||||
// Upgrade the deprecated fields
|
// Upgrade the deprecated fields
|
||||||
@ -1490,6 +1624,15 @@ func (ts *TokenStore) revokeTreeInternal(ctx context.Context, id string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Core) IsBatchTokenCreationRequest(ctx context.Context, path string) (bool, error) {
|
||||||
|
name := strings.TrimPrefix(path, "auth/token/create/")
|
||||||
|
roleEntry, err := c.tokenStore.tokenStoreRole(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return roleEntry.TokenType == logical.TokenTypeBatch, nil
|
||||||
|
}
|
||||||
|
|
||||||
// handleCreateAgainstRole handles the auth/token/create path for a role
|
// handleCreateAgainstRole handles the auth/token/create path for a role
|
||||||
func (ts *TokenStore) handleCreateAgainstRole(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
func (ts *TokenStore) handleCreateAgainstRole(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||||
name := d.Get("role_name").(string)
|
name := d.Get("role_name").(string)
|
||||||
@ -1924,6 +2067,9 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
|||||||
if parent == nil {
|
if parent == nil {
|
||||||
return logical.ErrorResponse("parent token lookup failed: no parent found"), logical.ErrInvalidRequest
|
return logical.ErrorResponse("parent token lookup failed: no parent found"), logical.ErrInvalidRequest
|
||||||
}
|
}
|
||||||
|
if parent.Type == logical.TokenTypeBatch {
|
||||||
|
return logical.ErrorResponse("batch tokens cannot create more tokens"), nil
|
||||||
|
}
|
||||||
|
|
||||||
// A token with a restricted number of uses cannot create a new token
|
// A token with a restricted number of uses cannot create a new token
|
||||||
// otherwise it could escape the restriction count.
|
// otherwise it could escape the restriction count.
|
||||||
@ -1949,6 +2095,7 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
|||||||
DisplayName string `mapstructure:"display_name"`
|
DisplayName string `mapstructure:"display_name"`
|
||||||
NumUses int `mapstructure:"num_uses"`
|
NumUses int `mapstructure:"num_uses"`
|
||||||
Period string
|
Period string
|
||||||
|
Type string `mapstructure:"type"`
|
||||||
}
|
}
|
||||||
if err := mapstructure.WeakDecode(req.Data, &data); err != nil {
|
if err := mapstructure.WeakDecode(req.Data, &data); err != nil {
|
||||||
return logical.ErrorResponse(fmt.Sprintf(
|
return logical.ErrorResponse(fmt.Sprintf(
|
||||||
@ -1982,6 +2129,56 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renewable := true
|
||||||
|
if data.Renewable != nil {
|
||||||
|
renewable = *data.Renewable
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenType := logical.TokenTypeService
|
||||||
|
tokenTypeStr := data.Type
|
||||||
|
if role != nil {
|
||||||
|
switch role.TokenType {
|
||||||
|
case logical.TokenTypeDefault, logical.TokenTypeService:
|
||||||
|
tokenTypeStr = logical.TokenTypeService.String()
|
||||||
|
case logical.TokenTypeBatch:
|
||||||
|
tokenTypeStr = logical.TokenTypeBatch.String()
|
||||||
|
default:
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf("role being used for token creation contains invalid token type %q", role.TokenType.String())), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch tokenTypeStr {
|
||||||
|
case "", "service":
|
||||||
|
case "batch":
|
||||||
|
var badReason string
|
||||||
|
switch {
|
||||||
|
case data.ExplicitMaxTTL != "":
|
||||||
|
dur, err := parseutil.ParseDurationSecond(data.ExplicitMaxTTL)
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse(`"explicit_max_ttl" value could not be parsed`), nil
|
||||||
|
}
|
||||||
|
if dur != 0 {
|
||||||
|
badReason = "explicit_max_ttl"
|
||||||
|
}
|
||||||
|
case data.NumUses != 0:
|
||||||
|
badReason = "num_uses"
|
||||||
|
case data.Period != "":
|
||||||
|
dur, err := parseutil.ParseDurationSecond(data.Period)
|
||||||
|
if err != nil {
|
||||||
|
return logical.ErrorResponse(`"period" value could not be parsed`), nil
|
||||||
|
}
|
||||||
|
if dur != 0 {
|
||||||
|
badReason = "period"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if badReason != "" {
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf("batch tokens cannot have %q set", badReason)), nil
|
||||||
|
}
|
||||||
|
tokenType = logical.TokenTypeBatch
|
||||||
|
renewable = false
|
||||||
|
default:
|
||||||
|
return logical.ErrorResponse("invalid 'token_type' value"), logical.ErrInvalidRequest
|
||||||
|
}
|
||||||
|
|
||||||
// Verify the number of uses is positive
|
// Verify the number of uses is positive
|
||||||
if data.NumUses < 0 {
|
if data.NumUses < 0 {
|
||||||
return logical.ErrorResponse("number of uses cannot be negative"),
|
return logical.ErrorResponse("number of uses cannot be negative"),
|
||||||
@ -2002,11 +2199,7 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
|||||||
NumUses: data.NumUses,
|
NumUses: data.NumUses,
|
||||||
CreationTime: time.Now().Unix(),
|
CreationTime: time.Now().Unix(),
|
||||||
NamespaceID: ns.ID,
|
NamespaceID: ns.ID,
|
||||||
}
|
Type: tokenType,
|
||||||
|
|
||||||
renewable := true
|
|
||||||
if data.Renewable != nil {
|
|
||||||
renewable = *data.Renewable
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the role is not nil, we add the role name as part of the token's
|
// If the role is not nil, we add the role name as part of the token's
|
||||||
@ -2169,10 +2362,17 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent attempts to create a root token without an actual root token as parent.
|
if strutil.StrListContains(te.Policies, "root") {
|
||||||
// This is to thwart privilege escalation by tokens having 'sudo' privileges.
|
// Prevent attempts to create a root token without an actual root token as parent.
|
||||||
if strutil.StrListContains(data.Policies, "root") && !strutil.StrListContains(parent.Policies, "root") {
|
// This is to thwart privilege escalation by tokens having 'sudo' privileges.
|
||||||
return logical.ErrorResponse("root tokens may not be created without parent token being root"), logical.ErrInvalidRequest
|
if !strutil.StrListContains(parent.Policies, "root") {
|
||||||
|
return logical.ErrorResponse("root tokens may not be created without parent token being root"), logical.ErrInvalidRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
if te.Type == logical.TokenTypeBatch {
|
||||||
|
// Batch tokens cannot be revoked so we should never have root batch tokens
|
||||||
|
return logical.ErrorResponse("batch tokens cannot be root tokens"), nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -2208,7 +2408,8 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
|||||||
|
|
||||||
// At this point, it is clear whether the token is going to be an orphan or
|
// At this point, it is clear whether the token is going to be an orphan or
|
||||||
// not. If the token is not going to be an orphan, inherit the parent's
|
// not. If the token is not going to be an orphan, inherit the parent's
|
||||||
// entity identifier into the child token.
|
// entity identifier into the child token. We must also verify that, if
|
||||||
|
// it's not an orphan, the parent isn't a batch token.
|
||||||
if te.Parent != "" {
|
if te.Parent != "" {
|
||||||
te.EntityID = parent.EntityID
|
te.EntityID = parent.EntityID
|
||||||
}
|
}
|
||||||
@ -2269,8 +2470,10 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
|||||||
te.TTL = dur
|
te.TTL = dur
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the lesser period/explicit max TTL if defined both in arguments and in role
|
// Set the lesser period/explicit max TTL if defined both in arguments and
|
||||||
if role != nil {
|
// in role. Batch tokens will error out if not set via role, but here we
|
||||||
|
// need to explicitly check
|
||||||
|
if role != nil && te.Type != logical.TokenTypeBatch {
|
||||||
if role.ExplicitMaxTTL != 0 {
|
if role.ExplicitMaxTTL != 0 {
|
||||||
switch {
|
switch {
|
||||||
case explicitMaxTTLToUse == 0:
|
case explicitMaxTTLToUse == 0:
|
||||||
@ -2345,6 +2548,7 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
|||||||
Period: periodToUse,
|
Period: periodToUse,
|
||||||
ExplicitMaxTTL: explicitMaxTTLToUse,
|
ExplicitMaxTTL: explicitMaxTTLToUse,
|
||||||
CreationPath: te.Path,
|
CreationPath: te.Path,
|
||||||
|
TokenType: te.Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range te.Policies {
|
for _, p := range te.Policies {
|
||||||
@ -2364,34 +2568,7 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
|||||||
// in a way that revokes all child tokens. Normally, using sys/revoke/leaseID will revoke
|
// in a way that revokes all child tokens. Normally, using sys/revoke/leaseID will revoke
|
||||||
// the token and all children anyways, but that is only available when there is a lease.
|
// the token and all children anyways, but that is only available when there is a lease.
|
||||||
func (ts *TokenStore) handleRevokeSelf(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
func (ts *TokenStore) handleRevokeSelf(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
te, err := ts.Lookup(ctx, req.ClientToken)
|
return ts.revokeCommon(ctx, req, data, req.ClientToken)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if te == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenNS, err := NamespaceByID(ctx, te.NamespaceID, ts.core)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if tokenNS == nil {
|
|
||||||
return nil, namespace.ErrNoNamespace
|
|
||||||
}
|
|
||||||
|
|
||||||
revokeCtx := namespace.ContextWithNamespace(ts.quitContext, tokenNS)
|
|
||||||
leaseID, err := ts.expiration.CreateOrFetchRevocationLeaseByToken(revokeCtx, te)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ts.expiration.Revoke(revokeCtx, leaseID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleRevokeTree handles the auth/token/revoke/id path for revocation of tokens
|
// handleRevokeTree handles the auth/token/revoke/id path for revocation of tokens
|
||||||
@ -2408,6 +2585,20 @@ func (ts *TokenStore) handleRevokeTree(ctx context.Context, req *logical.Request
|
|||||||
urltoken = true
|
urltoken = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if resp, err := ts.revokeCommon(ctx, req, data, id); resp != nil || err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts *TokenStore) revokeCommon(ctx context.Context, req *logical.Request, data *framework.FieldData, id string) (*logical.Response, error) {
|
||||||
te, err := ts.Lookup(ctx, id)
|
te, err := ts.Lookup(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -2416,6 +2607,10 @@ func (ts *TokenStore) handleRevokeTree(ctx context.Context, req *logical.Request
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if te.Type == logical.TokenTypeBatch {
|
||||||
|
return logical.ErrorResponse("batch tokens cannot be revoked"), nil
|
||||||
|
}
|
||||||
|
|
||||||
tokenNS, err := NamespaceByID(ctx, te.NamespaceID, ts.core)
|
tokenNS, err := NamespaceByID(ctx, te.NamespaceID, ts.core)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -2435,12 +2630,6 @@ func (ts *TokenStore) handleRevokeTree(ctx context.Context, req *logical.Request
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2477,6 +2666,10 @@ func (ts *TokenStore) handleRevokeOrphan(ctx context.Context, req *logical.Reque
|
|||||||
return logical.ErrorResponse("token to revoke not found"), logical.ErrInvalidRequest
|
return logical.ErrorResponse("token to revoke not found"), logical.ErrInvalidRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if te.Type == logical.TokenTypeBatch {
|
||||||
|
return logical.ErrorResponse("batch tokens cannot be revoked"), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Revoke and orphan
|
// Revoke and orphan
|
||||||
if err := ts.revokeOrphan(ctx, id); err != nil {
|
if err := ts.revokeOrphan(ctx, id); err != nil {
|
||||||
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
|
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
|
||||||
@ -2545,6 +2738,7 @@ func (ts *TokenStore) handleLookup(ctx context.Context, req *logical.Request, da
|
|||||||
"ttl": int64(0),
|
"ttl": int64(0),
|
||||||
"explicit_max_ttl": int64(out.ExplicitMaxTTL.Seconds()),
|
"explicit_max_ttl": int64(out.ExplicitMaxTTL.Seconds()),
|
||||||
"entity_id": out.EntityID,
|
"entity_id": out.EntityID,
|
||||||
|
"type": out.Type.String(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2645,8 +2839,14 @@ func (ts *TokenStore) handleRenew(ctx context.Context, req *logical.Request, dat
|
|||||||
return logical.ErrorResponse("token not found"), logical.ErrInvalidRequest
|
return logical.ErrorResponse("token not found"), logical.ErrInvalidRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var resp *logical.Response
|
||||||
|
|
||||||
|
if te.Type == logical.TokenTypeBatch {
|
||||||
|
return logical.ErrorResponse("batch tokens cannot be renewed"), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Renew the token and its children
|
// Renew the token and its children
|
||||||
resp, err := ts.expiration.RenewToken(ctx, req, te, increment)
|
resp, err = ts.expiration.RenewToken(ctx, req, te, increment)
|
||||||
|
|
||||||
if urltoken {
|
if urltoken {
|
||||||
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.`)
|
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.`)
|
||||||
@ -2761,6 +2961,7 @@ func (ts *TokenStore) tokenStoreRoleRead(ctx context.Context, req *logical.Reque
|
|||||||
"orphan": role.Orphan,
|
"orphan": role.Orphan,
|
||||||
"path_suffix": role.PathSuffix,
|
"path_suffix": role.PathSuffix,
|
||||||
"renewable": role.Renewable,
|
"renewable": role.Renewable,
|
||||||
|
"token_type": role.TokenType.String(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2898,6 +3099,38 @@ func (ts *TokenStore) tokenStoreRoleCreateUpdate(ctx context.Context, req *logic
|
|||||||
entry.DisallowedPolicies = strutil.RemoveDuplicates(data.Get("disallowed_policies").([]string), true)
|
entry.DisallowedPolicies = strutil.RemoveDuplicates(data.Get("disallowed_policies").([]string), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokenType := entry.TokenType
|
||||||
|
tokenTypeRaw, ok := data.GetOk("token_type")
|
||||||
|
if ok {
|
||||||
|
tokenTypeStr := tokenTypeRaw.(string)
|
||||||
|
switch tokenTypeStr {
|
||||||
|
case "", "service":
|
||||||
|
tokenType = logical.TokenTypeService
|
||||||
|
case "batch":
|
||||||
|
tokenType = logical.TokenTypeBatch
|
||||||
|
default:
|
||||||
|
return logical.ErrorResponse(fmt.Sprintf("invalid 'token_type' value %q", tokenTypeStr)), nil
|
||||||
|
}
|
||||||
|
} else if req.Operation == logical.CreateOperation {
|
||||||
|
tokenType = logical.TokenTypeService
|
||||||
|
}
|
||||||
|
entry.TokenType = tokenType
|
||||||
|
|
||||||
|
if entry.TokenType == logical.TokenTypeBatch {
|
||||||
|
if !entry.Orphan {
|
||||||
|
return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate non-orphan tokens"), nil
|
||||||
|
}
|
||||||
|
if entry.Period != 0 {
|
||||||
|
return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate periodic tokens"), nil
|
||||||
|
}
|
||||||
|
if entry.Renewable {
|
||||||
|
return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate renewable tokens"), nil
|
||||||
|
}
|
||||||
|
if entry.ExplicitMaxTTL != 0 {
|
||||||
|
return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate tokens with an explicit max TTL"), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ns, err := namespace.FromContext(ctx)
|
ns, err := namespace.FromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@ -13,6 +13,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-test/deep"
|
||||||
|
"github.com/hashicorp/errwrap"
|
||||||
hclog "github.com/hashicorp/go-hclog"
|
hclog "github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/go-uuid"
|
"github.com/hashicorp/go-uuid"
|
||||||
"github.com/hashicorp/vault/helper/locksutil"
|
"github.com/hashicorp/vault/helper/locksutil"
|
||||||
@ -286,10 +288,22 @@ func getBackendConfig(c *Core) *logical.BackendConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testMakeTokenViaBackend(t testing.TB, ts *TokenStore, root, client, ttl string, policy []string) {
|
func testMakeBatchTokenViaBackend(t testing.TB, ts *TokenStore, root, client, ttl string, policy []string) {
|
||||||
|
testMakeTokenViaBackend(t, ts, root, client, ttl, policy, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMakeServiceTokenViaBackend(t testing.TB, ts *TokenStore, root, client, ttl string, policy []string) {
|
||||||
|
testMakeTokenViaBackend(t, ts, root, client, ttl, policy, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMakeTokenViaBackend(t testing.TB, ts *TokenStore, root, client, ttl string, policy []string, batch bool) {
|
||||||
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
req.Data["id"] = client
|
if batch {
|
||||||
|
req.Data["type"] = "batch"
|
||||||
|
} else {
|
||||||
|
req.Data["id"] = client
|
||||||
|
}
|
||||||
req.Data["policies"] = policy
|
req.Data["policies"] = policy
|
||||||
req.Data["ttl"] = ttl
|
req.Data["ttl"] = ttl
|
||||||
resp := testMakeTokenViaRequest(t, ts, req)
|
resp := testMakeTokenViaRequest(t, ts, req)
|
||||||
@ -301,21 +315,26 @@ func testMakeTokenViaBackend(t testing.TB, ts *TokenStore, root, client, ttl str
|
|||||||
|
|
||||||
func testMakeTokenViaRequest(t testing.TB, ts *TokenStore, req *logical.Request) *logical.Response {
|
func testMakeTokenViaRequest(t testing.TB, ts *TokenStore, req *logical.Request) *logical.Response {
|
||||||
resp, err := ts.HandleRequest(namespace.TestContext(), req)
|
resp, err := ts.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil || (resp != nil && resp.IsError()) {
|
if err != nil {
|
||||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp == nil {
|
if resp == nil {
|
||||||
t.Fatalf("got nil token from create call")
|
t.Fatalf("got nil token from create call")
|
||||||
}
|
}
|
||||||
|
// Let the caller handle the error
|
||||||
|
if resp.IsError() {
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
te := &logical.TokenEntry{
|
te := &logical.TokenEntry{
|
||||||
Path: resp.Auth.CreationPath,
|
Path: resp.Auth.CreationPath,
|
||||||
NamespaceID: namespace.RootNamespaceID,
|
NamespaceID: namespace.RootNamespaceID,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ts.expiration.RegisterAuth(namespace.TestContext(), te, resp.Auth); err != nil {
|
if resp.Auth.TokenType != logical.TokenTypeBatch {
|
||||||
t.Fatal(err)
|
if err := ts.expiration.RegisterAuth(namespace.TestContext(), te, resp.Auth); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
te, err = ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
te, err = ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
||||||
@ -333,9 +352,15 @@ func testMakeTokenDirectly(t testing.TB, ts *TokenStore, te *logical.TokenEntry)
|
|||||||
if te.NamespaceID == "" {
|
if te.NamespaceID == "" {
|
||||||
te.NamespaceID = namespace.RootNamespaceID
|
te.NamespaceID = namespace.RootNamespaceID
|
||||||
}
|
}
|
||||||
|
if te.CreationTime == 0 {
|
||||||
|
te.CreationTime = time.Now().Unix()
|
||||||
|
}
|
||||||
if err := ts.create(namespace.RootContext(nil), te); err != nil {
|
if err := ts.create(namespace.RootContext(nil), te); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
if te.Type == logical.TokenTypeDefault {
|
||||||
|
te.Type = logical.TokenTypeService
|
||||||
|
}
|
||||||
auth := &logical.Auth{
|
auth := &logical.Auth{
|
||||||
NumUses: te.NumUses,
|
NumUses: te.NumUses,
|
||||||
DisplayName: te.DisplayName,
|
DisplayName: te.DisplayName,
|
||||||
@ -351,16 +376,37 @@ func testMakeTokenDirectly(t testing.TB, ts *TokenStore, te *logical.TokenEntry)
|
|||||||
Period: te.Period,
|
Period: te.Period,
|
||||||
ExplicitMaxTTL: te.ExplicitMaxTTL,
|
ExplicitMaxTTL: te.ExplicitMaxTTL,
|
||||||
CreationPath: te.Path,
|
CreationPath: te.Path,
|
||||||
|
TokenType: te.Type,
|
||||||
}
|
}
|
||||||
if err := ts.expiration.RegisterAuth(namespace.TestContext(), te, auth); err != nil {
|
err := ts.expiration.RegisterAuth(namespace.TestContext(), te, auth)
|
||||||
t.Fatal(err)
|
switch err {
|
||||||
|
case nil:
|
||||||
|
if te.Type == logical.TokenTypeBatch {
|
||||||
|
t.Fatal("expected error from trying to register auth with batch token")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if te.Type != logical.TokenTypeBatch {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testMakeTokenViaCore(t testing.TB, c *Core, root, client, ttl string, policy []string) {
|
func testMakeServiceTokenViaCore(t testing.TB, c *Core, root, client, ttl string, policy []string) {
|
||||||
|
testMakeTokenViaCore(t, c, root, client, ttl, policy, false, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMakeBatchTokenViaCore(t testing.TB, c *Core, root, client, ttl string, policy []string) {
|
||||||
|
testMakeTokenViaCore(t, c, root, client, ttl, policy, true, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMakeTokenViaCore(t testing.TB, c *Core, root, client, ttl string, policy []string, batch bool, outAuth *logical.Auth) {
|
||||||
req := logical.TestRequest(t, logical.UpdateOperation, "auth/token/create")
|
req := logical.TestRequest(t, logical.UpdateOperation, "auth/token/create")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
req.Data["id"] = client
|
if batch {
|
||||||
|
req.Data["type"] = "batch"
|
||||||
|
} else {
|
||||||
|
req.Data["id"] = client
|
||||||
|
}
|
||||||
req.Data["policies"] = policy
|
req.Data["policies"] = policy
|
||||||
req.Data["ttl"] = ttl
|
req.Data["ttl"] = ttl
|
||||||
|
|
||||||
@ -368,8 +414,13 @@ func testMakeTokenViaCore(t testing.TB, c *Core, root, client, ttl string, polic
|
|||||||
if err != nil || (resp != nil && resp.IsError()) {
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||||
}
|
}
|
||||||
if resp.Auth.ClientToken != client {
|
if !batch {
|
||||||
t.Fatalf("bad: %#v", *resp)
|
if resp.Auth.ClientToken != client {
|
||||||
|
t.Fatalf("bad: %#v", *resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if outAuth != nil && resp != nil && resp.Auth != nil {
|
||||||
|
*outAuth = *resp.Auth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,13 +455,27 @@ func TestTokenStore_AccessorIndex(t *testing.T) {
|
|||||||
if aEntry.TokenID != ent.ID {
|
if aEntry.TokenID != ent.ID {
|
||||||
t.Fatalf("bad: got\n%s\nexpected\n%s\n", aEntry.TokenID, ent.ID)
|
t.Fatalf("bad: got\n%s\nexpected\n%s\n", aEntry.TokenID, ent.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure a batch token doesn't get an accessor
|
||||||
|
ent.Type = logical.TokenTypeBatch
|
||||||
|
testMakeTokenDirectly(t, ts, ent)
|
||||||
|
|
||||||
|
out, err = ts.Lookup(namespace.TestContext(), 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenStore_HandleRequest_LookupAccessor(t *testing.T) {
|
func TestTokenStore_HandleRequest_LookupAccessor(t *testing.T) {
|
||||||
c, _, root := TestCoreUnsealed(t)
|
c, _, root := TestCoreUnsealed(t)
|
||||||
ts := c.tokenStore
|
ts := c.tokenStore
|
||||||
|
|
||||||
testMakeTokenViaBackend(t, ts, root, "tokenid", "", []string{"foo"})
|
testMakeServiceTokenViaBackend(t, ts, root, "tokenid", "", []string{"foo"})
|
||||||
out, err := ts.Lookup(namespace.TestContext(), "tokenid")
|
out, err := ts.Lookup(namespace.TestContext(), "tokenid")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
@ -448,7 +513,7 @@ func TestTokenStore_HandleRequest_ListAccessors(t *testing.T) {
|
|||||||
|
|
||||||
testKeys := []string{"token1", "token2", "token3", "token4"}
|
testKeys := []string{"token1", "token2", "token3", "token4"}
|
||||||
for _, key := range testKeys {
|
for _, key := range testKeys {
|
||||||
testMakeTokenViaBackend(t, ts, root, key, "", []string{"foo"})
|
testMakeServiceTokenViaBackend(t, ts, root, key, "", []string{"foo"})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revoke root to make the number of accessors match
|
// Revoke root to make the number of accessors match
|
||||||
@ -534,7 +599,7 @@ func TestTokenStore_HandleRequest_RevokeAccessor(t *testing.T) {
|
|||||||
rootToken, err := ts.rootToken(namespace.TestContext())
|
rootToken, err := ts.rootToken(namespace.TestContext())
|
||||||
root := rootToken.ID
|
root := rootToken.ID
|
||||||
|
|
||||||
testMakeTokenViaBackend(t, ts, root, "tokenid", "", []string{"foo"})
|
testMakeServiceTokenViaBackend(t, ts, root, "tokenid", "", []string{"foo"})
|
||||||
|
|
||||||
auth := &logical.Auth{
|
auth := &logical.Auth{
|
||||||
ClientToken: "tokenid",
|
ClientToken: "tokenid",
|
||||||
@ -587,7 +652,7 @@ func TestTokenStore_HandleRequest_RevokeAccessor(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now test without registering the token through the expiration manager
|
// Now test without registering the token through the expiration manager
|
||||||
testMakeTokenViaBackend(t, ts, root, "tokenid", "", []string{"foo"})
|
testMakeServiceTokenViaBackend(t, ts, root, "tokenid", "", []string{"foo"})
|
||||||
out, err = ts.Lookup(namespace.TestContext(), "tokenid")
|
out, err = ts.Lookup(namespace.TestContext(), "tokenid")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
@ -639,6 +704,27 @@ func TestTokenStore_RootToken(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTokenStore_NoRootBatch(t *testing.T) {
|
||||||
|
c, _, root := TestCoreUnsealed(t)
|
||||||
|
|
||||||
|
req := logical.TestRequest(t, logical.UpdateOperation, "auth/token/create")
|
||||||
|
req.ClientToken = root
|
||||||
|
req.Data["type"] = "batch"
|
||||||
|
req.Data["policies"] = "root"
|
||||||
|
req.Data["ttl"] = "5m"
|
||||||
|
|
||||||
|
resp, err := c.HandleRequest(namespace.TestContext(), req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resp == nil {
|
||||||
|
t.Fatal("expected response")
|
||||||
|
}
|
||||||
|
if !resp.IsError() {
|
||||||
|
t.Fatalf("expected error, got %#v", *resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTokenStore_CreateLookup(t *testing.T) {
|
func TestTokenStore_CreateLookup(t *testing.T) {
|
||||||
c, _, _ := TestCoreUnsealed(t)
|
c, _, _ := TestCoreUnsealed(t)
|
||||||
ts := c.tokenStore
|
ts := c.tokenStore
|
||||||
@ -930,6 +1016,7 @@ func TestTokenStore_Revoke_Leases(t *testing.T) {
|
|||||||
Path: "noop/foo",
|
Path: "noop/foo",
|
||||||
ClientToken: ent.ID,
|
ClientToken: ent.ID,
|
||||||
}
|
}
|
||||||
|
req.SetTokenEntry(ent)
|
||||||
resp := &logical.Response{
|
resp := &logical.Response{
|
||||||
Secret: &logical.Secret{
|
Secret: &logical.Secret{
|
||||||
LeaseOptions: logical.LeaseOptions{
|
LeaseOptions: logical.LeaseOptions{
|
||||||
@ -1226,6 +1313,19 @@ func TestTokenStore_HandleRequest_NonAssignable(t *testing.T) {
|
|||||||
if !resp.IsError() {
|
if !resp.IsError() {
|
||||||
t.Fatalf("expected error; response is %#v", *resp)
|
t.Fatalf("expected error; response is %#v", *resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Batch tokens too
|
||||||
|
req.Data["type"] = "batch"
|
||||||
|
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resp == nil {
|
||||||
|
t.Fatal("got a nil response")
|
||||||
|
}
|
||||||
|
if !resp.IsError() {
|
||||||
|
t.Fatalf("expected error; response is %#v", *resp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenStore_HandleRequest_CreateToken_DisplayName(t *testing.T) {
|
func TestTokenStore_HandleRequest_CreateToken_DisplayName(t *testing.T) {
|
||||||
@ -1250,6 +1350,7 @@ func TestTokenStore_HandleRequest_CreateToken_DisplayName(t *testing.T) {
|
|||||||
Path: "auth/token/create",
|
Path: "auth/token/create",
|
||||||
DisplayName: "token-foo-bar-baz",
|
DisplayName: "token-foo-bar-baz",
|
||||||
TTL: 0,
|
TTL: 0,
|
||||||
|
Type: logical.TokenTypeService,
|
||||||
}
|
}
|
||||||
out, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
out, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1269,7 +1370,15 @@ func TestTokenStore_HandleRequest_CreateToken_NumUses(t *testing.T) {
|
|||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
req.Data["num_uses"] = "1"
|
req.Data["num_uses"] = "1"
|
||||||
|
|
||||||
|
// Make sure batch tokens can't do limited use counts
|
||||||
|
req.Data["type"] = "batch"
|
||||||
resp, err := ts.HandleRequest(namespace.TestContext(), req)
|
resp, err := ts.HandleRequest(namespace.TestContext(), req)
|
||||||
|
if resp == nil || !resp.IsError() {
|
||||||
|
t.Fatalf("expected error: resp: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(req.Data, "type")
|
||||||
|
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil || (resp != nil && resp.IsError()) {
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||||
}
|
}
|
||||||
@ -1284,6 +1393,7 @@ func TestTokenStore_HandleRequest_CreateToken_NumUses(t *testing.T) {
|
|||||||
DisplayName: "token",
|
DisplayName: "token",
|
||||||
NumUses: 1,
|
NumUses: 1,
|
||||||
TTL: 0,
|
TTL: 0,
|
||||||
|
Type: logical.TokenTypeService,
|
||||||
}
|
}
|
||||||
out, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
out, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1337,7 +1447,15 @@ func TestTokenStore_HandleRequest_CreateToken_NoPolicy(t *testing.T) {
|
|||||||
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
|
|
||||||
|
// Make sure batch tokens won't automatically assign root
|
||||||
|
req.Data["type"] = "batch"
|
||||||
resp, err := ts.HandleRequest(namespace.TestContext(), req)
|
resp, err := ts.HandleRequest(namespace.TestContext(), req)
|
||||||
|
if resp == nil || !resp.IsError() {
|
||||||
|
t.Fatalf("expected error: resp: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(req.Data, "type")
|
||||||
|
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil || (resp != nil && resp.IsError()) {
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||||
}
|
}
|
||||||
@ -1351,6 +1469,7 @@ func TestTokenStore_HandleRequest_CreateToken_NoPolicy(t *testing.T) {
|
|||||||
Path: "auth/token/create",
|
Path: "auth/token/create",
|
||||||
DisplayName: "token",
|
DisplayName: "token",
|
||||||
TTL: 0,
|
TTL: 0,
|
||||||
|
Type: logical.TokenTypeService,
|
||||||
}
|
}
|
||||||
out, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
out, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1408,16 +1527,26 @@ func TestTokenStore_HandleRequest_CreateToken_RootID(t *testing.T) {
|
|||||||
if err != nil || (resp != nil && resp.IsError()) {
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.Auth.ClientToken != "foobar" {
|
if resp.Auth.ClientToken != "foobar" {
|
||||||
t.Fatalf("bad: %#v", resp)
|
t.Fatalf("bad: %#v", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retry with batch; batch should not actually accept a custom ID
|
||||||
|
req.Data["type"] = "batch"
|
||||||
|
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||||
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
|
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||||
|
}
|
||||||
|
out, _ := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
||||||
|
if out.ID == "foobar" {
|
||||||
|
t.Fatalf("bad: %#v", out)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenStore_HandleRequest_CreateToken_NonRootID(t *testing.T) {
|
func TestTokenStore_HandleRequest_CreateToken_NonRootID(t *testing.T) {
|
||||||
c, _, root := TestCoreUnsealed(t)
|
c, _, root := TestCoreUnsealed(t)
|
||||||
ts := c.tokenStore
|
ts := c.tokenStore
|
||||||
testMakeTokenViaBackend(t, ts, root, "client", "", []string{"foo"})
|
testMakeServiceTokenViaBackend(t, ts, root, "client", "", []string{"foo"})
|
||||||
|
|
||||||
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
||||||
req.ClientToken = "client"
|
req.ClientToken = "client"
|
||||||
@ -1431,12 +1560,22 @@ func TestTokenStore_HandleRequest_CreateToken_NonRootID(t *testing.T) {
|
|||||||
if resp.Data["error"] != "root or sudo privileges required to specify token id" {
|
if resp.Data["error"] != "root or sudo privileges required to specify token id" {
|
||||||
t.Fatalf("bad: %#v", resp)
|
t.Fatalf("bad: %#v", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retry with batch
|
||||||
|
req.Data["type"] = "batch"
|
||||||
|
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||||
|
if err != logical.ErrInvalidRequest {
|
||||||
|
t.Fatalf("err: %v resp: %#v", err, resp)
|
||||||
|
}
|
||||||
|
if resp.Data["error"] != "root or sudo privileges required to specify token id" {
|
||||||
|
t.Fatalf("bad: %#v", resp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenStore_HandleRequest_CreateToken_NonRoot_Subset(t *testing.T) {
|
func TestTokenStore_HandleRequest_CreateToken_NonRoot_Subset(t *testing.T) {
|
||||||
c, _, root := TestCoreUnsealed(t)
|
c, _, root := TestCoreUnsealed(t)
|
||||||
ts := c.tokenStore
|
ts := c.tokenStore
|
||||||
testMakeTokenViaBackend(t, ts, root, "client", "", []string{"foo", "bar"})
|
testMakeServiceTokenViaBackend(t, ts, root, "client", "", []string{"foo", "bar"})
|
||||||
|
|
||||||
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
||||||
req.ClientToken = "client"
|
req.ClientToken = "client"
|
||||||
@ -1449,12 +1588,28 @@ func TestTokenStore_HandleRequest_CreateToken_NonRoot_Subset(t *testing.T) {
|
|||||||
if resp.Auth.ClientToken == "" {
|
if resp.Auth.ClientToken == "" {
|
||||||
t.Fatalf("bad: %#v", resp)
|
t.Fatalf("bad: %#v", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ent := &logical.TokenEntry{
|
||||||
|
NamespaceID: namespace.RootNamespaceID,
|
||||||
|
Path: "test",
|
||||||
|
Policies: []string{"foo", "bar"},
|
||||||
|
TTL: time.Hour,
|
||||||
|
}
|
||||||
|
testMakeTokenDirectly(t, ts, ent)
|
||||||
|
req.ClientToken = ent.ID
|
||||||
|
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||||
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
|
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||||
|
}
|
||||||
|
if resp.Auth.ClientToken == "" {
|
||||||
|
t.Fatalf("bad: %#v", resp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenStore_HandleRequest_CreateToken_NonRoot_InvalidSubset(t *testing.T) {
|
func TestTokenStore_HandleRequest_CreateToken_NonRoot_InvalidSubset(t *testing.T) {
|
||||||
c, _, root := TestCoreUnsealed(t)
|
c, _, root := TestCoreUnsealed(t)
|
||||||
ts := c.tokenStore
|
ts := c.tokenStore
|
||||||
testMakeTokenViaBackend(t, ts, root, "client", "", []string{"foo", "bar"})
|
testMakeServiceTokenViaBackend(t, ts, root, "client", "", []string{"foo", "bar"})
|
||||||
|
|
||||||
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
||||||
req.ClientToken = "client"
|
req.ClientToken = "client"
|
||||||
@ -1480,7 +1635,7 @@ func TestTokenStore_HandleRequest_CreateToken_NonRoot_RootChild(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
testMakeTokenViaBackend(t, ts, root, "sudoClient", "", []string{"test1"})
|
testMakeServiceTokenViaBackend(t, ts, root, "sudoClient", "", []string{"test1"})
|
||||||
|
|
||||||
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
||||||
req.ClientToken = "sudoClient"
|
req.ClientToken = "sudoClient"
|
||||||
@ -1555,7 +1710,7 @@ func TestTokenStore_HandleRequest_CreateToken_Root_RootChild(t *testing.T) {
|
|||||||
func TestTokenStore_HandleRequest_CreateToken_NonRoot_NoParent(t *testing.T) {
|
func TestTokenStore_HandleRequest_CreateToken_NonRoot_NoParent(t *testing.T) {
|
||||||
c, _, root := TestCoreUnsealed(t)
|
c, _, root := TestCoreUnsealed(t)
|
||||||
ts := c.tokenStore
|
ts := c.tokenStore
|
||||||
testMakeTokenViaBackend(t, ts, root, "client", "", []string{"foo"})
|
testMakeServiceTokenViaBackend(t, ts, root, "client", "", []string{"foo"})
|
||||||
|
|
||||||
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
req := logical.TestRequest(t, logical.UpdateOperation, "create")
|
||||||
req.ClientToken = "client"
|
req.ClientToken = "client"
|
||||||
@ -1633,6 +1788,18 @@ func TestTokenStore_HandleRequest_CreateToken_Metadata(t *testing.T) {
|
|||||||
if !reflect.DeepEqual(out.Meta, meta) {
|
if !reflect.DeepEqual(out.Meta, meta) {
|
||||||
t.Fatalf("bad: expected:%#v\nactual:%#v", meta, out.Meta)
|
t.Fatalf("bad: expected:%#v\nactual:%#v", meta, out.Meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test with batch tokens
|
||||||
|
req.Data["type"] = "batch"
|
||||||
|
resp = testMakeTokenViaRequest(t, ts, req)
|
||||||
|
if resp.Auth.ClientToken == "" {
|
||||||
|
t.Fatalf("bad: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, _ = ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
||||||
|
if !reflect.DeepEqual(out.Meta, meta) {
|
||||||
|
t.Fatalf("bad: expected:%#v\nactual:%#v", meta, out.Meta)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenStore_HandleRequest_CreateToken_Lease(t *testing.T) {
|
func TestTokenStore_HandleRequest_CreateToken_Lease(t *testing.T) {
|
||||||
@ -1675,6 +1842,19 @@ func TestTokenStore_HandleRequest_CreateToken_TTL(t *testing.T) {
|
|||||||
if !resp.Auth.Renewable {
|
if !resp.Auth.Renewable {
|
||||||
t.Fatalf("bad: %#v", resp)
|
t.Fatalf("bad: %#v", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test batch tokens
|
||||||
|
req.Data["type"] = "batch"
|
||||||
|
resp = testMakeTokenViaRequest(t, ts, req)
|
||||||
|
if resp.Auth.ClientToken == "" {
|
||||||
|
t.Fatalf("bad: %#v", resp)
|
||||||
|
}
|
||||||
|
if resp.Auth.TTL != time.Hour {
|
||||||
|
t.Fatalf("bad: %#v", resp)
|
||||||
|
}
|
||||||
|
if resp.Auth.Renewable {
|
||||||
|
t.Fatalf("bad: %#v", resp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenStore_HandleRequest_Revoke(t *testing.T) {
|
func TestTokenStore_HandleRequest_Revoke(t *testing.T) {
|
||||||
@ -1684,7 +1864,7 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) {
|
|||||||
rootToken, err := ts.rootToken(namespace.TestContext())
|
rootToken, err := ts.rootToken(namespace.TestContext())
|
||||||
root := rootToken.ID
|
root := rootToken.ID
|
||||||
|
|
||||||
testMakeTokenViaBackend(t, ts, root, "child", "", []string{"root", "foo"})
|
testMakeServiceTokenViaBackend(t, ts, root, "child", "", []string{"root", "foo"})
|
||||||
|
|
||||||
te, err := ts.Lookup(namespace.TestContext(), "child")
|
te, err := ts.Lookup(namespace.TestContext(), "child")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1706,7 +1886,7 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) {
|
|||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
testMakeTokenViaBackend(t, ts, "child", "sub-child", "", []string{"foo"})
|
testMakeServiceTokenViaBackend(t, ts, "child", "sub-child", "", []string{"foo"})
|
||||||
|
|
||||||
te, err = ts.Lookup(namespace.TestContext(), "sub-child")
|
te, err = ts.Lookup(namespace.TestContext(), "sub-child")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1760,8 +1940,8 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now test without registering the tokens through the expiration manager
|
// Now test without registering the tokens through the expiration manager
|
||||||
testMakeTokenViaBackend(t, ts, root, "child", "", []string{"root", "foo"})
|
testMakeServiceTokenViaBackend(t, ts, root, "child", "", []string{"root", "foo"})
|
||||||
testMakeTokenViaBackend(t, ts, "child", "sub-child", "", []string{"foo"})
|
testMakeServiceTokenViaBackend(t, ts, "child", "sub-child", "", []string{"foo"})
|
||||||
|
|
||||||
req = logical.TestRequest(t, logical.UpdateOperation, "revoke")
|
req = logical.TestRequest(t, logical.UpdateOperation, "revoke")
|
||||||
req.Data = map[string]interface{}{
|
req.Data = map[string]interface{}{
|
||||||
@ -1798,8 +1978,8 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) {
|
|||||||
func TestTokenStore_HandleRequest_RevokeOrphan(t *testing.T) {
|
func TestTokenStore_HandleRequest_RevokeOrphan(t *testing.T) {
|
||||||
c, _, root := TestCoreUnsealed(t)
|
c, _, root := TestCoreUnsealed(t)
|
||||||
ts := c.tokenStore
|
ts := c.tokenStore
|
||||||
testMakeTokenViaBackend(t, ts, root, "child", "", []string{"root", "foo"})
|
testMakeServiceTokenViaBackend(t, ts, root, "child", "", []string{"root", "foo"})
|
||||||
testMakeTokenViaBackend(t, ts, "child", "sub-child", "", []string{"foo"})
|
testMakeServiceTokenViaBackend(t, ts, "child", "sub-child", "", []string{"foo"})
|
||||||
|
|
||||||
req := logical.TestRequest(t, logical.UpdateOperation, "revoke-orphan")
|
req := logical.TestRequest(t, logical.UpdateOperation, "revoke-orphan")
|
||||||
req.Data = map[string]interface{}{
|
req.Data = map[string]interface{}{
|
||||||
@ -1850,7 +2030,7 @@ func TestTokenStore_HandleRequest_RevokeOrphan(t *testing.T) {
|
|||||||
func TestTokenStore_HandleRequest_RevokeOrphan_NonRoot(t *testing.T) {
|
func TestTokenStore_HandleRequest_RevokeOrphan_NonRoot(t *testing.T) {
|
||||||
c, _, root := TestCoreUnsealed(t)
|
c, _, root := TestCoreUnsealed(t)
|
||||||
ts := c.tokenStore
|
ts := c.tokenStore
|
||||||
testMakeTokenViaBackend(t, ts, root, "child", "", []string{"foo"})
|
testMakeServiceTokenViaBackend(t, ts, root, "child", "", []string{"foo"})
|
||||||
|
|
||||||
out, err := ts.Lookup(namespace.TestContext(), "child")
|
out, err := ts.Lookup(namespace.TestContext(), "child")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1883,6 +2063,11 @@ func TestTokenStore_HandleRequest_RevokeOrphan_NonRoot(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
||||||
|
testTokenStore_HandleRequest_Lookup(t, false)
|
||||||
|
testTokenStore_HandleRequest_Lookup(t, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTokenStore_HandleRequest_Lookup(t *testing.T, batch bool) {
|
||||||
c, _, root := TestCoreUnsealed(t)
|
c, _, root := TestCoreUnsealed(t)
|
||||||
ts := c.tokenStore
|
ts := c.tokenStore
|
||||||
req := logical.TestRequest(t, logical.UpdateOperation, "lookup")
|
req := logical.TestRequest(t, logical.UpdateOperation, "lookup")
|
||||||
@ -1911,6 +2096,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
|||||||
"explicit_max_ttl": int64(0),
|
"explicit_max_ttl": int64(0),
|
||||||
"expire_time": nil,
|
"expire_time": nil,
|
||||||
"entity_id": "",
|
"entity_id": "",
|
||||||
|
"type": "service",
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.Data["creation_time"].(int64) == 0 {
|
if resp.Data["creation_time"].(int64) == 0 {
|
||||||
@ -1922,63 +2108,20 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
|||||||
t.Fatalf("bad: expected:%#v\nactual:%#v", exp, resp.Data)
|
t.Fatalf("bad: expected:%#v\nactual:%#v", exp, resp.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
testMakeTokenViaCore(t, c, root, "client", "3600s", []string{"foo"})
|
outAuth := new(logical.Auth)
|
||||||
|
testMakeTokenViaCore(t, c, root, "client", "3600s", []string{"foo"}, batch, outAuth)
|
||||||
|
|
||||||
// Test via GET
|
tokenType := "service"
|
||||||
req = logical.TestRequest(t, logical.UpdateOperation, "lookup")
|
expID := "client"
|
||||||
req.Data = map[string]interface{}{
|
if batch {
|
||||||
"token": "client",
|
tokenType = "batch"
|
||||||
}
|
expID = outAuth.ClientToken
|
||||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
|
||||||
if err != nil || (resp != nil && resp.IsError()) {
|
|
||||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
|
||||||
}
|
|
||||||
if resp == nil {
|
|
||||||
t.Fatalf("bad: %#v", resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
exp = map[string]interface{}{
|
|
||||||
"id": "client",
|
|
||||||
"accessor": resp.Data["accessor"],
|
|
||||||
"policies": []string{"default", "foo"},
|
|
||||||
"path": "auth/token/create",
|
|
||||||
"meta": map[string]string(nil),
|
|
||||||
"display_name": "token",
|
|
||||||
"orphan": false,
|
|
||||||
"num_uses": 0,
|
|
||||||
"creation_ttl": int64(3600),
|
|
||||||
"ttl": int64(3600),
|
|
||||||
"explicit_max_ttl": int64(0),
|
|
||||||
"renewable": true,
|
|
||||||
"entity_id": "",
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.Data["creation_time"].(int64) == 0 {
|
|
||||||
t.Fatalf("creation time was zero")
|
|
||||||
}
|
|
||||||
delete(resp.Data, "creation_time")
|
|
||||||
if resp.Data["issue_time"].(time.Time).IsZero() {
|
|
||||||
t.Fatal("issue time is default time")
|
|
||||||
}
|
|
||||||
delete(resp.Data, "issue_time")
|
|
||||||
if resp.Data["expire_time"].(time.Time).IsZero() {
|
|
||||||
t.Fatal("expire time is default time")
|
|
||||||
}
|
|
||||||
delete(resp.Data, "expire_time")
|
|
||||||
|
|
||||||
// Depending on timing of the test this may have ticked down, so accept 3599
|
|
||||||
if resp.Data["ttl"].(int64) == 3599 {
|
|
||||||
resp.Data["ttl"] = int64(3600)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(resp.Data, exp) {
|
|
||||||
t.Fatalf("bad: expected:%#v\nactual:%#v", exp, resp.Data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test via POST
|
// Test via POST
|
||||||
req = logical.TestRequest(t, logical.UpdateOperation, "lookup")
|
req = logical.TestRequest(t, logical.UpdateOperation, "lookup")
|
||||||
req.Data = map[string]interface{}{
|
req.Data = map[string]interface{}{
|
||||||
"token": "client",
|
"token": expID,
|
||||||
}
|
}
|
||||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil || (resp != nil && resp.IsError()) {
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
@ -1989,7 +2132,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
exp = map[string]interface{}{
|
exp = map[string]interface{}{
|
||||||
"id": "client",
|
"id": expID,
|
||||||
"accessor": resp.Data["accessor"],
|
"accessor": resp.Data["accessor"],
|
||||||
"policies": []string{"default", "foo"},
|
"policies": []string{"default", "foo"},
|
||||||
"path": "auth/token/create",
|
"path": "auth/token/create",
|
||||||
@ -2000,8 +2143,9 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
|||||||
"creation_ttl": int64(3600),
|
"creation_ttl": int64(3600),
|
||||||
"ttl": int64(3600),
|
"ttl": int64(3600),
|
||||||
"explicit_max_ttl": int64(0),
|
"explicit_max_ttl": int64(0),
|
||||||
"renewable": true,
|
"renewable": !batch,
|
||||||
"entity_id": "",
|
"entity_id": "",
|
||||||
|
"type": tokenType,
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.Data["creation_time"].(int64) == 0 {
|
if resp.Data["creation_time"].(int64) == 0 {
|
||||||
@ -2022,16 +2166,80 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
|||||||
resp.Data["ttl"] = int64(3600)
|
resp.Data["ttl"] = int64(3600)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(resp.Data, exp) {
|
if diff := deep.Equal(resp.Data, exp); diff != nil {
|
||||||
t.Fatalf("bad: expected:%#v\nactual:%#v", exp, resp.Data)
|
t.Fatal(diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test via POST
|
||||||
|
req = logical.TestRequest(t, logical.UpdateOperation, "lookup")
|
||||||
|
req.Data = map[string]interface{}{
|
||||||
|
"token": expID,
|
||||||
|
}
|
||||||
|
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||||
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
|
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||||
|
}
|
||||||
|
if resp == nil {
|
||||||
|
t.Fatalf("bad: %#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
exp = map[string]interface{}{
|
||||||
|
"id": expID,
|
||||||
|
"accessor": resp.Data["accessor"],
|
||||||
|
"policies": []string{"default", "foo"},
|
||||||
|
"path": "auth/token/create",
|
||||||
|
"meta": map[string]string(nil),
|
||||||
|
"display_name": "token",
|
||||||
|
"orphan": false,
|
||||||
|
"num_uses": 0,
|
||||||
|
"creation_ttl": int64(3600),
|
||||||
|
"ttl": int64(3600),
|
||||||
|
"explicit_max_ttl": int64(0),
|
||||||
|
"renewable": !batch,
|
||||||
|
"entity_id": "",
|
||||||
|
"type": tokenType,
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Data["creation_time"].(int64) == 0 {
|
||||||
|
t.Fatalf("creation time was zero")
|
||||||
|
}
|
||||||
|
delete(resp.Data, "creation_time")
|
||||||
|
if resp.Data["issue_time"].(time.Time).IsZero() {
|
||||||
|
t.Fatal("issue time is default time")
|
||||||
|
}
|
||||||
|
delete(resp.Data, "issue_time")
|
||||||
|
if resp.Data["expire_time"].(time.Time).IsZero() {
|
||||||
|
t.Fatal("expire time is default time")
|
||||||
|
}
|
||||||
|
delete(resp.Data, "expire_time")
|
||||||
|
|
||||||
|
// Depending on timing of the test this may have ticked down, so accept 3599
|
||||||
|
if resp.Data["ttl"].(int64) == 3599 {
|
||||||
|
resp.Data["ttl"] = int64(3600)
|
||||||
|
}
|
||||||
|
|
||||||
|
if diff := deep.Equal(resp.Data, exp); diff != nil {
|
||||||
|
t.Fatal(diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test last_renewal_time functionality
|
// Test last_renewal_time functionality
|
||||||
req = logical.TestRequest(t, logical.UpdateOperation, "renew")
|
req = logical.TestRequest(t, logical.UpdateOperation, "renew")
|
||||||
req.Data = map[string]interface{}{
|
req.Data = map[string]interface{}{
|
||||||
"token": "client",
|
"token": expID,
|
||||||
}
|
}
|
||||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if resp == nil {
|
||||||
|
t.Fatalf("bad: %#v", resp)
|
||||||
|
}
|
||||||
|
if batch && !resp.IsError() || !batch && resp.IsError() {
|
||||||
|
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Path = "lookup"
|
||||||
|
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil || (resp != nil && resp.IsError()) {
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||||
}
|
}
|
||||||
@ -2039,27 +2247,19 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
|||||||
t.Fatalf("bad: %#v", resp)
|
t.Fatalf("bad: %#v", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
req = logical.TestRequest(t, logical.UpdateOperation, "lookup")
|
if !batch {
|
||||||
req.Data = map[string]interface{}{
|
if resp.Data["last_renewal_time"].(int64) == 0 {
|
||||||
"token": "client",
|
t.Fatalf("last_renewal_time was zero")
|
||||||
}
|
}
|
||||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
} else if _, ok := resp.Data["last_renewal_time"]; ok {
|
||||||
if err != nil || (resp != nil && resp.IsError()) {
|
t.Fatal("expected zero last renewal time")
|
||||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
|
||||||
}
|
|
||||||
if resp == nil {
|
|
||||||
t.Fatalf("bad: %#v", resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.Data["last_renewal_time"].(int64) == 0 {
|
|
||||||
t.Fatalf("last_renewal_time was zero")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTokenStore_HandleRequest_LookupSelf(t *testing.T) {
|
func TestTokenStore_HandleRequest_LookupSelf(t *testing.T) {
|
||||||
c, _, root := TestCoreUnsealed(t)
|
c, _, root := TestCoreUnsealed(t)
|
||||||
ts := c.tokenStore
|
ts := c.tokenStore
|
||||||
testMakeTokenViaCore(t, c, root, "client", "3600s", []string{"foo"})
|
testMakeServiceTokenViaCore(t, c, root, "client", "3600s", []string{"foo"})
|
||||||
|
|
||||||
req := logical.TestRequest(t, logical.ReadOperation, "lookup-self")
|
req := logical.TestRequest(t, logical.ReadOperation, "lookup-self")
|
||||||
req.ClientToken = "client"
|
req.ClientToken = "client"
|
||||||
@ -2085,6 +2285,7 @@ func TestTokenStore_HandleRequest_LookupSelf(t *testing.T) {
|
|||||||
"ttl": int64(3600),
|
"ttl": int64(3600),
|
||||||
"explicit_max_ttl": int64(0),
|
"explicit_max_ttl": int64(0),
|
||||||
"entity_id": "",
|
"entity_id": "",
|
||||||
|
"type": "service",
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.Data["creation_time"].(int64) == 0 {
|
if resp.Data["creation_time"].(int64) == 0 {
|
||||||
@ -2257,6 +2458,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
|
|||||||
"path_suffix": "happenin",
|
"path_suffix": "happenin",
|
||||||
"explicit_max_ttl": int64(0),
|
"explicit_max_ttl": int64(0),
|
||||||
"renewable": true,
|
"renewable": true,
|
||||||
|
"token_type": "service",
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(expected, resp.Data) {
|
if !reflect.DeepEqual(expected, resp.Data) {
|
||||||
@ -2302,6 +2504,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
|
|||||||
"path_suffix": "happenin",
|
"path_suffix": "happenin",
|
||||||
"explicit_max_ttl": int64(0),
|
"explicit_max_ttl": int64(0),
|
||||||
"renewable": false,
|
"renewable": false,
|
||||||
|
"token_type": "service",
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(expected, resp.Data) {
|
if !reflect.DeepEqual(expected, resp.Data) {
|
||||||
@ -2339,6 +2542,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
|
|||||||
"path_suffix": "happenin",
|
"path_suffix": "happenin",
|
||||||
"period": int64(0),
|
"period": int64(0),
|
||||||
"renewable": false,
|
"renewable": false,
|
||||||
|
"token_type": "service",
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(expected, resp.Data) {
|
if !reflect.DeepEqual(expected, resp.Data) {
|
||||||
@ -2675,7 +2879,7 @@ func TestTokenStore_RolePathSuffix(t *testing.T) {
|
|||||||
c, _, root := TestCoreUnsealed(t)
|
c, _, root := TestCoreUnsealed(t)
|
||||||
ts := c.tokenStore
|
ts := c.tokenStore
|
||||||
|
|
||||||
req := logical.TestRequest(t, logical.UpdateOperation, "roles/test")
|
req := logical.TestRequest(t, logical.CreateOperation, "roles/test")
|
||||||
req.ClientToken = root
|
req.ClientToken = root
|
||||||
req.Data = map[string]interface{}{
|
req.Data = map[string]interface{}{
|
||||||
"path_suffix": "happenin",
|
"path_suffix": "happenin",
|
||||||
@ -2690,6 +2894,7 @@ func TestTokenStore_RolePathSuffix(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
req.Path = "create/test"
|
req.Path = "create/test"
|
||||||
|
req.Operation = logical.UpdateOperation
|
||||||
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||||
if err != nil || (resp != nil && resp.IsError()) {
|
if err != nil || (resp != nil && resp.IsError()) {
|
||||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||||
@ -4138,6 +4343,8 @@ func TestTokenStore_TidyLeaseRevocation(t *testing.T) {
|
|||||||
Path: "prod/aws/foo",
|
Path: "prod/aws/foo",
|
||||||
ClientToken: tut,
|
ClientToken: tut,
|
||||||
}
|
}
|
||||||
|
req.SetTokenEntry(testTokenEntry)
|
||||||
|
|
||||||
resp = &logical.Response{
|
resp = &logical.Response{
|
||||||
Secret: &logical.Secret{
|
Secret: &logical.Secret{
|
||||||
LeaseOptions: logical.LeaseOptions{
|
LeaseOptions: logical.LeaseOptions{
|
||||||
@ -4224,3 +4431,101 @@ func TestTokenStore_TidyLeaseRevocation(t *testing.T) {
|
|||||||
t.Fatal("found leases")
|
t.Fatal("found leases")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTokenStore_Batch_CannotCreateChildren(t *testing.T) {
|
||||||
|
var resp *logical.Response
|
||||||
|
|
||||||
|
core, _, root := TestCoreUnsealed(t)
|
||||||
|
ts := core.tokenStore
|
||||||
|
|
||||||
|
req := &logical.Request{
|
||||||
|
Path: "create",
|
||||||
|
ClientToken: root,
|
||||||
|
Operation: logical.UpdateOperation,
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"policies": []string{"policy1"},
|
||||||
|
"type": "batch",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resp = testMakeTokenViaRequest(t, ts, req)
|
||||||
|
if !reflect.DeepEqual(resp.Auth.Policies, []string{"default", "policy1"}) {
|
||||||
|
t.Fatalf("bad: policies: expected: [policy, default]; actual: %s", resp.Auth.Policies)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.ClientToken = resp.Auth.ClientToken
|
||||||
|
resp = testMakeTokenViaRequest(t, ts, req)
|
||||||
|
if !resp.IsError() {
|
||||||
|
t.Fatalf("bad: expected error, got %#v", *resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTokenStore_Batch_CannotRevoke(t *testing.T) {
|
||||||
|
var resp *logical.Response
|
||||||
|
var err error
|
||||||
|
|
||||||
|
core, _, root := TestCoreUnsealed(t)
|
||||||
|
ts := core.tokenStore
|
||||||
|
|
||||||
|
req := &logical.Request{
|
||||||
|
Path: "create",
|
||||||
|
ClientToken: root,
|
||||||
|
Operation: logical.UpdateOperation,
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"policies": []string{"policy1"},
|
||||||
|
"type": "batch",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resp = testMakeTokenViaRequest(t, ts, req)
|
||||||
|
if !reflect.DeepEqual(resp.Auth.Policies, []string{"default", "policy1"}) {
|
||||||
|
t.Fatalf("bad: policies: expected: [policy, default]; actual: %s", resp.Auth.Policies)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Path = "revoke"
|
||||||
|
req.Data["token"] = resp.Auth.ClientToken
|
||||||
|
resp, err = ts.HandleRequest(namespace.TestContext(), req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !resp.IsError() {
|
||||||
|
t.Fatalf("bad: expected error, got %#v", *resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTokenStore_Batch_NoCubbyhole(t *testing.T) {
|
||||||
|
var resp *logical.Response
|
||||||
|
var err error
|
||||||
|
|
||||||
|
core, _, root := TestCoreUnsealed(t)
|
||||||
|
ts := core.tokenStore
|
||||||
|
|
||||||
|
req := &logical.Request{
|
||||||
|
Path: "create",
|
||||||
|
ClientToken: root,
|
||||||
|
Operation: logical.UpdateOperation,
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"policies": []string{"policy1"},
|
||||||
|
"type": "batch",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resp = testMakeTokenViaRequest(t, ts, req)
|
||||||
|
if !reflect.DeepEqual(resp.Auth.Policies, []string{"default", "policy1"}) {
|
||||||
|
t.Fatalf("bad: policies: expected: [policy, default]; actual: %s", resp.Auth.Policies)
|
||||||
|
}
|
||||||
|
|
||||||
|
te, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Path = "cubbyhole/foo"
|
||||||
|
req.Operation = logical.CreateOperation
|
||||||
|
req.ClientToken = te.ID
|
||||||
|
req.SetTokenEntry(te)
|
||||||
|
resp, err = core.HandleRequest(namespace.TestContext(), req)
|
||||||
|
if err != nil && !errwrap.Contains(err, logical.ErrInvalidRequest.Error()) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !resp.IsError() {
|
||||||
|
t.Fatalf("bad: expected error, got %#v", *resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -21,7 +21,8 @@ in-memory). It is only made for development or experimentation.
|
|||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
The properties of the dev server:
|
The properties of the dev server (some can be overridden with command line
|
||||||
|
flags or by specifying a configuration file):
|
||||||
|
|
||||||
* **Initialized and unsealed** - The server will be automatically initialized
|
* **Initialized and unsealed** - The server will be automatically initialized
|
||||||
and unsealed. You don't need to use `vault operator unseal`. It is ready
|
and unsealed. You don't need to use `vault operator unseal`. It is ready
|
||||||
|
|||||||
@ -1,16 +1,15 @@
|
|||||||
---
|
---
|
||||||
layout: "docs"
|
layout: "docs"
|
||||||
page_title: "Basic Concepts"
|
page_title: "Concepts"
|
||||||
sidebar_current: "docs-concepts"
|
sidebar_current: "docs-concepts"
|
||||||
description: |-
|
description: |-
|
||||||
Basic concepts that are important to understand for Vault usage.
|
Concepts that are important to understand for Vault usage.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Basic Concepts
|
# Concepts
|
||||||
|
|
||||||
This section covers some high level basic concepts that are important
|
This section covers some concepts that are important to understand for day to
|
||||||
to understand for day to day Vault usage and operation. Every page in
|
day Vault usage and operation. Every page in this section is recommended
|
||||||
this section is recommended reading for anyone consuming or operating
|
reading for anyone consuming or operating Vault.
|
||||||
Vault.
|
|
||||||
|
|
||||||
Please use the navigation to the left to learn more about a topic.
|
Please use the navigation to the left to learn more about a topic.
|
||||||
|
|||||||
@ -8,12 +8,12 @@ description: |-
|
|||||||
|
|
||||||
# Lease, Renew, and Revoke
|
# Lease, Renew, and Revoke
|
||||||
|
|
||||||
With every dynamic secret and authentication token, Vault creates a _lease_:
|
With every dynamic secret and `service` type authentication token, Vault
|
||||||
metadata containing information such as a time duration, renewability, and
|
creates a _lease_: metadata containing information such as a time duration,
|
||||||
more. Vault promises that the data will be valid for the given duration, or
|
renewability, and more. Vault promises that the data will be valid for the
|
||||||
Time To Live (TTL). Once the lease is expired, Vault can automatically revoke
|
given duration, or Time To Live (TTL). Once the lease is expired, Vault can
|
||||||
the data, and the consumer of the secret can no longer be certain that it is
|
automatically revoke the data, and the consumer of the secret can no longer be
|
||||||
valid.
|
certain that it is valid.
|
||||||
|
|
||||||
The benefit should be clear: consumers of secrets need to check in with
|
The benefit should be clear: consumers of secrets need to check in with
|
||||||
Vault routinely to either renew the lease (if allowed) or request a
|
Vault routinely to either renew the lease (if allowed) or request a
|
||||||
@ -35,7 +35,8 @@ or automatically by Vault. When a lease is expired, Vault will automatically
|
|||||||
revoke that lease.
|
revoke that lease.
|
||||||
|
|
||||||
**Note**: The [Key/Value Backend](/docs/secrets/kv/index.html) which stores
|
**Note**: The [Key/Value Backend](/docs/secrets/kv/index.html) which stores
|
||||||
arbitrary secrets does not issue leases.
|
arbitrary secrets does not issue leases although it will sometimes return a
|
||||||
|
lease duration; see the documentation for more information.
|
||||||
|
|
||||||
## Lease IDs
|
## Lease IDs
|
||||||
|
|
||||||
@ -61,14 +62,6 @@ so often.
|
|||||||
As a result, the return value of renewals should be carefully inspected to
|
As a result, the return value of renewals should be carefully inspected to
|
||||||
determine what the new lease is.
|
determine what the new lease is.
|
||||||
|
|
||||||
**Note**: Prior to version 0.3, Vault documentation and help text did not
|
|
||||||
distinguish sufficiently between a _lease_ and a _lease duration_. Starting
|
|
||||||
with version 0.3, Vault will start migrating to the term _ttl_ to describe
|
|
||||||
lease durations, at least for user-facing text. As _lease duration_ is still a
|
|
||||||
legitimate (but more verbose) description, there are currently no plans to
|
|
||||||
change the JSON key used in responses, in order to retain
|
|
||||||
backwards-compatibility.
|
|
||||||
|
|
||||||
## Prefix-based Revocation
|
## Prefix-based Revocation
|
||||||
|
|
||||||
In addition to revoking a single secret, operators with proper access control
|
In addition to revoking a single secret, operators with proper access control
|
||||||
|
|||||||
@ -31,9 +31,15 @@ renewal time, and more.
|
|||||||
|
|
||||||
Read on for a deeper dive into token concepts.
|
Read on for a deeper dive into token concepts.
|
||||||
|
|
||||||
## Token Concepts
|
## Token Types
|
||||||
|
|
||||||
### The Token Store
|
As of Vault 1.0, there are two types of tokens: `service` tokens and `batch`
|
||||||
|
tokens. A section near the bottom of this page contains detailed information
|
||||||
|
about their differences, but it is useful to understand other token concepts
|
||||||
|
first. The features in the following sections all apply to service tokens, and
|
||||||
|
their applicability to batch tokens is discussed later.
|
||||||
|
|
||||||
|
## The Token Store
|
||||||
|
|
||||||
Often in documentation or in help channels, the "token store" is referenced.
|
Often in documentation or in help channels, the "token store" is referenced.
|
||||||
This is the same as the [`token` authentication
|
This is the same as the [`token` authentication
|
||||||
@ -42,13 +48,13 @@ backend in that it is responsible for creating and storing tokens, and cannot
|
|||||||
be disabled. It is also the only auth method that has no login
|
be disabled. It is also the only auth method that has no login
|
||||||
capability -- all actions require existing authenticated tokens.
|
capability -- all actions require existing authenticated tokens.
|
||||||
|
|
||||||
### Root Tokens
|
## Root Tokens
|
||||||
|
|
||||||
Root tokens are tokens that have the `root` policy attached to them. Root
|
Root tokens are tokens that have the `root` policy attached to them. Root
|
||||||
tokens can do anything in Vault. _Anything_. In addition, they are the only
|
tokens can do anything in Vault. _Anything_. In addition, they are the only
|
||||||
type of token within Vault that can be set to never expire without any renewal
|
type of token within Vault that can be set to never expire without any renewal
|
||||||
needed. As a result, it is purposefully hard to create root tokens; in fact, as
|
needed. As a result, it is purposefully hard to create root tokens; in fact
|
||||||
of version 0.6.1, there are only three ways to create root tokens:
|
there are only three ways to create root tokens:
|
||||||
|
|
||||||
1. The initial root token generated at `vault operator init` time -- this token has no
|
1. The initial root token generated at `vault operator init` time -- this token has no
|
||||||
expiration
|
expiration
|
||||||
@ -70,7 +76,7 @@ whenever a root token is live. This way multiple people can verify as to the
|
|||||||
tasks performed with the root token, and that the token was revoked immediately
|
tasks performed with the root token, and that the token was revoked immediately
|
||||||
after these tasks were completed.
|
after these tasks were completed.
|
||||||
|
|
||||||
### Token Hierarchies and Orphan Tokens
|
## Token Hierarchies and Orphan Tokens
|
||||||
|
|
||||||
Normally, when a token holder creates new tokens, these tokens will be created
|
Normally, when a token holder creates new tokens, these tokens will be created
|
||||||
as children of the original token; tokens they create will be children of them;
|
as children of the original token; tokens they create will be children of them;
|
||||||
@ -93,7 +99,7 @@ endpoint, which revokes the given token but rather than revoke the rest of the
|
|||||||
tree, it instead sets the tokens' immediate children to be orphans. Use with
|
tree, it instead sets the tokens' immediate children to be orphans. Use with
|
||||||
caution!
|
caution!
|
||||||
|
|
||||||
### Token Accessors
|
## Token Accessors
|
||||||
|
|
||||||
When tokens are created, a token accessor is also created and returned. This
|
When tokens are created, a token accessor is also created and returned. This
|
||||||
accessor is a value that acts as a reference to a token and can only be used to
|
accessor is a value that acts as a reference to a token and can only be used to
|
||||||
@ -121,7 +127,7 @@ dangerous endpoint (since listing all of the accessors means that they can then
|
|||||||
be used to revoke all tokens), it also provides a way to audit and revoke the
|
be used to revoke all tokens), it also provides a way to audit and revoke the
|
||||||
currently-active set of tokens.
|
currently-active set of tokens.
|
||||||
|
|
||||||
### Token Time-To-Live, Periodic Tokens, and Explicit Max TTLs
|
## Token Time-To-Live, Periodic Tokens, and Explicit Max TTLs
|
||||||
|
|
||||||
Every non-root token has a time-to-live (TTL) associated with it, which is a
|
Every non-root token has a time-to-live (TTL) associated with it, which is a
|
||||||
current period of validity since either the token's creation time or last
|
current period of validity since either the token's creation time or last
|
||||||
@ -137,7 +143,7 @@ token is a periodic token (available for creation by `root`/`sudo` users, token
|
|||||||
store roles, or some auth methods), has an explicit maximum TTL
|
store roles, or some auth methods), has an explicit maximum TTL
|
||||||
attached, or neither.
|
attached, or neither.
|
||||||
|
|
||||||
#### The General Case
|
### The General Case
|
||||||
|
|
||||||
In the general case, where there is neither a period nor explicit maximum TTL
|
In the general case, where there is neither a period nor explicit maximum TTL
|
||||||
value set on the token, the token's lifetime since it was created will be
|
value set on the token, the token's lifetime since it was created will be
|
||||||
@ -165,7 +171,7 @@ current value and the client may want to reauthenticate and acquire a new
|
|||||||
token. However, outside of direct operator interaction, Vault will never revoke
|
token. However, outside of direct operator interaction, Vault will never revoke
|
||||||
a token before the returned TTL has expired.
|
a token before the returned TTL has expired.
|
||||||
|
|
||||||
#### Explicit Max TTLs
|
### Explicit Max TTLs
|
||||||
|
|
||||||
Tokens can have an explicit max TTL set on them. This value becomes a hard
|
Tokens can have an explicit max TTL set on them. This value becomes a hard
|
||||||
limit on the token's lifetime -- no matter what the values in (1), (2), and (3)
|
limit on the token's lifetime -- no matter what the values in (1), (2), and (3)
|
||||||
@ -173,7 +179,7 @@ from the general case are, the token cannot live past this explicitly-set
|
|||||||
value. This has an effect even when using periodic tokens to escape the normal
|
value. This has an effect even when using periodic tokens to escape the normal
|
||||||
TTL mechanism.
|
TTL mechanism.
|
||||||
|
|
||||||
#### Periodic Tokens
|
### Periodic Tokens
|
||||||
|
|
||||||
In some cases, having a token be revoked would be problematic -- for instance,
|
In some cases, having a token be revoked would be problematic -- for instance,
|
||||||
if a long-running service needs to maintain its SQL connection pool over a long
|
if a long-running service needs to maintain its SQL connection pool over a long
|
||||||
@ -209,9 +215,65 @@ There are a few important things to know when using periodic tokens:
|
|||||||
* A token with both a period and an explicit max TTL will act like a periodic
|
* A token with both a period and an explicit max TTL will act like a periodic
|
||||||
token but will be revoked when the explicit max TTL is reached
|
token but will be revoked when the explicit max TTL is reached
|
||||||
|
|
||||||
### CIDR-Bound Tokens
|
## CIDR-Bound Tokens
|
||||||
|
|
||||||
Some tokens are able to be bound to CIDR(s) that restrict the range of client
|
Some tokens are able to be bound to CIDR(s) that restrict the range of client
|
||||||
IPs allowed to use them. These affect all tokens except for non-expiring root
|
IPs allowed to use them. These affect all tokens except for non-expiring root
|
||||||
tokens (those with a TTL of zero). If a root token has an expiration, it also
|
tokens (those with a TTL of zero). If a root token has an expiration, it also
|
||||||
is affected by CIDR-binding.
|
is affected by CIDR-binding.
|
||||||
|
|
||||||
|
## Token Types in Detail
|
||||||
|
|
||||||
|
There are currently two types of tokens.
|
||||||
|
|
||||||
|
### Service Tokens
|
||||||
|
|
||||||
|
Service tokens are what users will generally think of as "normal" Vault tokens.
|
||||||
|
They support all features, such as renewal, revocation, creating child tokens,
|
||||||
|
and more. The are correspondingly heavyweight to create and track.
|
||||||
|
|
||||||
|
### Batch Tokens
|
||||||
|
|
||||||
|
Batch tokens are are encrypted blobs that carry enough information for them to
|
||||||
|
be used for Vault actions, but they require no storage on disk to track them.
|
||||||
|
As a result they are extremely lightweight and scalable, but lack most of the
|
||||||
|
flexibility and features of service tokens.
|
||||||
|
|
||||||
|
### Token Type Comparison
|
||||||
|
|
||||||
|
This reference chart describes the difference in behavior between service and
|
||||||
|
batch tokens.
|
||||||
|
|
||||||
|
| | Service Tokens | Batch Tokens |
|
||||||
|
|---|---:|---:|
|
||||||
|
| Can Be Root Tokens | Yes | No |
|
||||||
|
| Can Create Child Tokens | Yes | No |
|
||||||
|
| Can be Renewable | Yes | No |
|
||||||
|
| Can be Periodic | Yes | No |
|
||||||
|
| Can have Explicit Max TTL | Yes | No (always uses a fixed TTL) |
|
||||||
|
| Has Accessors | Yes | No |
|
||||||
|
| Has Cubbyhole | Yes | No |
|
||||||
|
| Revoked with Parent (if not orphan) | Yes | Stops Working |
|
||||||
|
| Dynamic Secrets Lease Assignment | Self | Parent (if not orphan) |
|
||||||
|
| Can be Used Across Performance Replication Clusters | No | Yes (if orphan) |
|
||||||
|
| Creation Scales with Performance Standby Node Count | No | Yes |
|
||||||
|
| Cost | Heavyweight; multiple storage writes per token creation | Lightweight; no storage cost for token creation |
|
||||||
|
|
||||||
|
### Service vs. Batch Token Lease Handling
|
||||||
|
|
||||||
|
#### Service Tokens
|
||||||
|
|
||||||
|
Leases created by service tokens (including child tokens' leases) are tracked
|
||||||
|
along with the service token and revoked when the token expires.
|
||||||
|
|
||||||
|
#### Batch Tokens
|
||||||
|
|
||||||
|
Leases created by batch tokens are constrained to the remaining TTL of the
|
||||||
|
batch tokens and, if the batch token is not an orphan, are tracked by the
|
||||||
|
parent. They are revoked when the batch token's TTL expires, or when the batch
|
||||||
|
token's parent is revoked (at which point the batch token is also denied access
|
||||||
|
to Vault).
|
||||||
|
|
||||||
|
As a corollary, batch tokens can be used across performance replication
|
||||||
|
clusters, but only if they are orphan, since non-orphan tokens will not be able
|
||||||
|
to ensure the validity of the parent token.
|
||||||
|
|||||||
@ -43,7 +43,7 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li<%= sidebar_current("docs-concepts") %>>
|
<li<%= sidebar_current("docs-concepts") %>>
|
||||||
<a href="/docs/concepts/index.html">Basic Concepts</a>
|
<a href="/docs/concepts/index.html">Concepts</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li<%= sidebar_current("docs-concepts-devserver") %>>
|
<li<%= sidebar_current("docs-concepts-devserver") %>>
|
||||||
<a href="/docs/concepts/dev-server.html">"Dev" Server</a>
|
<a href="/docs/concepts/dev-server.html">"Dev" Server</a>
|
||||||
@ -63,6 +63,32 @@
|
|||||||
|
|
||||||
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||||
<a href="/docs/concepts/tokens.html">Tokens</a>
|
<a href="/docs/concepts/tokens.html">Tokens</a>
|
||||||
|
<ul class="nav">
|
||||||
|
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||||
|
<a href="/docs/concepts/tokens.html#token-types">Token Types</a>
|
||||||
|
</li>
|
||||||
|
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||||
|
<a href="/docs/concepts/tokens.html#the-token-store">The Token Store</a>
|
||||||
|
</li>
|
||||||
|
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||||
|
<a href="/docs/concepts/tokens.html#root-tokens">Root Tokens</a>
|
||||||
|
</li>
|
||||||
|
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||||
|
<a href="/docs/concepts/tokens.html#token-hierarchies-and-orphan-tokens">Token Hierarchies and Orphan Tokens</a>
|
||||||
|
</li>
|
||||||
|
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||||
|
<a href="/docs/concepts/tokens.html#token-accessors">Token Accessors</a>
|
||||||
|
</li>
|
||||||
|
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||||
|
<a href="/docs/concepts/tokens.html#token-time-to-live-periodic-tokens-and-explicit-max-ttls">Token Time-To-Live, Periodic Tokens, and Explicit Max TTLs</a>
|
||||||
|
</li>
|
||||||
|
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||||
|
<a href="/docs/concepts/tokens.html#cidr-bound-tokens">CIDR-Bound Tokens</a>
|
||||||
|
</li>
|
||||||
|
<li<%= sidebar_current("docs-concepts-tokens") %>>
|
||||||
|
<a href="/docs/concepts/tokens.html#token-types-in-detail">Token Types in Detail</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li<%= sidebar_current("docs-concepts-response-wrapping") %>>
|
<li<%= sidebar_current("docs-concepts-response-wrapping") %>>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user