Skip to content

Commit

Permalink
Batch tokens (#755)
Browse files Browse the repository at this point in the history
  • Loading branch information
jefferai committed Oct 15, 2018
1 parent 8db6aab commit a58d313
Show file tree
Hide file tree
Showing 51 changed files with 2,118 additions and 518 deletions.
1 change: 1 addition & 0 deletions api/auth_token.go
Expand Up @@ -271,4 +271,5 @@ type TokenCreateRequest struct {
DisplayName string `json:"display_name"`
NumUses int `json:"num_uses"`
Renewable *bool `json:"renewable,omitempty"`
Type string `json:"type"`
}
48 changes: 5 additions & 43 deletions api/sys_auth.go
Expand Up @@ -73,46 +73,8 @@ func (c *Sys) DisableAuth(path string) error {
return err
}

// Structures for the requests/resposne are all down here. They aren't
// individually documented because the map almost directly to the raw HTTP API
// documentation. Please refer to that documentation for more details.

type EnableAuthOptions struct {
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"`
}
// Rather than duplicate, we can use modern Go's type aliasing
type EnableAuthOptions = MountInput
type AuthConfigInput = MountConfigInput
type AuthMount = MountOutput
type AuthConfigOutput = MountConfigOutput
4 changes: 3 additions & 1 deletion api/sys_mounts.go
Expand Up @@ -132,10 +132,10 @@ type MountInput struct {
Type string `json:"type"`
Description string `json:"description"`
Config MountConfigInput `json:"config"`
Options map[string]string `json:"options"`
Local bool `json:"local"`
PluginName string `json:"plugin_name,omitempty"`
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
Options map[string]string `json:"options"`
}

type MountConfigInput struct {
Expand All @@ -149,6 +149,7 @@ type MountConfigInput struct {
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"`
TokenType string `json:"token_type,omitempty" mapstructure:"token_type"`
}

type MountOutput struct {
Expand All @@ -170,4 +171,5 @@ type MountConfigOutput struct {
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"`
TokenType string `json:"token_type,omitempty" mapstructure:"token_type"`
}
9 changes: 7 additions & 2 deletions audit/format.go
Expand Up @@ -134,6 +134,7 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config
Metadata: auth.Metadata,
EntityID: auth.EntityID,
RemainingUses: req.ClientTokenRemainingUses,
TokenType: auth.TokenType.String(),
},

Request: AuditRequest{
Expand Down Expand Up @@ -304,6 +305,8 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
ExternalNamespacePolicies: resp.Auth.ExternalNamespacePolicies,
Metadata: resp.Auth.Metadata,
NumUses: resp.Auth.NumUses,
EntityID: resp.Auth.EntityID,
TokenType: resp.Auth.TokenType.String(),
}
}

Expand Down Expand Up @@ -334,16 +337,17 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
Type: "response",
Error: errString,
Auth: AuditAuth{
ClientToken: auth.ClientToken,
Accessor: auth.Accessor,
DisplayName: auth.DisplayName,
Policies: auth.Policies,
TokenPolicies: auth.TokenPolicies,
IdentityPolicies: auth.IdentityPolicies,
ExternalNamespacePolicies: auth.ExternalNamespacePolicies,
Metadata: auth.Metadata,
ClientToken: auth.ClientToken,
Accessor: auth.Accessor,
RemainingUses: req.ClientTokenRemainingUses,
EntityID: auth.EntityID,
TokenType: auth.TokenType.String(),
},

Request: AuditRequest{
Expand Down Expand Up @@ -437,6 +441,7 @@ type AuditAuth struct {
NumUses int `json:"num_uses,omitempty"`
RemainingUses int `json:"remaining_uses,omitempty"`
EntityID string `json:"entity_id"`
TokenType string `json:"token_type"`
}

type AuditSecret struct {
Expand Down
18 changes: 15 additions & 3 deletions audit/format_json_test.go
Expand Up @@ -37,7 +37,13 @@ func TestFormatJSON_formatRequest(t *testing.T) {
ExpectedStr string
}{
"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{
Operation: logical.UpdateOperation,
Path: "/foo",
Expand All @@ -56,7 +62,13 @@ func TestFormatJSON_formatRequest(t *testing.T) {
expectedResultStr,
},
"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{
Operation: logical.UpdateOperation,
Path: "/foo",
Expand Down Expand Up @@ -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"}
`
20 changes: 16 additions & 4 deletions audit/format_jsonx_test.go
Expand Up @@ -36,7 +36,13 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
ExpectedStr string
}{
"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{
Operation: logical.UpdateOperation,
Path: "/foo",
Expand All @@ -53,11 +59,17 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
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),
},
"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{
Operation: logical.UpdateOperation,
Path: "/foo",
Expand All @@ -74,7 +86,7 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
errors.New("this is an error"),
"",
"@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),
},
}
Expand Down
9 changes: 9 additions & 0 deletions builtin/credential/approle/path_login.go
Expand Up @@ -304,6 +304,15 @@ func (b *backend) pathLoginUpdate(ctx context.Context, req *logical.Request, dat
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{
Auth: auth,
}, nil
Expand Down
33 changes: 33 additions & 0 deletions builtin/credential/approle/path_role.go
Expand Up @@ -84,6 +84,9 @@ type roleStorageEntry struct {
// SecretIDPrefix is the storage prefix for persisting secret IDs. This
// differs based on whether the secret IDs are cluster local or not.
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
Expand Down Expand Up @@ -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
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,
Callbacks: map[logical.Operation]framework.OperationFunc{
Expand Down Expand Up @@ -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))
}

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.
// Sanitizing the TTL and MaxTTL is not required now and can be performed
// at credential issue time.
Expand Down Expand Up @@ -1061,6 +1093,7 @@ func (b *backend) pathRoleRead(ctx context.Context, req *logical.Request, data *
"token_num_uses": role.TokenNumUses,
"token_ttl": role.TokenTTL / time.Second,
"local_secret_ids": false,
"token_type": role.TokenType,
}

if role.SecretIDPrefix == secretIDLocalPrefix {
Expand Down
2 changes: 2 additions & 0 deletions builtin/credential/approle/path_role_test.go
Expand Up @@ -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"},
"bound_cidr_list": []string{"127.0.0.1/32", "127.0.0.1/16"}, // returned for backwards compatibility
"token_bound_cidrs": []string{},
"token_type": "default",
}

var expectedStruct roleStorageEntry
Expand Down Expand Up @@ -1637,6 +1638,7 @@ func TestAppRole_RoleWithTokenBoundCIDRsCRUD(t *testing.T) {
"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"},
"bound_cidr_list": []string{"127.0.0.1/32", "127.0.0.1/16"}, // provided for backwards compatibility
"token_type": "default",
}

var expectedStruct roleStorageEntry
Expand Down
11 changes: 11 additions & 0 deletions command/auth_enable.go
Expand Up @@ -30,6 +30,7 @@ type AuthEnableCommand struct {
flagOptions map[string]string
flagLocal bool
flagSealWrap bool
flagTokenType string
flagVersion int
}

Expand Down Expand Up @@ -162,6 +163,12 @@ func (c *AuthEnableCommand) Flags() *FlagSets {
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{
Name: "version",
Target: &c.flagVersion,
Expand Down Expand Up @@ -257,6 +264,10 @@ func (c *AuthEnableCommand) Run(args []string) int {
if fl.Name == flagNamePassthroughRequestHeaders {
authOpts.Config.PassthroughRequestHeaders = c.flagPassthroughRequestHeaders
}

if fl.Name == flagNameTokenType {
authOpts.Config.TokenType = c.flagTokenType
}
})

if err := client.Sys().EnableAuthWithOptions(authPath, authOpts); err != nil {
Expand Down

0 comments on commit a58d313

Please sign in to comment.