Token identity support (#6267)
* Implemented token backend support for identity * Fixed tests * Refactored a few checks for the token entity overwrite. Fixed tests. * Moved entity alias check up so that the entity and entity alias is only created when it has been specified in allowed_entity_aliases list * go mod vendor * Added glob pattern * Optimized allowed entity alias check * Added test for asterisk only * Changed to glob pattern anywhere * Changed response code in case of failure. Changed globbing pattern check. Added docs. * Added missing token role get parameter. Added more samples * Fixed failing tests * Corrected some cosmetical review points * Changed response code for invalid provided entity alias * Fixed minor things * Fixed failing test
This commit is contained in:
parent
e18866498d
commit
2b5aca4300
|
@ -272,4 +272,5 @@ type TokenCreateRequest struct {
|
|||
NumUses int `json:"num_uses"`
|
||||
Renewable *bool `json:"renewable,omitempty"`
|
||||
Type string `json:"type"`
|
||||
EntityAlias string `json:"entity_alias"`
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ type TokenCreateCommand struct {
|
|||
flagType string
|
||||
flagMetadata map[string]string
|
||||
flagPolicies []string
|
||||
flagEntityAlias string
|
||||
}
|
||||
|
||||
func (c *TokenCreateCommand) Synopsis() string {
|
||||
|
@ -176,6 +177,16 @@ func (c *TokenCreateCommand) Flags() *FlagSets {
|
|||
"specified multiple times to attach multiple policies.",
|
||||
})
|
||||
|
||||
f.StringVar(&StringVar{
|
||||
Name: "entity-alias",
|
||||
Target: &c.flagEntityAlias,
|
||||
Default: "",
|
||||
Usage: "Name of the entity alias to associate with during token creation. " +
|
||||
"Only works in combination with -role argument and used entity alias " +
|
||||
"must be listed in allowed_entity_aliases. If this has been specified, " +
|
||||
"the entity will not be inherited from the parent.",
|
||||
})
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
|
@ -224,6 +235,7 @@ func (c *TokenCreateCommand) Run(args []string) int {
|
|||
ExplicitMaxTTL: c.flagExplicitMaxTTL.String(),
|
||||
Period: c.flagPeriod.String(),
|
||||
Type: c.flagType,
|
||||
EntityAlias: c.flagEntityAlias,
|
||||
}
|
||||
|
||||
var secret *api.Secret
|
||||
|
|
|
@ -7,20 +7,18 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/hashicorp/errwrap"
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
sockaddr "github.com/hashicorp/go-sockaddr"
|
||||
|
||||
metrics "github.com/armon/go-metrics"
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/go-sockaddr"
|
||||
"github.com/hashicorp/vault/helper/identity"
|
||||
"github.com/hashicorp/vault/helper/namespace"
|
||||
"github.com/hashicorp/vault/sdk/framework"
|
||||
|
@ -397,6 +395,11 @@ func (ts *TokenStore) paths() []*framework.Path {
|
|||
Description: "Use 'token_bound_cidrs' instead.",
|
||||
Deprecated: true,
|
||||
},
|
||||
|
||||
"allowed_entity_aliases": &framework.FieldSchema{
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: "String or JSON list of allowed entity aliases. If set, specifies the entity aliases which are allowed to be used during token generation. This field supports globbing.",
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
|
@ -611,6 +614,9 @@ type tsRoleEntry struct {
|
|||
|
||||
// The set of CIDRs that tokens generated using this role will be bound to
|
||||
BoundCIDRs []*sockaddr.SockAddrMarshaler `json:"bound_cidrs"`
|
||||
|
||||
// The set of allowed entity aliases used during token creation
|
||||
AllowedEntityAliases []string `json:"allowed_entity_aliases" mapstructure:"allowed_entity_aliases" structs:"allowed_entity_aliases"`
|
||||
}
|
||||
|
||||
type accessorEntry struct {
|
||||
|
@ -1819,11 +1825,11 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data
|
|||
}
|
||||
|
||||
var countAccessorList,
|
||||
countCubbyholeKeys,
|
||||
deletedCountAccessorEmptyToken,
|
||||
deletedCountAccessorInvalidToken,
|
||||
deletedCountInvalidTokenInAccessor,
|
||||
deletedCountInvalidCubbyholeKey int64
|
||||
countCubbyholeKeys,
|
||||
deletedCountAccessorEmptyToken,
|
||||
deletedCountAccessorInvalidToken,
|
||||
deletedCountInvalidTokenInAccessor,
|
||||
deletedCountInvalidCubbyholeKey int64
|
||||
|
||||
validCubbyholeKeys := make(map[string]bool)
|
||||
|
||||
|
@ -2106,6 +2112,7 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
|||
NumUses int `mapstructure:"num_uses"`
|
||||
Period string
|
||||
Type string `mapstructure:"type"`
|
||||
EntityAlias string `mapstructure:"entity_alias"`
|
||||
}
|
||||
if err := mapstructure.WeakDecode(req.Data, &data); err != nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
|
@ -2202,6 +2209,51 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
|
|||
logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
// Verify the entity alias
|
||||
var explicitEntityID string
|
||||
if data.EntityAlias != "" {
|
||||
// Parameter is only allowed in combination with token role
|
||||
if role == nil {
|
||||
return logical.ErrorResponse("'entity_alias' is only allowed in combination with token role"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
// Check if there is a concrete match
|
||||
if !strutil.StrListContains(role.AllowedEntityAliases, data.EntityAlias) &&
|
||||
!strutil.StrListContainsGlob(role.AllowedEntityAliases, data.EntityAlias) {
|
||||
return logical.ErrorResponse("invalid 'entity_alias' value"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
// Get mount accessor which is required to lookup entity alias
|
||||
mountValidationResp := ts.core.router.MatchingMountByAccessor(req.MountAccessor)
|
||||
if mountValidationResp == nil {
|
||||
return logical.ErrorResponse("auth token mount accessor not found"), nil
|
||||
}
|
||||
|
||||
// Create alias for later processing
|
||||
alias := &logical.Alias{
|
||||
Name: data.EntityAlias,
|
||||
MountAccessor: mountValidationResp.Accessor,
|
||||
MountType: mountValidationResp.Type,
|
||||
}
|
||||
|
||||
// Create or fetch entity from entity alias
|
||||
entity, err := ts.core.identityStore.CreateOrFetchEntity(ctx, alias)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if entity == nil {
|
||||
return nil, errors.New("failed to create or fetch entity from given entity alias")
|
||||
}
|
||||
|
||||
// Validate that the entity is not disabled
|
||||
if entity.Disabled {
|
||||
return logical.ErrorResponse("entity from given entity alias is disabled"), logical.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// Set new entity id
|
||||
explicitEntityID = entity.ID
|
||||
}
|
||||
|
||||
// Setup the token entry
|
||||
te := logical.TokenEntry{
|
||||
Parent: req.ClientToken,
|
||||
|
@ -2434,9 +2486,14 @@ 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
|
||||
// not. If the token is not going to be an orphan, inherit the parent's
|
||||
// not. If setEntityID is set, the entity identifier will be overwritten.
|
||||
// Otherwise, if the token is not going to be an orphan, inherit the parent's
|
||||
// entity identifier into the child token.
|
||||
if te.Parent != "" {
|
||||
switch {
|
||||
case explicitEntityID != "":
|
||||
// Overwrite the entity identifier
|
||||
te.EntityID = explicitEntityID
|
||||
case te.Parent != "":
|
||||
te.EntityID = parent.EntityID
|
||||
|
||||
// If the parent has bound CIDRs, copy those into the child. We don't
|
||||
|
@ -2978,6 +3035,7 @@ func (ts *TokenStore) tokenStoreRoleRead(ctx context.Context, req *logical.Reque
|
|||
"path_suffix": role.PathSuffix,
|
||||
"renewable": role.Renewable,
|
||||
"token_type": role.TokenType.String(),
|
||||
"allowed_entity_aliases": role.AllowedEntityAliases,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -3183,6 +3241,11 @@ func (ts *TokenStore) tokenStoreRoleCreateUpdate(ctx context.Context, req *logic
|
|||
}
|
||||
}
|
||||
|
||||
allowedEntityAliasesRaw, ok := data.GetOk("allowed_entity_aliases")
|
||||
if ok {
|
||||
entry.AllowedEntityAliases = strutil.RemoveDuplicates(allowedEntityAliasesRaw.([]string), true)
|
||||
}
|
||||
|
||||
ns, err := namespace.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -13,17 +13,18 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-sockaddr"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
"github.com/hashicorp/errwrap"
|
||||
hclog "github.com/hashicorp/go-hclog"
|
||||
uuid "github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-sockaddr"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/vault/helper/identity"
|
||||
"github.com/hashicorp/vault/helper/namespace"
|
||||
"github.com/hashicorp/vault/sdk/helper/locksutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/parseutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/tokenutil"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
func TestTokenStore_CreateOrphanResponse(t *testing.T) {
|
||||
|
@ -2614,6 +2615,342 @@ func TestTokenStore_HandleRequest_Renew(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_HandleRequest_CreateToken_ExistingEntityAlias(t *testing.T) {
|
||||
core, _, root := TestCoreUnsealed(t)
|
||||
i := core.identityStore
|
||||
ctx := namespace.RootContext(nil)
|
||||
testPolicyName := "testpolicy"
|
||||
entityAliasName := "testentityalias"
|
||||
testRoleName := "test"
|
||||
|
||||
// Create manually an entity
|
||||
resp, err := i.HandleRequest(ctx, &logical.Request{
|
||||
Path: "entity",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"name": "testentity",
|
||||
"policies": []string{testPolicyName},
|
||||
},
|
||||
})
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
entityID := resp.Data["id"].(string)
|
||||
|
||||
// Find mount accessor
|
||||
resp, err = core.systemBackend.HandleRequest(namespace.RootContext(nil), &logical.Request{
|
||||
Path: "auth",
|
||||
Operation: logical.ReadOperation,
|
||||
})
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
|
||||
}
|
||||
tokenMountAccessor := resp.Data["token/"].(map[string]interface{})["accessor"].(string)
|
||||
|
||||
// Create manually an entity alias
|
||||
resp, err = i.HandleRequest(ctx, &logical.Request{
|
||||
Path: "entity-alias",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"name": entityAliasName,
|
||||
"canonical_id": entityID,
|
||||
"mount_accessor": tokenMountAccessor,
|
||||
},
|
||||
})
|
||||
|
||||
// Create token role
|
||||
resp, err = core.HandleRequest(ctx, &logical.Request{
|
||||
Path: "auth/token/roles/" + testRoleName,
|
||||
ClientToken: root,
|
||||
Operation: logical.CreateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"orphan": true,
|
||||
"period": "72h",
|
||||
"path_suffix": "happenin",
|
||||
"bound_cidrs": []string{"0.0.0.0/0"},
|
||||
"allowed_entity_aliases": []string{"test1", "test2", entityAliasName},
|
||||
},
|
||||
})
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
|
||||
resp, err = core.HandleRequest(ctx, &logical.Request{
|
||||
Path: "auth/token/create/" + testRoleName,
|
||||
Operation: logical.UpdateOperation,
|
||||
ClientToken: root,
|
||||
Data: map[string]interface{}{
|
||||
"entity_alias": entityAliasName,
|
||||
},
|
||||
})
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("expected a response")
|
||||
}
|
||||
if resp.Auth.EntityID != entityID {
|
||||
t.Fatalf("expected '%s' got '%s'", entityID, resp.Auth.EntityID)
|
||||
}
|
||||
|
||||
policyFound := false
|
||||
for _, policy := range resp.Auth.IdentityPolicies {
|
||||
if policy == testPolicyName {
|
||||
policyFound = true
|
||||
}
|
||||
}
|
||||
if !policyFound {
|
||||
t.Fatalf("Policy '%s' not derived by entity but should be. Auth %#v", testPolicyName, resp.Auth)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_HandleRequest_CreateToken_NonExistingEntityAlias(t *testing.T) {
|
||||
core, _, root := TestCoreUnsealed(t)
|
||||
i := core.identityStore
|
||||
ctx := namespace.RootContext(nil)
|
||||
entityAliasName := "testentityalias"
|
||||
testRoleName := "test"
|
||||
|
||||
// Create token role
|
||||
resp, err := core.HandleRequest(ctx, &logical.Request{
|
||||
Path: "auth/token/roles/" + testRoleName,
|
||||
ClientToken: root,
|
||||
Operation: logical.CreateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"period": "72h",
|
||||
"path_suffix": "happenin",
|
||||
"bound_cidrs": []string{"0.0.0.0/0"},
|
||||
"allowed_entity_aliases": []string{"test1", "test2", entityAliasName},
|
||||
},
|
||||
})
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
|
||||
// Create token with non existing entity alias
|
||||
resp, err = core.HandleRequest(ctx, &logical.Request{
|
||||
Path: "auth/token/create/" + testRoleName,
|
||||
Operation: logical.UpdateOperation,
|
||||
ClientToken: root,
|
||||
Data: map[string]interface{}{
|
||||
"entity_alias": entityAliasName,
|
||||
},
|
||||
})
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("expected a response")
|
||||
}
|
||||
|
||||
// Read the new entity
|
||||
resp, err = i.HandleRequest(ctx, &logical.Request{
|
||||
Path: "entity/id/" + resp.Auth.EntityID,
|
||||
Operation: logical.ReadOperation,
|
||||
})
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
|
||||
}
|
||||
|
||||
// Get the attached alias information
|
||||
aliases := resp.Data["aliases"].([]interface{})
|
||||
if len(aliases) != 1 {
|
||||
t.Fatalf("expected only one alias but got %d; Aliases: %#v", len(aliases), aliases)
|
||||
}
|
||||
alias := &identity.Alias{}
|
||||
if err := mapstructure.Decode(aliases[0], alias); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Validate
|
||||
if alias.Name != entityAliasName {
|
||||
t.Fatalf("alias name should be '%s' but is '%s'", entityAliasName, alias.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_HandleRequest_CreateToken_GlobPatternWildcardEntityAlias(t *testing.T) {
|
||||
core, _, root := TestCoreUnsealed(t)
|
||||
i := core.identityStore
|
||||
ctx := namespace.RootContext(nil)
|
||||
testRoleName := "test"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
globPattern string
|
||||
aliasName string
|
||||
}{
|
||||
{
|
||||
name: "prefix-asterisk",
|
||||
globPattern: "*-web",
|
||||
aliasName: "department-web",
|
||||
},
|
||||
{
|
||||
name: "suffix-asterisk",
|
||||
globPattern: "web-*",
|
||||
aliasName: "web-department",
|
||||
},
|
||||
{
|
||||
name: "middle-asterisk",
|
||||
globPattern: "web-*-web",
|
||||
aliasName: "web-department-web",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// Create token role
|
||||
resp, err := core.HandleRequest(ctx, &logical.Request{
|
||||
Path: "auth/token/roles/" + testRoleName,
|
||||
ClientToken: root,
|
||||
Operation: logical.CreateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"period": "72h",
|
||||
"path_suffix": "happening",
|
||||
"bound_cidrs": []string{"0.0.0.0/0"},
|
||||
"allowed_entity_aliases": []string{"test1", "test2", test.globPattern},
|
||||
},
|
||||
})
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
|
||||
// Create token with non existing entity alias
|
||||
resp, err = core.HandleRequest(ctx, &logical.Request{
|
||||
Path: "auth/token/create/" + testRoleName,
|
||||
Operation: logical.UpdateOperation,
|
||||
ClientToken: root,
|
||||
Data: map[string]interface{}{
|
||||
"entity_alias": test.aliasName,
|
||||
},
|
||||
})
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("expected a response")
|
||||
}
|
||||
|
||||
// Read the new entity
|
||||
resp, err = i.HandleRequest(ctx, &logical.Request{
|
||||
Path: "entity/id/" + resp.Auth.EntityID,
|
||||
Operation: logical.ReadOperation,
|
||||
})
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
|
||||
}
|
||||
|
||||
// Get the attached alias information
|
||||
aliases := resp.Data["aliases"].([]interface{})
|
||||
if len(aliases) != 1 {
|
||||
t.Fatalf("expected only one alias but got %d; Aliases: %#v", len(aliases), aliases)
|
||||
}
|
||||
alias := &identity.Alias{}
|
||||
if err := mapstructure.Decode(aliases[0], alias); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Validate
|
||||
if alias.Name != test.aliasName {
|
||||
t.Fatalf("alias name should be '%s' but is '%s'", test.aliasName, alias.Name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_HandleRequest_CreateToken_NotAllowedEntityAlias(t *testing.T) {
|
||||
core, _, root := TestCoreUnsealed(t)
|
||||
i := core.identityStore
|
||||
ctx := namespace.RootContext(nil)
|
||||
testPolicyName := "testpolicy"
|
||||
entityAliasName := "testentityalias"
|
||||
testRoleName := "test"
|
||||
|
||||
// Create manually an entity
|
||||
resp, err := i.HandleRequest(ctx, &logical.Request{
|
||||
Path: "entity",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"name": "testentity",
|
||||
"policies": []string{testPolicyName},
|
||||
},
|
||||
})
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
entityID := resp.Data["id"].(string)
|
||||
|
||||
// Find mount accessor
|
||||
resp, err = core.systemBackend.HandleRequest(ctx, &logical.Request{
|
||||
Path: "auth",
|
||||
Operation: logical.ReadOperation,
|
||||
})
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("bad: resp: %#v\nerr: %v", resp, err)
|
||||
}
|
||||
tokenMountAccessor := resp.Data["token/"].(map[string]interface{})["accessor"].(string)
|
||||
|
||||
// Create manually an entity alias
|
||||
resp, err = i.HandleRequest(ctx, &logical.Request{
|
||||
Path: "entity-alias",
|
||||
Operation: logical.UpdateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"name": entityAliasName,
|
||||
"canonical_id": entityID,
|
||||
"mount_accessor": tokenMountAccessor,
|
||||
},
|
||||
})
|
||||
|
||||
// Create token role
|
||||
resp, err = core.HandleRequest(ctx, &logical.Request{
|
||||
Path: "auth/token/roles/" + testRoleName,
|
||||
ClientToken: root,
|
||||
Operation: logical.CreateOperation,
|
||||
Data: map[string]interface{}{
|
||||
"period": "72h",
|
||||
"allowed_entity_aliases": []string{"test1", "test2", "testentityaliasn"},
|
||||
},
|
||||
})
|
||||
if err != nil || (resp != nil && resp.IsError()) {
|
||||
t.Fatalf("err: %v\nresp: %#v", err, resp)
|
||||
}
|
||||
|
||||
resp, _ = core.HandleRequest(ctx, &logical.Request{
|
||||
Path: "auth/token/create/" + testRoleName,
|
||||
Operation: logical.UpdateOperation,
|
||||
ClientToken: root,
|
||||
Data: map[string]interface{}{
|
||||
"entity_alias": entityAliasName,
|
||||
},
|
||||
})
|
||||
if resp == nil || resp.Data == nil {
|
||||
t.Fatal("expected a response")
|
||||
}
|
||||
if resp.Data["error"] != "invalid 'entity_alias' value" {
|
||||
t.Fatalf("wrong error returned. Err: %s", resp.Data["error"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_HandleRequest_CreateToken_NoRoleEntityAlias(t *testing.T) {
|
||||
core, _, root := TestCoreUnsealed(t)
|
||||
ctx := namespace.RootContext(nil)
|
||||
entityAliasName := "testentityalias"
|
||||
|
||||
resp, _ := core.HandleRequest(ctx, &logical.Request{
|
||||
Path: "auth/token/create",
|
||||
Operation: logical.UpdateOperation,
|
||||
ClientToken: root,
|
||||
Data: map[string]interface{}{
|
||||
"entity_alias": entityAliasName,
|
||||
},
|
||||
})
|
||||
if resp == nil || resp.Data == nil {
|
||||
t.Fatal("expected a response")
|
||||
}
|
||||
if resp.Data["error"] != "'entity_alias' is only allowed in combination with token role" {
|
||||
t.Fatalf("wrong error returned. Err: %s", resp.Data["error"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_HandleRequest_RenewSelf(t *testing.T) {
|
||||
exp := mockExpiration(t)
|
||||
ts := exp.tokenStore
|
||||
|
@ -2719,6 +3056,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
|
|||
"renewable": true,
|
||||
"token_type": "default-service",
|
||||
"token_num_uses": 123,
|
||||
"allowed_entity_aliases": []string(nil),
|
||||
}
|
||||
|
||||
if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "0.0.0.0/0" {
|
||||
|
@ -2778,6 +3116,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
|
|||
"explicit_max_ttl": int64(288000),
|
||||
"renewable": false,
|
||||
"token_type": "default-service",
|
||||
"allowed_entity_aliases": []string(nil),
|
||||
}
|
||||
|
||||
if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "0.0.0.0/0" {
|
||||
|
@ -2827,6 +3166,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
|
|||
"token_period": int64(0),
|
||||
"renewable": false,
|
||||
"token_type": "default-service",
|
||||
"allowed_entity_aliases": []string(nil),
|
||||
}
|
||||
|
||||
if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "0.0.0.0/0" {
|
||||
|
@ -2876,6 +3216,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
|
|||
"token_period": int64(0),
|
||||
"renewable": false,
|
||||
"token_type": "default-service",
|
||||
"allowed_entity_aliases": []string(nil),
|
||||
}
|
||||
|
||||
if diff := deep.Equal(expected, resp.Data); diff != nil {
|
||||
|
@ -3685,6 +4026,7 @@ func TestTokenStore_RoleTokenFields(t *testing.T) {
|
|||
"explicit_max_ttl": int64(3600),
|
||||
"renewable": false,
|
||||
"token_type": "batch",
|
||||
"allowed_entity_aliases": []string(nil),
|
||||
}
|
||||
|
||||
if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1" {
|
||||
|
@ -3737,6 +4079,7 @@ func TestTokenStore_RoleTokenFields(t *testing.T) {
|
|||
"explicit_max_ttl": int64(7200),
|
||||
"renewable": false,
|
||||
"token_type": "default-service",
|
||||
"allowed_entity_aliases": []string(nil),
|
||||
}
|
||||
|
||||
if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1" {
|
||||
|
@ -3788,6 +4131,7 @@ func TestTokenStore_RoleTokenFields(t *testing.T) {
|
|||
"explicit_max_ttl": int64(0),
|
||||
"renewable": false,
|
||||
"token_type": "default-service",
|
||||
"allowed_entity_aliases": []string(nil),
|
||||
}
|
||||
|
||||
if resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1" {
|
||||
|
@ -3841,6 +4185,7 @@ func TestTokenStore_RoleTokenFields(t *testing.T) {
|
|||
"explicit_max_ttl": int64(0),
|
||||
"renewable": false,
|
||||
"token_type": "service",
|
||||
"allowed_entity_aliases": []string(nil),
|
||||
}
|
||||
|
||||
if resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1" {
|
||||
|
|
|
@ -102,6 +102,10 @@ during this call.
|
|||
- `period` `(string: "")` - If specified, the token will be periodic; it will have
|
||||
no maximum TTL (unless an "explicit-max-ttl" is also set) but every renewal
|
||||
will use the given period. Requires a root/sudo token to use.
|
||||
- `entity_alias` `(string: "")` - Name of the entity alias to associate with
|
||||
during token creation. Only works in combination with `role_name` argument
|
||||
and used entity alias must be listed in `allowed_entity_aliases`. If this has
|
||||
been specified, the entity will not be inherited from the parent.
|
||||
|
||||
### Sample Payload
|
||||
|
||||
|
@ -573,16 +577,20 @@ $ curl \
|
|||
"lease_duration": 0,
|
||||
"renewable": false,
|
||||
"data": {
|
||||
"allowed_policies": [
|
||||
"dev"
|
||||
"allowed_entity_aliases": [
|
||||
"my-entity-alias"
|
||||
],
|
||||
"allowed_policies": [],
|
||||
"disallowed_policies": [],
|
||||
"explicit_max_ttl": 0,
|
||||
"name": "nomad",
|
||||
"orphan": false,
|
||||
"path_suffix": "",
|
||||
"period": 0,
|
||||
"renewable": true
|
||||
"renewable": true,
|
||||
"token_explicit_max_ttl": 0,
|
||||
"token_period": 0,
|
||||
"token_type": "default-service"
|
||||
},
|
||||
"warnings": null
|
||||
}
|
||||
|
@ -682,6 +690,9 @@ tokens created against a role to be revoked using the
|
|||
be returned unless the client requests a `batch` type token at token creation
|
||||
time. If `default-batch`, `batch` tokens will be returned unless the client
|
||||
requests a `service` type token at token creation time.
|
||||
- `allowed_entity_aliases` `(string: "", or list: [])` - String or JSON list
|
||||
of allowed entity aliases. If set, specifies the entity aliases which are
|
||||
allowed to be used during token generation. This field supports globbing.
|
||||
|
||||
### Sample Payload
|
||||
|
||||
|
@ -692,7 +703,8 @@ tokens created against a role to be revoked using the
|
|||
"name": "nomad",
|
||||
"orphan": false,
|
||||
"bound_cidrs": ["127.0.0.1/32", "128.252.0.0/16"],
|
||||
"renewable": true
|
||||
"renewable": true,
|
||||
"allowed_entity_aliases": ["web-entity-alias", "app-entity-*"]
|
||||
```
|
||||
|
||||
### Sample Request
|
||||
|
|
Loading…
Reference in New Issue