2015-03-18 20:19:19 +00:00
package vault
import (
"encoding/json"
"fmt"
2015-04-15 21:24:07 +00:00
"regexp"
2015-12-30 20:18:30 +00:00
"sort"
2015-03-24 22:10:46 +00:00
"strings"
"time"
2015-03-18 20:19:19 +00:00
2015-04-08 23:43:17 +00:00
"github.com/armon/go-metrics"
2016-02-29 18:27:31 +00:00
"github.com/fatih/structs"
2015-12-16 17:56:20 +00:00
"github.com/hashicorp/go-uuid"
2015-10-30 14:59:26 +00:00
"github.com/hashicorp/vault/helper/salt"
2015-03-18 20:19:19 +00:00
"github.com/hashicorp/vault/logical"
2015-03-31 19:48:19 +00:00
"github.com/hashicorp/vault/logical/framework"
2015-04-07 21:16:35 +00:00
"github.com/mitchellh/mapstructure"
2015-03-18 20:19:19 +00:00
)
const (
// lookupPrefix is the prefix used to store tokens for their
// primary ID based index
lookupPrefix = "id/"
// parentPrefix is the prefix used to store tokens for their
// secondar parent based index
parentPrefix = "parent/"
2015-03-18 21:00:42 +00:00
// tokenSubPath is the sub-path used for the token store
// view. This is nested under the system view.
tokenSubPath = "token/"
2015-03-18 20:19:19 +00:00
)
2015-04-15 21:24:07 +00:00
var (
// displayNameSanitize is used to sanitize a display name given to a token.
displayNameSanitize = regexp . MustCompile ( "[^a-zA-Z0-9-]" )
2016-02-29 18:27:31 +00:00
// prefixSanitize is used to ensure a prefix in a role is valid.
prefixSanitize = regexp . MustCompile ( "\\w[\\w-.]+\\w" )
2015-04-15 21:24:07 +00:00
)
2015-03-18 20:19:19 +00:00
// TokenStore is used to manage client tokens. Tokens are used for
// clients to authenticate, and each token is mapped to an applicable
// set of policy which is used for authorization.
type TokenStore struct {
2015-03-31 19:48:19 +00:00
* framework . Backend
2015-03-18 20:19:19 +00:00
view * BarrierView
2015-06-30 21:08:21 +00:00
salt * salt . Salt
2015-04-03 18:40:08 +00:00
expiration * ExpirationManager
2015-09-10 01:58:09 +00:00
2015-09-15 17:49:53 +00:00
cubbyholeBackend * CubbyholeBackend
2015-10-07 19:30:54 +00:00
2015-11-06 16:36:40 +00:00
policyLookupFunc func ( string ) ( * Policy , error )
2015-03-18 20:19:19 +00:00
}
// NewTokenStore is used to construct a token store that is
// backed by the given barrier view.
2015-09-04 20:58:12 +00:00
func NewTokenStore ( c * Core , config * logical . BackendConfig ) ( * TokenStore , error ) {
2015-03-19 02:11:52 +00:00
// Create a sub-view
2015-09-04 20:58:12 +00:00
view := c . systemBarrierView . SubView ( tokenSubPath )
2015-03-19 02:11:52 +00:00
2015-03-18 20:19:19 +00:00
// Initialize the store
t := & TokenStore {
2015-09-15 15:28:07 +00:00
view : view ,
2015-03-18 20:19:19 +00:00
}
2015-11-06 16:52:26 +00:00
if c . policyStore != nil {
t . policyLookupFunc = c . policyStore . GetPolicy
2015-10-07 19:30:54 +00:00
}
2015-06-30 21:08:21 +00:00
// Setup the salt
2015-09-18 16:18:37 +00:00
salt , err := salt . NewSalt ( view , & salt . Config {
HashFunc : salt . SHA1Hash ,
} )
2015-03-18 20:19:19 +00:00
if err != nil {
2015-06-30 21:08:21 +00:00
return nil , err
2015-03-18 20:19:19 +00:00
}
2015-06-30 21:08:21 +00:00
t . salt = salt
2015-03-31 19:48:19 +00:00
// Setup the framework endpoints
t . Backend = & framework . Backend {
2016-01-29 22:44:09 +00:00
AuthRenew : t . authRenew ,
2015-04-11 23:28:16 +00:00
2015-04-03 18:40:08 +00:00
PathsSpecial : & logical . Paths {
Root : [ ] string {
"revoke-prefix/*" ,
2015-09-16 13:22:15 +00:00
"revoke-orphan/*" ,
2015-04-03 18:40:08 +00:00
} ,
} ,
2015-03-31 19:48:19 +00:00
Paths : [ ] * framework . Path {
2015-11-03 20:10:46 +00:00
& framework . Path {
2016-02-29 18:27:31 +00:00
Pattern : "roles/?$" ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . ListOperation : t . tokenStoreRoleList ,
} ,
// HelpSynopsis: pathRoleHelpSyn,
// HelpDescription: pathRoleHelpDesc,
} ,
& framework . Path {
Pattern : "roles/" + framework . GenericNameRegex ( "role_name" ) ,
Fields : map [ string ] * framework . FieldSchema {
"role_name" : & framework . FieldSchema {
Type : framework . TypeString ,
Description : "Name of the role" ,
} ,
"allowed_policies" : & framework . FieldSchema {
Type : framework . TypeString ,
Default : "" ,
Description : ` If set , tokens created via this role
can be created with any subset of this list ,
rather than the normal semantics of a subset
of the client token ' s policies . This
parameter should be sent as a comma - delimited
string . ` ,
} ,
"orphan" : & framework . FieldSchema {
Type : framework . TypeBool ,
Default : false ,
Description : ` If true , tokens created via this role
will be orphan tokens ( have no parent ) ` ,
} ,
"period" : & framework . FieldSchema {
Type : framework . TypeDurationSecond ,
Default : 0 ,
Description : ` If set , tokens created via this role
will have no max lifetime ; instead , their
renewal period will be fixed to this value .
This takes an integer number of seconds ,
or a string duration ( e . g . "24h" ) . ` ,
} ,
"prefix" : & framework . FieldSchema {
Type : framework . TypeString ,
Default : "" ,
Description : ` If set , tokens created via this role
will contain the given prefix as a part of
their path . This can be used to assist use
of the ' revoke - prefix ' endpoint later on .
The given prefix must match the regular
expression ` + prefixSanitize . String ( ) ,
} ,
} ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . ReadOperation : t . tokenStoreRoleRead ,
logical . UpdateOperation : t . tokenStoreRoleCreate ,
logical . DeleteOperation : t . tokenStoreRoleDelete ,
} ,
// HelpSynopsis: pathRoleHelpSyn,
// HelpDescription: pathRoleHelpDesc,
} ,
& framework . Path {
2016-02-29 19:56:04 +00:00
Pattern : "create-orphan$" ,
2015-11-03 20:10:46 +00:00
Callbacks : map [ logical . Operation ] framework . OperationFunc {
2016-01-07 15:30:47 +00:00
logical . UpdateOperation : t . handleCreateOrphan ,
2015-11-03 20:10:46 +00:00
} ,
HelpSynopsis : strings . TrimSpace ( tokenCreateOrphanHelp ) ,
HelpDescription : strings . TrimSpace ( tokenCreateOrphanHelp ) ,
} ,
2016-02-29 18:27:31 +00:00
& framework . Path {
Pattern : "create/" + framework . GenericNameRegex ( "role_name" ) ,
Fields : map [ string ] * framework . FieldSchema {
"role_name" : & framework . FieldSchema {
Type : framework . TypeString ,
Description : "Name of the role" ,
} ,
} ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . UpdateOperation : t . handleCreateRole ,
} ,
//HelpSynopsis: strings.TrimSpace(tokenCreateOrphanHelp),
//HelpDescription: strings.TrimSpace(tokenCreateOrphanHelp),
} ,
2015-03-31 19:48:19 +00:00
& framework . Path {
Pattern : "create$" ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
2016-01-07 15:30:47 +00:00
logical . UpdateOperation : t . handleCreate ,
2015-03-31 19:48:19 +00:00
} ,
HelpSynopsis : strings . TrimSpace ( tokenCreateHelp ) ,
HelpDescription : strings . TrimSpace ( tokenCreateHelp ) ,
} ,
& framework . Path {
2015-03-31 19:51:00 +00:00
Pattern : "lookup/(?P<token>.+)" ,
Fields : map [ string ] * framework . FieldSchema {
"token" : & framework . FieldSchema {
Type : framework . TypeString ,
Description : "Token to lookup" ,
} ,
} ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . ReadOperation : t . handleLookup ,
} ,
HelpSynopsis : strings . TrimSpace ( tokenLookupHelp ) ,
HelpDescription : strings . TrimSpace ( tokenLookupHelp ) ,
} ,
& framework . Path {
Pattern : "lookup-self$" ,
2015-03-31 19:48:19 +00:00
Fields : map [ string ] * framework . FieldSchema {
"token" : & framework . FieldSchema {
Type : framework . TypeString ,
Description : "Token to lookup" ,
} ,
} ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . ReadOperation : t . handleLookup ,
} ,
HelpSynopsis : strings . TrimSpace ( tokenLookupHelp ) ,
HelpDescription : strings . TrimSpace ( tokenLookupHelp ) ,
} ,
2015-09-17 17:22:30 +00:00
& framework . Path {
2015-10-07 16:49:13 +00:00
Pattern : "revoke-self$" ,
2015-09-17 17:22:30 +00:00
Callbacks : map [ logical . Operation ] framework . OperationFunc {
2016-01-07 15:30:47 +00:00
logical . UpdateOperation : t . handleRevokeSelf ,
2015-09-17 17:22:30 +00:00
} ,
HelpSynopsis : strings . TrimSpace ( tokenRevokeSelfHelp ) ,
HelpDescription : strings . TrimSpace ( tokenRevokeSelfHelp ) ,
} ,
2015-03-31 19:48:19 +00:00
& framework . Path {
Pattern : "revoke/(?P<token>.+)" ,
Fields : map [ string ] * framework . FieldSchema {
"token" : & framework . FieldSchema {
Type : framework . TypeString ,
Description : "Token to revoke" ,
} ,
} ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
2016-01-07 15:30:47 +00:00
logical . UpdateOperation : t . handleRevokeTree ,
2015-03-31 19:48:19 +00:00
} ,
HelpSynopsis : strings . TrimSpace ( tokenRevokeHelp ) ,
HelpDescription : strings . TrimSpace ( tokenRevokeHelp ) ,
} ,
2015-04-03 19:11:49 +00:00
2015-03-31 19:48:19 +00:00
& framework . Path {
Pattern : "revoke-orphan/(?P<token>.+)" ,
Fields : map [ string ] * framework . FieldSchema {
"token" : & framework . FieldSchema {
Type : framework . TypeString ,
Description : "Token to revoke" ,
} ,
} ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
2016-01-07 15:30:47 +00:00
logical . UpdateOperation : t . handleRevokeOrphan ,
2015-03-31 19:48:19 +00:00
} ,
HelpSynopsis : strings . TrimSpace ( tokenRevokeOrphanHelp ) ,
HelpDescription : strings . TrimSpace ( tokenRevokeOrphanHelp ) ,
} ,
2015-04-03 18:40:08 +00:00
& framework . Path {
Pattern : "revoke-prefix/(?P<prefix>.+)" ,
Fields : map [ string ] * framework . FieldSchema {
"prefix" : & framework . FieldSchema {
Type : framework . TypeString ,
Description : "Token source prefix to revoke" ,
} ,
} ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
2016-01-07 15:30:47 +00:00
logical . UpdateOperation : t . handleRevokePrefix ,
2015-04-03 18:40:08 +00:00
} ,
HelpSynopsis : strings . TrimSpace ( tokenRevokePrefixHelp ) ,
HelpDescription : strings . TrimSpace ( tokenRevokePrefixHelp ) ,
} ,
2015-04-03 19:11:49 +00:00
2015-10-07 16:49:13 +00:00
& framework . Path {
Pattern : "renew-self$" ,
Fields : map [ string ] * framework . FieldSchema {
"token" : & framework . FieldSchema {
Type : framework . TypeString ,
Description : "Token to renew" ,
} ,
"increment" : & framework . FieldSchema {
Type : framework . TypeDurationSecond ,
Description : "The desired increment in seconds to the token expiration" ,
} ,
} ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
2016-01-07 15:30:47 +00:00
logical . UpdateOperation : t . handleRenewSelf ,
2015-10-07 16:49:13 +00:00
} ,
HelpSynopsis : strings . TrimSpace ( tokenRenewSelfHelp ) ,
HelpDescription : strings . TrimSpace ( tokenRenewSelfHelp ) ,
} ,
2015-04-03 19:11:49 +00:00
& framework . Path {
Pattern : "renew/(?P<token>.+)" ,
Fields : map [ string ] * framework . FieldSchema {
"token" : & framework . FieldSchema {
Type : framework . TypeString ,
Description : "Token to renew" ,
} ,
2015-04-09 21:23:37 +00:00
"increment" : & framework . FieldSchema {
2015-06-17 22:58:20 +00:00
Type : framework . TypeDurationSecond ,
2015-04-09 21:23:37 +00:00
Description : "The desired increment in seconds to the token expiration" ,
} ,
2015-04-03 19:11:49 +00:00
} ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
2016-01-07 15:30:47 +00:00
logical . UpdateOperation : t . handleRenew ,
2015-04-03 19:11:49 +00:00
} ,
HelpSynopsis : strings . TrimSpace ( tokenRenewHelp ) ,
HelpDescription : strings . TrimSpace ( tokenRenewHelp ) ,
} ,
2015-03-31 19:48:19 +00:00
} ,
}
2015-09-04 20:58:12 +00:00
t . Backend . Setup ( config )
2015-03-18 20:19:19 +00:00
return t , nil
}
// TokenEntry is used to represent a given token
type TokenEntry struct {
2016-01-04 21:43:07 +00:00
ID string // ID of this entry, generally a random UUID
Parent string // Parent token, used for revocation trees
Policies [ ] string // Which named policies should be used
Path string // Used for audit trails, this is something like "auth/user/login"
Meta map [ string ] string // Used for auditing. This could include things like "source", "user", "ip"
DisplayName string // Used for operators to be able to associate with the source
NumUses int // Used to restrict the number of uses (zero is unlimited). This is to support one-time-tokens (generalized).
CreationTime int64 // Time of token creation
TTL time . Duration // Duration set when token was created
2016-02-29 18:27:31 +00:00
Role string // If set, the role that was used for parameters at creation time
}
// tsRoleEntry contains token store role information
type tsRoleEntry struct {
// The name of the role. Embedded so it can be used for pathing
2016-02-29 19:13:09 +00:00
Name string ` json:"name" mapstructure:"name" structs:"name" `
2016-02-29 18:27:31 +00:00
// The policies that creation functions using this role can assign to a token,
// escaping or further locking down normal subset checking
2016-02-29 19:13:09 +00:00
AllowedPolicies [ ] string ` json:"allowed_policies" mapstructure:"allowed_policies" structs:"allowed_policies" `
2016-02-29 18:27:31 +00:00
// If true, tokens created using this role will be orphans
2016-02-29 19:13:09 +00:00
Orphan bool ` json:"orphan" mapstructure:"orphan" structs:"orphan" `
2016-02-29 18:27:31 +00:00
// If non-zero, tokens created using this role will be able to be renewed
// forever, but will have a fixed renewal period of this value
2016-02-29 19:13:09 +00:00
Period time . Duration ` json:"period" mapstructure:"period" structs:"period" `
2016-02-29 18:27:31 +00:00
// If set, a prefix will be set on leases, making it easier to revoke using
// 'revoke-prefix'.
2016-02-29 19:13:09 +00:00
Prefix string ` json:"prefix" mapstructure:"prefix" structs:"prefix" `
2015-03-18 20:19:19 +00:00
}
2015-04-03 18:40:08 +00:00
// SetExpirationManager is used to provide the token store with
// an expiration manager. This is used to manage prefix based revocation
// of tokens and to cleanup entries when removed from the token store.
2015-09-18 20:33:52 +00:00
func ( ts * TokenStore ) SetExpirationManager ( exp * ExpirationManager ) {
ts . expiration = exp
2015-04-03 18:40:08 +00:00
}
2015-04-03 00:39:38 +00:00
// SaltID is used to apply a salt and hash to an ID to make sure its not reversable
func ( ts * TokenStore ) SaltID ( id string ) string {
2015-06-30 21:08:21 +00:00
return ts . salt . SaltID ( id )
2015-03-18 20:19:19 +00:00
}
2015-03-24 00:16:37 +00:00
// RootToken is used to generate a new token with root privileges and no parent
2015-10-30 14:59:26 +00:00
func ( ts * TokenStore ) rootToken ( ) ( * TokenEntry , error ) {
2015-03-24 00:16:37 +00:00
te := & TokenEntry {
2015-09-18 20:33:52 +00:00
Policies : [ ] string { "root" } ,
Path : "auth/token/root" ,
DisplayName : "root" ,
CreationTime : time . Now ( ) . Unix ( ) ,
2015-03-24 00:16:37 +00:00
}
2015-10-30 14:59:26 +00:00
if err := ts . create ( te ) ; err != nil {
2015-03-24 00:16:37 +00:00
return nil , err
}
return te , nil
}
2015-03-18 20:19:19 +00:00
// Create is used to create a new token entry. The entry is assigned
2015-03-24 21:22:50 +00:00
// a newly generated ID if not provided.
2015-10-30 14:59:26 +00:00
func ( ts * TokenStore ) create ( entry * TokenEntry ) error {
2015-04-08 23:43:17 +00:00
defer metrics . MeasureSince ( [ ] string { "token" , "create" } , time . Now ( ) )
2015-03-24 21:22:50 +00:00
// Generate an ID if necessary
if entry . ID == "" {
2016-01-13 18:40:08 +00:00
entryUUID , err := uuid . GenerateUUID ( )
if err != nil {
return err
}
entry . ID = entryUUID
2015-03-24 21:22:50 +00:00
}
2015-12-30 19:30:02 +00:00
return ts . storeCommon ( entry , true )
}
2016-01-04 21:43:07 +00:00
// Store is used to store an updated token entry without writing the
// secondary index.
2015-12-30 19:30:02 +00:00
func ( ts * TokenStore ) store ( entry * TokenEntry ) error {
defer metrics . MeasureSince ( [ ] string { "token" , "store" } , time . Now ( ) )
return ts . storeCommon ( entry , false )
}
// storeCommon handles the actual storage of an entry, possibly generating
// secondary indexes
func ( ts * TokenStore ) storeCommon ( entry * TokenEntry , writeSecondary bool ) error {
2015-04-03 00:39:38 +00:00
saltedId := ts . SaltID ( entry . ID )
2015-03-24 21:22:50 +00:00
// Marshal the entry
2015-03-18 20:19:19 +00:00
enc , err := json . Marshal ( entry )
if err != nil {
return fmt . Errorf ( "failed to encode entry: %v" , err )
}
2015-12-30 19:30:02 +00:00
if writeSecondary {
// Write the secondary index if necessary. This is done before the
// primary index because we'd rather have a dangling pointer with
// a missing primary instead of missing the parent index and potentially
// escaping the revocation chain.
if entry . Parent != "" {
// Ensure the parent exists
parent , err := ts . Lookup ( entry . Parent )
if err != nil {
return fmt . Errorf ( "failed to lookup parent: %v" , err )
}
if parent == nil {
return fmt . Errorf ( "parent token not found" )
}
2015-03-18 20:19:19 +00:00
2015-12-30 19:30:02 +00:00
// Create the index entry
path := parentPrefix + ts . SaltID ( entry . Parent ) + "/" + saltedId
le := & logical . StorageEntry { Key : path }
if err := ts . view . Put ( le ) ; err != nil {
return fmt . Errorf ( "failed to persist entry: %v" , err )
}
2015-03-18 20:19:19 +00:00
}
}
// Write the primary ID
path := lookupPrefix + saltedId
le := & logical . StorageEntry { Key : path , Value : enc }
if err := ts . view . Put ( le ) ; err != nil {
return fmt . Errorf ( "failed to persist entry: %v" , err )
}
return nil
}
2015-04-17 18:51:04 +00:00
// UseToken is used to manage restricted use tokens and decrement
// their available uses.
func ( ts * TokenStore ) UseToken ( te * TokenEntry ) error {
// If the token is not restricted, there is nothing to do
if te . NumUses == 0 {
return nil
}
// Decrement the count
te . NumUses -= 1
// Revoke the token if there are no remaining uses.
// XXX: There is a race condition here with parallel
// requests using the same token. This would require
// some global coordination to avoid, as we must ensure
// no requests using the same restricted token are handled
// in parallel.
if te . NumUses == 0 {
return ts . Revoke ( te . ID )
}
// Marshal the entry
enc , err := json . Marshal ( te )
if err != nil {
return fmt . Errorf ( "failed to encode entry: %v" , err )
}
// Write under the primary ID
saltedId := ts . SaltID ( te . ID )
path := lookupPrefix + saltedId
le := & logical . StorageEntry { Key : path , Value : enc }
if err := ts . view . Put ( le ) ; err != nil {
return fmt . Errorf ( "failed to persist entry: %v" , err )
}
return nil
}
2015-03-18 20:19:19 +00:00
// Lookup is used to find a token given its ID
func ( ts * TokenStore ) Lookup ( id string ) ( * TokenEntry , error ) {
2015-04-08 23:43:17 +00:00
defer metrics . MeasureSince ( [ ] string { "token" , "lookup" } , time . Now ( ) )
2015-03-18 20:21:16 +00:00
if id == "" {
return nil , fmt . Errorf ( "cannot lookup blank token" )
}
2015-04-03 00:39:38 +00:00
return ts . lookupSalted ( ts . SaltID ( id ) )
2015-03-18 20:19:19 +00:00
}
// lookupSlated is used to find a token given its salted ID
func ( ts * TokenStore ) lookupSalted ( saltedId string ) ( * TokenEntry , error ) {
// Lookup token
path := lookupPrefix + saltedId
raw , err := ts . view . Get ( path )
if err != nil {
return nil , fmt . Errorf ( "failed to read entry: %v" , err )
}
// Bail if not found
if raw == nil {
return nil , nil
}
// Unmarshal the token
entry := new ( TokenEntry )
if err := json . Unmarshal ( raw . Value , entry ) ; err != nil {
return nil , fmt . Errorf ( "failed to decode entry: %v" , err )
}
return entry , nil
}
// Revoke is used to invalidate a given token, any child tokens
// will be orphaned.
func ( ts * TokenStore ) Revoke ( id string ) error {
2015-04-08 23:43:17 +00:00
defer metrics . MeasureSince ( [ ] string { "token" , "revoke" } , time . Now ( ) )
2015-03-18 20:21:16 +00:00
if id == "" {
return fmt . Errorf ( "cannot revoke blank token" )
}
2015-09-10 01:58:09 +00:00
2015-04-03 00:39:38 +00:00
return ts . revokeSalted ( ts . SaltID ( id ) )
2015-03-18 20:19:19 +00:00
}
// revokeSalted is used to invalidate a given salted token,
// any child tokens will be orphaned.
func ( ts * TokenStore ) revokeSalted ( saltedId string ) error {
// Lookup the token first
entry , err := ts . lookupSalted ( saltedId )
if err != nil {
return err
}
// Nuke the primary key first
path := lookupPrefix + saltedId
if ts . view . Delete ( path ) ; err != nil {
return fmt . Errorf ( "failed to delete entry: %v" , err )
}
// Clear the secondary index if any
if entry != nil && entry . Parent != "" {
2015-04-03 00:39:38 +00:00
path := parentPrefix + ts . SaltID ( entry . Parent ) + "/" + saltedId
2015-03-18 20:19:19 +00:00
if ts . view . Delete ( path ) ; err != nil {
return fmt . Errorf ( "failed to delete entry: %v" , err )
}
}
2015-04-10 22:12:04 +00:00
// Revoke all secrets under this token
if entry != nil {
if err := ts . expiration . RevokeByToken ( entry . ID ) ; err != nil {
return err
}
}
2015-09-10 01:58:09 +00:00
// Destroy the cubby space
2015-09-15 15:28:07 +00:00
err = ts . destroyCubbyhole ( saltedId )
2015-09-10 01:58:09 +00:00
if err != nil {
return err
}
2015-03-18 20:19:19 +00:00
return nil
}
// RevokeTree is used to invalide a given token and all
// child tokens.
func ( ts * TokenStore ) RevokeTree ( id string ) error {
2015-04-08 23:43:17 +00:00
defer metrics . MeasureSince ( [ ] string { "token" , "revoke-tree" } , time . Now ( ) )
2015-03-18 20:21:16 +00:00
// Verify the token is not blank
if id == "" {
return fmt . Errorf ( "cannot revoke blank token" )
}
2015-03-18 20:19:19 +00:00
// Get the salted ID
2015-04-03 00:39:38 +00:00
saltedId := ts . SaltID ( id )
2015-03-18 20:19:19 +00:00
2015-04-10 22:06:54 +00:00
// Nuke the entire tree recursively
2015-03-18 20:19:19 +00:00
if err := ts . revokeTreeSalted ( saltedId ) ; err != nil {
return err
}
return nil
}
// revokeTreeSalted is used to invalide a given token and all
// child tokens using a saltedID.
func ( ts * TokenStore ) revokeTreeSalted ( saltedId string ) error {
// Scan for child tokens
path := parentPrefix + saltedId + "/"
children , err := ts . view . List ( path )
if err != nil {
return fmt . Errorf ( "failed to scan for children: %v" , err )
}
// Recursively nuke the children. The subtle nuance here is that
// we don't have the acutal ID of the child, but we have the salted
// value. Turns out, this is good enough!
for _ , child := range children {
if err := ts . revokeTreeSalted ( child ) ; err != nil {
2015-04-10 22:06:54 +00:00
return err
2015-03-18 20:19:19 +00:00
}
}
2015-04-10 22:06:54 +00:00
// Revoke this entry
if err := ts . revokeSalted ( saltedId ) ; err != nil {
return fmt . Errorf ( "failed to revoke entry: %v" , err )
2015-03-18 20:19:19 +00:00
}
return nil
}
2016-02-29 18:27:31 +00:00
// handleCreateRole handles the auth/token/create path for a role
func ( ts * TokenStore ) handleCreateRole (
req * logical . Request , d * framework . FieldData ) ( * logical . Response , error ) {
name := d . Get ( "role_name" ) . ( string )
roleEntry , err := ts . getTokenStoreRole ( req . Storage , name )
if err != nil {
return nil , err
}
if roleEntry == nil {
return logical . ErrorResponse ( fmt . Sprintf ( "unknown role %s" , name ) ) , nil
}
return ts . handleCreateCommon ( req , d , false , roleEntry )
}
2015-11-03 20:10:46 +00:00
// handleCreate handles the auth/token/create path for creation of new orphan
// tokens
func ( ts * TokenStore ) handleCreateOrphan (
req * logical . Request , d * framework . FieldData ) ( * logical . Response , error ) {
2016-02-29 18:27:31 +00:00
return ts . handleCreateCommon ( req , d , true , nil )
2015-11-03 20:10:46 +00:00
}
// handleCreate handles the auth/token/create path for creation of new non-orphan
// tokens
2015-03-31 19:48:19 +00:00
func ( ts * TokenStore ) handleCreate (
2015-04-07 21:16:35 +00:00
req * logical . Request , d * framework . FieldData ) ( * logical . Response , error ) {
2016-02-29 18:27:31 +00:00
return ts . handleCreateCommon ( req , d , false , nil )
2015-11-03 20:10:46 +00:00
}
// handleCreateCommon handles the auth/token/create path for creation of new tokens
func ( ts * TokenStore ) handleCreateCommon (
2016-02-29 18:27:31 +00:00
req * logical . Request , d * framework . FieldData , orphan bool , role * tsRoleEntry ) ( * logical . Response , error ) {
2015-03-24 22:10:46 +00:00
// Read the parent policy
parent , err := ts . Lookup ( req . ClientToken )
if err != nil || parent == nil {
return logical . ErrorResponse ( "parent token lookup failed" ) , logical . ErrInvalidRequest
}
2015-04-17 18:34:25 +00:00
// A token with a restricted number of uses cannot create a new token
// otherwise it could escape the restriction count.
if parent . NumUses > 0 {
return logical . ErrorResponse ( "restricted use token cannot generate child tokens" ) ,
logical . ErrInvalidRequest
}
2015-09-21 14:04:03 +00:00
// Check if the client token has sudo/root privileges for the requested path
isSudo := ts . System ( ) . SudoPrivilege ( req . MountPoint + req . Path , req . ClientToken )
2015-03-24 22:10:46 +00:00
// Read and parse the fields
2015-04-07 21:16:35 +00:00
var data struct {
2015-11-09 22:30:50 +00:00
ID string
Policies [ ] string
Metadata map [ string ] string ` mapstructure:"meta" `
NoParent bool ` mapstructure:"no_parent" `
NoDefaultPolicy bool ` mapstructure:"no_default_policy" `
Lease string
TTL string
DisplayName string ` mapstructure:"display_name" `
NumUses int ` mapstructure:"num_uses" `
2015-04-07 21:16:35 +00:00
}
if err := mapstructure . WeakDecode ( req . Data , & data ) ; err != nil {
return logical . ErrorResponse ( fmt . Sprintf (
"Error decoding request: %s" , err ) ) , logical . ErrInvalidRequest
}
2015-03-24 22:10:46 +00:00
2015-04-17 18:34:25 +00:00
// Verify the number of uses is positive
if data . NumUses < 0 {
return logical . ErrorResponse ( "number of uses cannot be negative" ) ,
logical . ErrInvalidRequest
}
2015-03-24 22:10:46 +00:00
// Setup the token entry
te := TokenEntry {
2015-09-18 20:33:52 +00:00
Parent : req . ClientToken ,
Path : "auth/token/create" ,
Meta : data . Metadata ,
DisplayName : "token" ,
NumUses : data . NumUses ,
CreationTime : time . Now ( ) . Unix ( ) ,
2015-04-15 21:24:07 +00:00
}
2016-02-29 18:27:31 +00:00
if role != nil {
te . Role = role . Name
te . Path = fmt . Sprintf ( "%s/%s" , te . Path , role . Name )
if role . Prefix != "" {
te . Path = fmt . Sprintf ( "%s/%s" , te . Path , role . Prefix )
}
}
2015-04-15 21:24:07 +00:00
// Attach the given display name if any
if data . DisplayName != "" {
full := "token-" + data . DisplayName
full = displayNameSanitize . ReplaceAllString ( full , "-" )
full = strings . TrimSuffix ( full , "-" )
te . DisplayName = full
2015-03-24 22:10:46 +00:00
}
2015-09-18 23:59:06 +00:00
// Allow specifying the ID of the token if the client has root or sudo privileges
2015-04-07 21:16:35 +00:00
if data . ID != "" {
2015-09-18 23:59:06 +00:00
if ! isSudo {
return logical . ErrorResponse ( "root or sudo privileges required to specify token id" ) ,
2015-03-24 22:10:46 +00:00
logical . ErrInvalidRequest
}
2015-04-07 21:16:35 +00:00
te . ID = data . ID
2015-03-24 22:10:46 +00:00
}
2016-02-29 18:27:31 +00:00
switch {
// If we have a role, we don't even consider parent policies; the role
// allowed policies trumps all
case role != nil :
if ! strListSubset ( role . AllowedPolicies , data . Policies ) {
return logical . ErrorResponse ( "token policies must be subset of the role's allowed policies" ) , logical . ErrInvalidRequest
}
case len ( data . Policies ) == 0 :
2015-04-07 21:19:52 +00:00
data . Policies = parent . Policies
2016-02-29 18:27:31 +00:00
// When a role is not in use, only permit policies to be a subset unless
// the client has root or sudo privileges
case ! isSudo && ! strListSubset ( parent . Policies , data . Policies ) :
2015-04-07 21:19:52 +00:00
return logical . ErrorResponse ( "child policies must be subset of parent" ) , logical . ErrInvalidRequest
2015-03-24 22:10:46 +00:00
}
2015-12-30 20:18:30 +00:00
// Use a map to filter out/prevent duplicates
policyMap := map [ string ] bool { }
for _ , policy := range data . Policies {
2016-01-09 02:23:27 +00:00
if policy == "" {
// Don't allow a policy with no name, even though it is a valid
// slice member
continue
}
2015-12-30 20:18:30 +00:00
policyMap [ policy ] = true
}
if ! policyMap [ "root" ] &&
! data . NoDefaultPolicy {
policyMap [ "default" ] = true
}
for k , _ := range policyMap {
te . Policies = append ( te . Policies , k )
2015-11-06 22:27:15 +00:00
}
2015-12-30 20:18:30 +00:00
sort . Strings ( te . Policies )
2015-03-24 22:10:46 +00:00
2016-02-29 18:27:31 +00:00
switch {
case role != nil :
if role . Orphan {
te . Parent = ""
}
case data . NoParent :
// Only allow an orphan token if the client has sudo policy
2015-09-18 23:59:06 +00:00
if ! isSudo {
return logical . ErrorResponse ( "root or sudo privileges required to create orphan token" ) ,
2015-03-24 22:10:46 +00:00
logical . ErrInvalidRequest
}
2015-04-07 21:16:35 +00:00
te . Parent = ""
2016-02-29 18:27:31 +00:00
default :
2015-11-03 20:10:46 +00:00
// This comes from create-orphan, which can be properly ACLd
if orphan {
te . Parent = ""
}
2015-03-24 22:10:46 +00:00
}
2016-02-29 18:27:31 +00:00
if role != nil && role . Period > 0 {
te . TTL = role . Period
} else {
// Parse the TTL/lease if any
if data . TTL != "" {
dur , err := time . ParseDuration ( data . TTL )
if err != nil {
return logical . ErrorResponse ( err . Error ( ) ) , logical . ErrInvalidRequest
}
if dur < 0 {
return logical . ErrorResponse ( "ttl must be positive" ) , logical . ErrInvalidRequest
}
te . TTL = dur
} else if data . Lease != "" {
dur , err := time . ParseDuration ( data . Lease )
if err != nil {
return logical . ErrorResponse ( err . Error ( ) ) , logical . ErrInvalidRequest
}
if dur < 0 {
return logical . ErrorResponse ( "lease must be positive" ) , logical . ErrInvalidRequest
}
te . TTL = dur
2015-03-24 22:10:46 +00:00
}
2015-09-18 20:33:52 +00:00
2016-02-29 18:27:31 +00:00
sysView := ts . System ( )
2015-09-18 20:33:52 +00:00
2016-02-29 18:27:31 +00:00
// Set the default lease if non-provided, root tokens are exempt
if te . TTL == 0 && ! strListContains ( te . Policies , "root" ) {
te . TTL = sysView . DefaultLeaseTTL ( )
}
2015-09-18 20:33:52 +00:00
2016-02-29 18:27:31 +00:00
// Limit the lease duration
if te . TTL > sysView . MaxLeaseTTL ( ) {
te . TTL = sysView . MaxLeaseTTL ( )
}
2015-03-24 22:10:46 +00:00
}
// Create the token
2015-10-30 14:59:26 +00:00
if err := ts . create ( & te ) ; err != nil {
2015-03-24 22:10:46 +00:00
return logical . ErrorResponse ( err . Error ( ) ) , logical . ErrInvalidRequest
}
// Generate the response
resp := & logical . Response {
2015-03-31 03:26:39 +00:00
Auth : & logical . Auth {
2015-04-26 03:21:35 +00:00
DisplayName : te . DisplayName ,
Policies : te . Policies ,
Metadata : te . Meta ,
2015-04-09 19:14:04 +00:00
LeaseOptions : logical . LeaseOptions {
2016-01-29 22:44:09 +00:00
TTL : te . TTL ,
Renewable : true ,
2015-04-09 19:14:04 +00:00
} ,
ClientToken : te . ID ,
2016-02-29 18:27:31 +00:00
InternalData : map [ string ] interface { } {
"id" : te . ID ,
} ,
2015-03-24 22:10:46 +00:00
} ,
}
2015-03-31 03:26:39 +00:00
2015-10-07 19:30:54 +00:00
if ts . policyLookupFunc != nil {
2015-11-06 16:36:40 +00:00
for _ , p := range te . Policies {
policy , err := ts . policyLookupFunc ( p )
if err != nil {
return logical . ErrorResponse ( fmt . Sprintf ( "could not look up policy %s" , p ) ) , nil
2015-10-07 19:30:54 +00:00
}
2015-11-06 16:36:40 +00:00
if policy == nil {
resp . AddWarning ( fmt . Sprintf ( "policy \"%s\" does not exist" , p ) )
2015-10-07 19:30:54 +00:00
}
}
}
2015-03-24 22:10:46 +00:00
return resp , nil
}
2015-09-17 17:22:30 +00:00
// handleRevokeSelf handles the auth/token/revoke-self path for revocation of tokens
// 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.
func ( ts * TokenStore ) handleRevokeSelf (
req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
// Revoke the token and its children
if err := ts . RevokeTree ( req . ClientToken ) ; err != nil {
return logical . ErrorResponse ( err . Error ( ) ) , logical . ErrInvalidRequest
}
return nil , nil
}
2015-03-24 22:30:09 +00:00
// handleRevokeTree handles the auth/token/revoke/id path for revocation of tokens
2015-04-08 20:35:32 +00:00
// in a way that revokes all child tokens. Normally, using sys/revoke/leaseID will revoke
2015-03-24 22:30:09 +00:00
// the token and all children anyways, but that is only available when there is a lease.
2015-03-31 19:48:19 +00:00
func ( ts * TokenStore ) handleRevokeTree (
req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
id := data . Get ( "token" ) . ( string )
2015-03-24 22:30:09 +00:00
if id == "" {
return logical . ErrorResponse ( "missing token ID" ) , logical . ErrInvalidRequest
}
// Revoke the token and its children
if err := ts . RevokeTree ( id ) ; err != nil {
return logical . ErrorResponse ( err . Error ( ) ) , logical . ErrInvalidRequest
}
return nil , nil
}
2015-03-24 22:10:46 +00:00
// handleRevokeOrphan handles the auth/token/revoke-orphan/id path for revocation of tokens
2015-04-08 20:35:32 +00:00
// in a way that leaves child tokens orphaned. Normally, using sys/revoke/leaseID will revoke
2015-03-24 22:10:46 +00:00
// the token and all children.
2015-03-31 19:48:19 +00:00
func ( ts * TokenStore ) handleRevokeOrphan (
req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2015-03-24 22:30:09 +00:00
// Parse the id
2015-03-31 19:48:19 +00:00
id := data . Get ( "token" ) . ( string )
2015-03-24 22:30:09 +00:00
if id == "" {
return logical . ErrorResponse ( "missing token ID" ) , logical . ErrInvalidRequest
}
2015-09-16 13:22:15 +00:00
parent , err := ts . Lookup ( req . ClientToken )
if err != nil {
return logical . ErrorResponse ( fmt . Sprintf ( "parent token lookup failed: %s" , err . Error ( ) ) ) , logical . ErrInvalidRequest
}
if parent == nil {
return logical . ErrorResponse ( "parent token lookup failed" ) , logical . ErrInvalidRequest
}
2015-09-21 14:04:03 +00:00
// Check if the client token has sudo/root privileges for the requested path
isSudo := ts . System ( ) . SudoPrivilege ( req . MountPoint + req . Path , req . ClientToken )
2015-09-16 13:22:15 +00:00
2015-09-18 23:59:06 +00:00
if ! isSudo {
return logical . ErrorResponse ( "root or sudo privileges required to revoke and orphan" ) ,
2015-09-16 13:22:15 +00:00
logical . ErrInvalidRequest
}
2015-03-24 22:30:09 +00:00
// Revoke and orphan
if err := ts . Revoke ( id ) ; err != nil {
return logical . ErrorResponse ( err . Error ( ) ) , logical . ErrInvalidRequest
}
2015-03-20 20:54:57 +00:00
return nil , nil
}
2015-03-24 22:10:46 +00:00
2015-04-03 18:40:08 +00:00
// handleRevokePrefix handles the auth/token/revoke-prefix/path for revocation of tokens
// generated by a given path.
func ( ts * TokenStore ) handleRevokePrefix (
req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
// Parse the prefix
prefix := data . Get ( "prefix" ) . ( string )
if prefix == "" {
return logical . ErrorResponse ( "missing source prefix" ) , logical . ErrInvalidRequest
}
// Revoke using the prefix
if err := ts . expiration . RevokePrefix ( prefix ) ; err != nil {
return logical . ErrorResponse ( err . Error ( ) ) , logical . ErrInvalidRequest
}
return nil , nil
}
2015-03-24 22:39:33 +00:00
// handleLookup handles the auth/token/lookup/id path for querying information about
// a particular token. This can be used to see which policies are applicable.
2015-03-31 19:48:19 +00:00
func ( ts * TokenStore ) handleLookup (
req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
id := data . Get ( "token" ) . ( string )
2015-03-31 19:50:07 +00:00
if id == "" {
id = req . ClientToken
}
2015-03-24 22:39:33 +00:00
if id == "" {
return logical . ErrorResponse ( "missing token ID" ) , logical . ErrInvalidRequest
}
// Lookup the token
out , err := ts . Lookup ( id )
2015-06-19 01:30:18 +00:00
2015-03-24 22:39:33 +00:00
if err != nil {
return logical . ErrorResponse ( err . Error ( ) ) , logical . ErrInvalidRequest
}
if out == nil {
2015-06-19 01:30:18 +00:00
return logical . ErrorResponse ( "bad token" ) , logical . ErrPermissionDenied
2015-03-24 22:39:33 +00:00
}
// Generate a response. We purposely omit the parent reference otherwise
2015-10-02 17:33:19 +00:00
// you could escalate your privileges.
2015-03-24 22:39:33 +00:00
resp := & logical . Response {
Data : map [ string ] interface { } {
2016-01-04 21:43:07 +00:00
"id" : out . ID ,
"policies" : out . Policies ,
"path" : out . Path ,
"meta" : out . Meta ,
"display_name" : out . DisplayName ,
"num_uses" : out . NumUses ,
"orphan" : false ,
2016-01-04 22:11:22 +00:00
"creation_time" : int64 ( out . CreationTime ) ,
2016-02-01 16:16:32 +00:00
"creation_ttl" : int64 ( out . TTL . Seconds ( ) ) ,
"ttl" : int64 ( 0 ) ,
2016-02-29 18:27:31 +00:00
"role" : out . Role ,
2015-03-24 22:39:33 +00:00
} ,
}
2015-11-09 18:19:59 +00:00
if out . Parent == "" {
resp . Data [ "orphan" ] = true
}
2016-01-04 21:43:07 +00:00
// Fetch the last renewal time
leaseTimes , err := ts . expiration . FetchLeaseTimesByToken ( out . Path , out . ID )
if err != nil {
return logical . ErrorResponse ( err . Error ( ) ) , logical . ErrInvalidRequest
}
2016-02-01 16:16:32 +00:00
if leaseTimes != nil {
if ! leaseTimes . LastRenewalTime . IsZero ( ) {
resp . Data [ "last_renewal_time" ] = leaseTimes . LastRenewalTime . Unix ( )
}
if ! leaseTimes . ExpireTime . IsZero ( ) {
resp . Data [ "ttl" ] = int64 ( leaseTimes . ExpireTime . Sub ( time . Now ( ) . Round ( time . Second ) ) . Seconds ( ) )
}
2016-01-04 21:43:07 +00:00
}
2015-03-24 22:39:33 +00:00
return resp , nil
}
2015-10-07 16:49:13 +00:00
func ( ts * TokenStore ) handleRenewSelf (
req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
data . Raw [ "token" ] = req . ClientToken
return ts . handleRenew ( req , data )
}
2015-04-03 19:11:49 +00:00
// handleRenew handles the auth/token/renew/id path for renewal of tokens.
// This is used to prevent token expiration and revocation.
func ( ts * TokenStore ) handleRenew (
req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
id := data . Get ( "token" ) . ( string )
if id == "" {
return logical . ErrorResponse ( "missing token ID" ) , logical . ErrInvalidRequest
}
2015-04-09 21:23:37 +00:00
incrementRaw := data . Get ( "increment" ) . ( int )
// Convert the increment
increment := time . Duration ( incrementRaw ) * time . Second
2015-04-03 19:11:49 +00:00
// Lookup the token
2015-12-30 19:30:02 +00:00
te , err := ts . Lookup ( id )
2015-04-03 19:11:49 +00:00
if err != nil {
return logical . ErrorResponse ( err . Error ( ) ) , logical . ErrInvalidRequest
}
// Verify the token exists
2015-12-30 19:30:02 +00:00
if te == nil {
2015-04-03 19:11:49 +00:00
return logical . ErrorResponse ( "token not found" ) , logical . ErrInvalidRequest
}
2015-10-09 21:11:31 +00:00
// Renew the token and its children
2016-02-18 16:22:04 +00:00
auth , err := ts . expiration . RenewToken ( req , te . Path , te . ID , increment )
2015-04-06 23:35:39 +00:00
if err != nil {
2015-04-03 19:11:49 +00:00
return logical . ErrorResponse ( err . Error ( ) ) , logical . ErrInvalidRequest
}
2015-04-06 23:35:39 +00:00
// Generate the response
resp := & logical . Response {
Auth : auth ,
}
return resp , nil
2015-04-03 19:11:49 +00:00
}
2015-09-15 15:28:07 +00:00
func ( ts * TokenStore ) destroyCubbyhole ( saltedID string ) error {
2015-09-15 17:49:53 +00:00
if ts . cubbyholeBackend == nil {
2015-09-15 15:28:07 +00:00
// Should only ever happen in testing
return nil
}
2015-09-15 17:49:53 +00:00
return ts . cubbyholeBackend . revoke ( salt . SaltID ( ts . cubbyholeBackend . saltUUID , saltedID , salt . SHA1Hash ) )
2015-09-15 15:28:07 +00:00
}
2016-01-29 22:44:09 +00:00
func ( ts * TokenStore ) authRenew (
req * logical . Request , d * framework . FieldData ) ( * logical . Response , error ) {
2016-02-29 18:27:31 +00:00
if req . Auth == nil {
return nil , fmt . Errorf ( "request auth is nil" )
}
2016-01-29 22:44:09 +00:00
f := framework . LeaseExtend ( 0 , 0 , ts . System ( ) )
2016-02-29 18:27:31 +00:00
idInt , ok := req . Auth . InternalData [ "id" ]
if ! ok {
// Fall back here; this is pre-roles so there are no stored IDs, so use previous behavior
return f ( req , d )
}
id , ok := idInt . ( string )
if ! ok {
return nil , fmt . Errorf ( "found id in internal data but could not interpret as string" )
}
te , err := ts . Lookup ( id )
if err != nil {
return nil , fmt . Errorf ( "error looking up token: %s" , err )
}
if te == nil {
return nil , fmt . Errorf ( "no token entry found during lookup" )
}
// No role? Use normal LeaseExtend semantics
if te . Role == "" {
return f ( req , d )
}
role , err := ts . getTokenStoreRole ( req . Storage , te . Role )
if err != nil {
return nil , fmt . Errorf ( "error looking up role %s: %s" , te . Role , err )
}
if role == nil {
resp , err := f ( req , d )
if resp != nil {
resp . AddWarning ( fmt . Sprintf ( "The token was created via role %s, but that role could no longer be found. Renewal fell back to normal token renewal semantics; if this token was a periodic token, this could mean that it can no longer be renewed." , te . Role ) )
}
return resp , err
}
if role . Period != 0 {
req . Auth . TTL = role . Period
return & logical . Response { Auth : req . Auth } , nil
}
2016-01-29 22:44:09 +00:00
return f ( req , d )
}
2016-02-29 18:27:31 +00:00
func ( ts * TokenStore ) getTokenStoreRole ( s logical . Storage , n string ) ( * tsRoleEntry , error ) {
2016-02-29 19:13:09 +00:00
entry , err := s . Get ( "roles/" + n )
2016-02-29 18:27:31 +00:00
if err != nil {
return nil , err
}
if entry == nil {
return nil , nil
}
var result tsRoleEntry
if err := entry . DecodeJSON ( & result ) ; err != nil {
return nil , err
}
return & result , nil
}
func ( ts * TokenStore ) tokenStoreRoleList (
req * logical . Request , d * framework . FieldData ) ( * logical . Response , error ) {
2016-02-29 19:13:09 +00:00
entries , err := req . Storage . List ( "roles/" )
2016-02-29 18:27:31 +00:00
if err != nil {
return nil , err
}
2016-02-29 19:13:09 +00:00
ret := make ( [ ] string , len ( entries ) )
for i , entry := range entries {
ret [ i ] = strings . TrimPrefix ( entry , "roles/" )
}
return logical . ListResponse ( ret ) , nil
2016-02-29 18:27:31 +00:00
}
func ( ts * TokenStore ) tokenStoreRoleDelete (
req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2016-02-29 19:13:09 +00:00
err := req . Storage . Delete ( "roles/" + data . Get ( "role_name" ) . ( string ) )
2016-02-29 18:27:31 +00:00
if err != nil {
return nil , err
}
return nil , nil
}
func ( ts * TokenStore ) tokenStoreRoleRead (
req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2016-02-29 19:13:09 +00:00
role , err := ts . getTokenStoreRole ( req . Storage , data . Get ( "role_name" ) . ( string ) )
2016-02-29 18:27:31 +00:00
if err != nil {
return nil , err
}
if role == nil {
return nil , nil
}
resp := & logical . Response {
Data : structs . New ( role ) . Map ( ) ,
}
return resp , nil
}
func ( ts * TokenStore ) tokenStoreRoleCreate (
req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
name := data . Get ( "role_name" ) . ( string )
if name == "" {
return logical . ErrorResponse ( "role name cannot be empty" ) , nil
}
prefix := data . Get ( "prefix" ) . ( string )
if prefix != "" {
matched := prefixSanitize . MatchString ( prefix )
if ! matched {
return logical . ErrorResponse ( fmt . Sprintf ( "given role prefix contains invalid characters; must match %s" , prefixSanitize . String ( ) ) ) , nil
}
}
entry := & tsRoleEntry {
Name : name ,
Orphan : data . Get ( "orphan" ) . ( bool ) ,
Period : time . Second * time . Duration ( data . Get ( "period" ) . ( int ) ) ,
Prefix : prefix ,
}
allowedPolicies := data . Get ( "allowed_policies" ) . ( string )
if allowedPolicies != "" {
entry . AllowedPolicies = strings . Split ( allowedPolicies , "," )
}
// Store it
2016-02-29 19:13:09 +00:00
jsonEntry , err := logical . StorageEntryJSON ( "roles/" + name , entry )
2016-02-29 18:27:31 +00:00
if err != nil {
return nil , err
}
if err := req . Storage . Put ( jsonEntry ) ; err != nil {
return nil , err
}
return nil , nil
}
2015-03-24 22:10:46 +00:00
const (
tokenBackendHelp = ` The token credential backend is always enabled and builtin to Vault .
Client tokens are used to identify a client and to allow Vault to associate policies and ACLs
which are enforced on every request . This backend also allows for generating sub - tokens as well
2015-09-12 01:08:32 +00:00
as revocation of tokens . The tokens are renewable if associated with a lease . `
2015-03-24 22:30:09 +00:00
tokenCreateHelp = ` The token create path is used to create new tokens. `
2015-11-03 20:10:46 +00:00
tokenCreateOrphanHelp = ` The token create path is used to create new orphan tokens. `
2015-03-24 22:39:33 +00:00
tokenLookupHelp = ` This endpoint will lookup a token and its properties. `
2015-09-17 17:22:30 +00:00
tokenRevokeHelp = ` This endpoint will delete the given token and all of its child tokens. `
tokenRevokeSelfHelp = ` This endpoint will delete the token used to call it and all of its child tokens. `
2015-03-24 22:30:09 +00:00
tokenRevokeOrphanHelp = ` This endpoint will delete the token and orphan its child tokens. `
2015-04-03 18:40:08 +00:00
tokenRevokePrefixHelp = ` This endpoint will delete all tokens generated under a prefix with their child tokens. `
2015-10-07 16:49:13 +00:00
tokenRenewHelp = ` This endpoint will renew the given token and prevent expiration. `
tokenRenewSelfHelp = ` This endpoint will renew the token used to call it and prevent expiration. `
2015-03-24 22:10:46 +00:00
)