2023-03-15 16:00:52 +00:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
2019-06-14 14:17:04 +00:00
package tokenutil
import (
"errors"
"fmt"
"time"
2021-07-16 00:17:31 +00:00
"github.com/hashicorp/go-secure-stdlib/parseutil"
"github.com/hashicorp/go-secure-stdlib/strutil"
2019-06-14 14:17:04 +00:00
sockaddr "github.com/hashicorp/go-sockaddr"
"github.com/hashicorp/vault/sdk/framework"
2019-07-02 13:52:05 +00:00
"github.com/hashicorp/vault/sdk/helper/policyutil"
2019-06-14 14:17:04 +00:00
"github.com/hashicorp/vault/sdk/logical"
)
// TokenParams contains a set of common parameters that auth plugins can use
// for setting token behavior
type TokenParams struct {
// The set of CIDRs that tokens generated using this role will be bound to
TokenBoundCIDRs [ ] * sockaddr . SockAddrMarshaler ` json:"token_bound_cidrs" `
// If set, the token entry will have an explicit maximum TTL set, rather
// than deferring to role/mount values
TokenExplicitMaxTTL time . Duration ` json:"token_explicit_max_ttl" mapstructure:"token_explicit_max_ttl" `
// The max TTL to use for the token
TokenMaxTTL time . Duration ` json:"token_max_ttl" mapstructure:"token_max_ttl" `
// If set, core will not automatically add default to the policy list
TokenNoDefaultPolicy bool ` json:"token_no_default_policy" mapstructure:"token_no_default_policy" `
// The maximum number of times a token issued from this role may be used.
TokenNumUses int ` json:"token_num_uses" mapstructure:"token_num_uses" `
// 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
TokenPeriod time . Duration ` json:"token_period" mapstructure:"token_period" `
// The policies to set
TokenPolicies [ ] string ` json:"token_policies" mapstructure:"token_policies" `
// The type of token this role should issue
TokenType logical . TokenType ` json:"token_type" mapstructure:"token_type" `
// The TTL to user for the token
TokenTTL time . Duration ` json:"token_ttl" mapstructure:"token_ttl" `
}
// AddTokenFields adds fields to an existing role. It panics if it would
// overwrite an existing field.
func AddTokenFields ( m map [ string ] * framework . FieldSchema ) {
AddTokenFieldsWithAllowList ( m , nil )
}
// AddTokenFields adds fields to an existing role. It panics if it would
// overwrite an existing field. Allowed can be use to restrict the set, e.g. if
// there would be conflicts.
func AddTokenFieldsWithAllowList ( m map [ string ] * framework . FieldSchema , allowed [ ] string ) {
r := TokenFields ( )
for k , v := range r {
if len ( allowed ) > 0 && ! strutil . StrListContains ( allowed , k ) {
continue
}
if _ , has := m [ k ] ; has {
panic ( fmt . Sprintf ( "adding role field %s would overwrite existing field" , k ) )
}
m [ k ] = v
}
}
// TokenFields provides a set of field schemas for the parameters
func TokenFields ( ) map [ string ] * framework . FieldSchema {
return map [ string ] * framework . FieldSchema {
2021-04-08 16:43:39 +00:00
"token_bound_cidrs" : {
2019-06-14 14:17:04 +00:00
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. ` ,
2019-07-01 12:57:57 +00:00
DisplayAttrs : & framework . DisplayAttributes {
2019-11-07 16:54:34 +00:00
Name : "Generated Token's Bound CIDRs" ,
2019-10-17 23:19:14 +00:00
Group : "Tokens" ,
2019-07-01 12:57:57 +00:00
} ,
2019-06-14 14:17:04 +00:00
} ,
2021-04-08 16:43:39 +00:00
"token_explicit_max_ttl" : {
2019-06-14 14:17:04 +00:00
Type : framework . TypeDurationSecond ,
Description : tokenExplicitMaxTTLHelp ,
2019-07-01 12:57:57 +00:00
DisplayAttrs : & framework . DisplayAttributes {
2019-11-07 16:54:34 +00:00
Name : "Generated Token's Explicit Maximum TTL" ,
2019-10-17 23:19:14 +00:00
Group : "Tokens" ,
2019-07-01 12:57:57 +00:00
} ,
2019-06-14 14:17:04 +00:00
} ,
2021-04-08 16:43:39 +00:00
"token_max_ttl" : {
2019-06-14 14:17:04 +00:00
Type : framework . TypeDurationSecond ,
Description : "The maximum lifetime of the generated token" ,
2019-07-01 12:57:57 +00:00
DisplayAttrs : & framework . DisplayAttributes {
2019-11-07 16:54:34 +00:00
Name : "Generated Token's Maximum TTL" ,
2019-10-17 23:19:14 +00:00
Group : "Tokens" ,
2019-07-01 12:57:57 +00:00
} ,
2019-06-14 14:17:04 +00:00
} ,
2021-04-08 16:43:39 +00:00
"token_no_default_policy" : {
2019-06-14 14:17:04 +00:00
Type : framework . TypeBool ,
Description : "If true, the 'default' policy will not automatically be added to generated tokens" ,
2019-07-01 12:57:57 +00:00
DisplayAttrs : & framework . DisplayAttributes {
2019-11-07 16:54:34 +00:00
Name : "Do Not Attach 'default' Policy To Generated Tokens" ,
2019-10-17 23:19:14 +00:00
Group : "Tokens" ,
2019-07-01 12:57:57 +00:00
} ,
2019-06-14 14:17:04 +00:00
} ,
2021-04-08 16:43:39 +00:00
"token_period" : {
2019-06-14 14:17:04 +00:00
Type : framework . TypeDurationSecond ,
Description : tokenPeriodHelp ,
2019-07-01 12:57:57 +00:00
DisplayAttrs : & framework . DisplayAttributes {
2019-11-07 16:54:34 +00:00
Name : "Generated Token's Period" ,
2019-10-17 23:19:14 +00:00
Group : "Tokens" ,
2019-07-01 12:57:57 +00:00
} ,
2019-06-14 14:17:04 +00:00
} ,
2021-04-08 16:43:39 +00:00
"token_policies" : {
2019-06-14 14:17:04 +00:00
Type : framework . TypeCommaStringSlice ,
Description : "Comma-separated list of policies" ,
2019-07-01 12:57:57 +00:00
DisplayAttrs : & framework . DisplayAttributes {
2019-11-07 16:54:34 +00:00
Name : "Generated Token's Policies" ,
2019-10-17 23:19:14 +00:00
Group : "Tokens" ,
2019-07-01 12:57:57 +00:00
} ,
2019-06-14 14:17:04 +00:00
} ,
2021-04-08 16:43:39 +00:00
"token_type" : {
2019-06-14 14:17:04 +00:00
Type : framework . TypeString ,
Default : "default-service" ,
Description : "The type of token to generate, service or batch" ,
2019-07-01 12:57:57 +00:00
DisplayAttrs : & framework . DisplayAttributes {
2019-11-07 16:54:34 +00:00
Name : "Generated Token's Type" ,
2019-10-17 23:19:14 +00:00
Group : "Tokens" ,
2019-07-01 12:57:57 +00:00
} ,
2019-06-14 14:17:04 +00:00
} ,
2021-04-08 16:43:39 +00:00
"token_ttl" : {
2019-06-14 14:17:04 +00:00
Type : framework . TypeDurationSecond ,
Description : "The initial ttl of the token to generate" ,
2019-07-01 12:57:57 +00:00
DisplayAttrs : & framework . DisplayAttributes {
2019-11-07 16:54:34 +00:00
Name : "Generated Token's Initial TTL" ,
2019-10-17 23:19:14 +00:00
Group : "Tokens" ,
2019-07-01 12:57:57 +00:00
} ,
2019-06-14 14:17:04 +00:00
} ,
2021-04-08 16:43:39 +00:00
"token_num_uses" : {
2019-06-14 14:17:04 +00:00
Type : framework . TypeInt ,
Description : "The maximum number of times a token may be used, a value of zero means unlimited" ,
2019-07-01 12:57:57 +00:00
DisplayAttrs : & framework . DisplayAttributes {
2019-11-07 16:54:34 +00:00
Name : "Maximum Uses of Generated Tokens" ,
2019-10-17 23:19:14 +00:00
Group : "Tokens" ,
2019-07-01 12:57:57 +00:00
} ,
2019-06-14 14:17:04 +00:00
} ,
}
}
// ParseTokenFields provides common field parsing functionality into a TokenFields struct
func ( t * TokenParams ) ParseTokenFields ( req * logical . Request , d * framework . FieldData ) error {
if boundCIDRsRaw , ok := d . GetOk ( "token_bound_cidrs" ) ; ok {
boundCIDRs , err := parseutil . ParseAddrs ( boundCIDRsRaw . ( [ ] string ) )
if err != nil {
return err
}
t . TokenBoundCIDRs = boundCIDRs
}
if explicitMaxTTLRaw , ok := d . GetOk ( "token_explicit_max_ttl" ) ; ok {
t . TokenExplicitMaxTTL = time . Duration ( explicitMaxTTLRaw . ( int ) ) * time . Second
}
if maxTTLRaw , ok := d . GetOk ( "token_max_ttl" ) ; ok {
t . TokenMaxTTL = time . Duration ( maxTTLRaw . ( int ) ) * time . Second
}
if t . TokenMaxTTL < 0 {
return errors . New ( "'token_max_ttl' cannot be negative" )
}
if noDefaultRaw , ok := d . GetOk ( "token_no_default_policy" ) ; ok {
t . TokenNoDefaultPolicy = noDefaultRaw . ( bool )
}
if periodRaw , ok := d . GetOk ( "token_period" ) ; ok {
t . TokenPeriod = time . Duration ( periodRaw . ( int ) ) * time . Second
}
if t . TokenPeriod < 0 {
return errors . New ( "'token_period' cannot be negative" )
}
if policiesRaw , ok := d . GetOk ( "token_policies" ) ; ok {
t . TokenPolicies = policiesRaw . ( [ ] string )
}
if tokenTypeRaw , ok := d . GetOk ( "token_type" ) ; ok {
var tokenType logical . TokenType
tokenTypeStr := tokenTypeRaw . ( string )
switch tokenTypeStr {
2019-07-03 02:16:43 +00:00
case "" , "default" :
tokenType = logical . TokenTypeDefault
2019-06-14 14:17:04 +00:00
case "service" :
tokenType = logical . TokenTypeService
case "batch" :
tokenType = logical . TokenTypeBatch
default :
return fmt . Errorf ( "invalid 'token_type' value %q" , tokenTypeStr )
}
t . TokenType = tokenType
}
2021-05-19 18:06:08 +00:00
if tokenNumUses , ok := d . GetOk ( "token_num_uses" ) ; ok {
t . TokenNumUses = tokenNumUses . ( int )
}
if t . TokenNumUses < 0 {
return errors . New ( "'token_num_uses' cannot be negative" )
}
2019-06-29 18:50:34 +00:00
if t . TokenType == logical . TokenTypeBatch || t . TokenType == logical . TokenTypeDefaultBatch {
if t . TokenPeriod != 0 {
return errors . New ( "'token_type' cannot be 'batch' or 'default_batch' when set to generate periodic tokens" )
2019-06-29 18:48:17 +00:00
}
if t . TokenNumUses != 0 {
2019-06-29 18:50:34 +00:00
return errors . New ( "'token_type' cannot be 'batch' or 'default_batch' when set to generate tokens with limited use count" )
2019-06-29 18:48:17 +00:00
}
}
2019-06-14 14:17:04 +00:00
if ttlRaw , ok := d . GetOk ( "token_ttl" ) ; ok {
t . TokenTTL = time . Duration ( ttlRaw . ( int ) ) * time . Second
}
if t . TokenTTL < 0 {
return errors . New ( "'token_ttl' cannot be negative" )
}
if t . TokenTTL > 0 && t . TokenMaxTTL > 0 && t . TokenTTL > t . TokenMaxTTL {
return errors . New ( "'token_ttl' cannot be greater than 'token_max_ttl'" )
}
return nil
}
// PopulateTokenData adds information from TokenParams into the map
func ( t * TokenParams ) PopulateTokenData ( m map [ string ] interface { } ) {
m [ "token_bound_cidrs" ] = t . TokenBoundCIDRs
2019-06-18 16:22:12 +00:00
m [ "token_explicit_max_ttl" ] = int64 ( t . TokenExplicitMaxTTL . Seconds ( ) )
m [ "token_max_ttl" ] = int64 ( t . TokenMaxTTL . Seconds ( ) )
2019-06-14 14:17:04 +00:00
m [ "token_no_default_policy" ] = t . TokenNoDefaultPolicy
2019-06-18 16:22:12 +00:00
m [ "token_period" ] = int64 ( t . TokenPeriod . Seconds ( ) )
2019-06-14 14:17:04 +00:00
m [ "token_policies" ] = t . TokenPolicies
m [ "token_type" ] = t . TokenType . String ( )
2019-06-18 16:22:12 +00:00
m [ "token_ttl" ] = int64 ( t . TokenTTL . Seconds ( ) )
2019-06-14 14:17:04 +00:00
m [ "token_num_uses" ] = t . TokenNumUses
2019-06-29 20:36:21 +00:00
if len ( t . TokenPolicies ) == 0 {
m [ "token_policies" ] = [ ] string { }
}
if len ( t . TokenBoundCIDRs ) == 0 {
m [ "token_bound_cidrs" ] = [ ] string { }
}
2019-06-14 14:17:04 +00:00
}
// PopulateTokenAuth populates Auth with parameters
func ( t * TokenParams ) PopulateTokenAuth ( auth * logical . Auth ) {
auth . BoundCIDRs = t . TokenBoundCIDRs
auth . ExplicitMaxTTL = t . TokenExplicitMaxTTL
auth . MaxTTL = t . TokenMaxTTL
auth . NoDefaultPolicy = t . TokenNoDefaultPolicy
auth . Period = t . TokenPeriod
auth . Policies = t . TokenPolicies
2019-07-02 14:23:46 +00:00
auth . Renewable = true
2019-06-14 14:17:04 +00:00
auth . TokenType = t . TokenType
auth . TTL = t . TokenTTL
auth . NumUses = t . TokenNumUses
}
2019-06-27 18:52:52 +00:00
func DeprecationText ( param string ) string {
return fmt . Sprintf ( "Use %q instead. If this and %q are both specified, only %q will be used." , param , param , param )
}
2019-07-02 13:52:05 +00:00
func upgradeDurationValue ( d * framework . FieldData , oldKey , newKey string , oldVal , newVal * time . Duration ) error {
_ , ok := d . GetOk ( newKey )
if ! ok {
raw , ok := d . GetOk ( oldKey )
if ok {
* oldVal = time . Duration ( raw . ( int ) ) * time . Second
* newVal = * oldVal
}
} else {
_ , ok = d . GetOk ( oldKey )
if ok {
* oldVal = * newVal
} else {
* oldVal = 0
}
}
return nil
}
func upgradeIntValue ( d * framework . FieldData , oldKey , newKey string , oldVal , newVal * int ) error {
_ , ok := d . GetOk ( newKey )
if ! ok {
raw , ok := d . GetOk ( oldKey )
if ok {
* oldVal = raw . ( int )
* newVal = * oldVal
}
} else {
_ , ok = d . GetOk ( oldKey )
if ok {
* oldVal = * newVal
} else {
* oldVal = 0
}
}
return nil
}
func upgradeStringSliceValue ( d * framework . FieldData , oldKey , newKey string , oldVal , newVal * [ ] string ) error {
_ , ok := d . GetOk ( newKey )
if ! ok {
raw , ok := d . GetOk ( oldKey )
if ok {
// Special case: if we're looking at "token_policies" parse the policies
if newKey == "token_policies" {
* oldVal = policyutil . ParsePolicies ( raw )
} else {
* oldVal = raw . ( [ ] string )
}
* newVal = * oldVal
}
} else {
_ , ok = d . GetOk ( oldKey )
if ok {
* oldVal = * newVal
} else {
* oldVal = nil
}
}
return nil
}
func upgradeSockAddrSliceValue ( d * framework . FieldData , oldKey , newKey string , oldVal , newVal * [ ] * sockaddr . SockAddrMarshaler ) error {
_ , ok := d . GetOk ( newKey )
if ! ok {
raw , ok := d . GetOk ( oldKey )
if ok {
boundCIDRs , err := parseutil . ParseAddrs ( raw )
if err != nil {
return err
}
* oldVal = boundCIDRs
* newVal = * oldVal
}
} else {
_ , ok = d . GetOk ( oldKey )
if ok {
* oldVal = * newVal
} else {
* oldVal = nil
}
}
return nil
}
// UpgradeValue takes in old/new data keys and old/new values and calls out to
// a helper function to perform upgrades in a standardized way. It reqiures
// pointers in all cases so that we can set directly into the target struct.
func UpgradeValue ( d * framework . FieldData , oldKey , newKey string , oldVal , newVal interface { } ) error {
switch typedOldVal := oldVal . ( type ) {
case * time . Duration :
typedNewVal , ok := newVal . ( * time . Duration )
if ! ok {
return errors . New ( "mismatch in value types in tokenutil.UpgradeValue" )
}
return upgradeDurationValue ( d , oldKey , newKey , typedOldVal , typedNewVal )
case * int :
typedNewVal , ok := newVal . ( * int )
if ! ok {
return errors . New ( "mismatch in value types in tokenutil.UpgradeValue" )
}
return upgradeIntValue ( d , oldKey , newKey , typedOldVal , typedNewVal )
case * [ ] string :
typedNewVal , ok := newVal . ( * [ ] string )
if ! ok {
return errors . New ( "mismatch in value types in tokenutil.UpgradeValue" )
}
return upgradeStringSliceValue ( d , oldKey , newKey , typedOldVal , typedNewVal )
case * [ ] * sockaddr . SockAddrMarshaler :
typedNewVal , ok := newVal . ( * [ ] * sockaddr . SockAddrMarshaler )
if ! ok {
return errors . New ( "mismatch in value types in tokenutil.UpgradeValue" )
}
return upgradeSockAddrSliceValue ( d , oldKey , newKey , typedOldVal , typedNewVal )
default :
return errors . New ( "unhandled type in tokenutil.UpgradeValue" )
}
}
2019-06-14 14:17:04 +00:00
const (
tokenPeriodHelp = ` 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" ) . `
tokenExplicitMaxTTLHelp = ` If set , tokens created via this role
carry an explicit maximum TTL . During renewal ,
the current maximum TTL values of the role
and the mount are not checked for changes ,
and any updates to these values will have
no effect on the token being renewed . `
)