mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-05 12:26:34 +02:00
* feat(identity): accept oauth tokens type: draft * feat(identity): make basic token lookup op work type: draft * fix(identity): failing tests type: draft * feat(identity): add new tests + handle renew & revoke type: draft * feat(identity): clean up and tests type: draft * feat(identity)(Accept OAuth JWT tokens): Add more tests * feat(identity)(Accept OAuth JWT tokens): Test updates * feat(identity)(Accept OAuth JWT tokens): Revert go version changes * feat(identity)(Accept OAuth JWT tokens): add missing godoc for tests * feat(identity)(Accept OAuth JWT tokens): fix tests * feat(identity)(Accept OAuth JWT tokens): fix more CI issues * [POC] Accept and Validate External OAuth Token for Agent Identity (#10991) * feat(identity): accept oauth tokens type: draft * feat(identity): make basic token lookup op work type: draft * fix(identity): failing tests type: draft * feat(identity): add new tests + handle renew & revoke type: draft * feat(identity): clean up and tests type: draft * feat(identity)(Accept OAuth JWT tokens): Add more tests * feat(identity)(Accept OAuth JWT tokens): Test updates * feat(identity)(Accept OAuth JWT tokens): Revert go version changes * feat(identity)(Accept OAuth JWT tokens): add missing godoc for tests * feat(identity)(Accept OAuth JWT tokens): fix tests * feat(identity)(Accept OAuth JWT tokens): fix more CI issues * Add JWT token tidy cleanup functionality * Refactor JWT tidy tests to use retryUntil instead of time.Sleep * feat(identity)(Accept OAuth JWT tokens): fix leaky lease * feat(identity)(Accept OAuth JWT tokens): fix leaky lease II and add claims to TE * Add TestLogicalWithJwtAndSCIM e2e test * feat(identity)(Accept OAuth JWT tokens): add more tests * feat(identity)(Accept OAuth JWT tokens): change email * feat(identity)(Accept OAuth JWT tokens): e2e test for scim * Accept and validate RAR (#11005) * fix(identity)(Accept OAuth JWT tokens): add missing headers --------- * wip * wip * wip * feat(identity): auth with delegation jwts * feat(identity): optimize jwks fetching * wip * add lookup by entity-id * feat(identity): optimize jwks fetching * feat(identity): optimize jwks fetching and refactoring * feat(identity): fix breaking tests * feat(identity): accept oauth tokens type: draft * feat(identity): make basic token lookup op work type: draft * fix(identity): failing tests type: draft * feat(identity): add new tests + handle renew & revoke type: draft * feat(identity): clean up and tests type: draft * feat(identity)(Accept OAuth JWT tokens): Add more tests * feat(identity)(Accept OAuth JWT tokens): Test updates * feat(identity)(Accept OAuth JWT tokens): Revert go version changes * feat(identity)(Accept OAuth JWT tokens): add missing godoc for tests * feat(identity)(Accept OAuth JWT tokens): fix tests * feat(identity)(Accept OAuth JWT tokens): fix more CI issues * [POC] Accept and Validate External OAuth Token for Agent Identity (#10991) * feat(identity): accept oauth tokens type: draft * feat(identity): make basic token lookup op work type: draft * fix(identity): failing tests type: draft * feat(identity): add new tests + handle renew & revoke type: draft * feat(identity): clean up and tests type: draft * feat(identity)(Accept OAuth JWT tokens): Add more tests * feat(identity)(Accept OAuth JWT tokens): Test updates * feat(identity)(Accept OAuth JWT tokens): Revert go version changes * feat(identity)(Accept OAuth JWT tokens): add missing godoc for tests * feat(identity)(Accept OAuth JWT tokens): fix tests * feat(identity)(Accept OAuth JWT tokens): fix more CI issues * Add JWT token tidy cleanup functionality * Refactor JWT tidy tests to use retryUntil instead of time.Sleep * feat(identity)(Accept OAuth JWT tokens): fix leaky lease * feat(identity)(Accept OAuth JWT tokens): fix leaky lease II and add claims to TE * Add TestLogicalWithJwtAndSCIM e2e test * feat(identity)(Accept OAuth JWT tokens): add more tests * feat(identity)(Accept OAuth JWT tokens): change email * feat(identity)(Accept OAuth JWT tokens): e2e test for scim * Accept and validate RAR (#11005) * fix(identity)(Accept OAuth JWT tokens): add missing headers --------- * feat(identity): auth with delegation jwts * feat(identity): optimize jwks fetching * wip * wip * wip * wip * add lookup by entity-id * feat(identity): optimize jwks fetching * feat(identity): optimize jwks fetching and refactoring * feat(identity): fix breaking tests * VAULT-42642 Fix feature branch tests, some CE -> Ent moving (#12417) * VAULT-42642 Fix feathre branch tests, some CE -> Ent moving * VAULT-42642 fix last test? * skip test * proto * more test fixes * buf format * Fix createVaultEntityForUser signature (#12439) * VAULT-42533 Agent Registry: enforce entity invariants, miscellaneous improvements (#12399) * VAULT-42533 enforce entity invariants, miscellaneous improvements * typos * Improve path handling for rar + add tests (#11934) * Moving stuff around for CE * fmt * backend * more CE changes * more fixes * further fixes * rework clone * VAULT-42621 Move JWT tests (and some standalone functionality) to enterprise files (#12460) * VAULT-42621 Move JWT tests (and some standalone functionality) to enterprise files * logical * skip failing tests * more skips * missed tests * one more * VAULT-42619 Refactor token_ent and surrounding files to move logic into enterprise (#12473) * VAULT-42619 Refactor token_ent and surrounding files to move logic into enterprise * test fixes * feedback * VAULT-42631 Rename ISJWT to IsEnterpriseToken, some refactoring (#12509) * VAULT-42631 Rename ISJWT to IsEnterpriseToken, some refactoring * godocs * VAULT-42779 refactor JWT parts of acl.go to enterprise files (#12512) * VAULT-42630 move token tidy for JWT to ent files (#12534) * VAULT-42818 rename jwtjti in request struct (#12539) * VAULT-42633 CE-ify request handling and flow around validateJwtAndFetchEntity (#12541) * VAULT-42633 CE-ify request handling * Copyright headers * VAULT-42633 Move IsJWT back to CE code, add explanations (#12576) * VAULT-42633 CE-ify request handling * Copyright headers * VAULT-42633 Move IsJWT back to CE code, add explanations * VAULT-42870 Move jwtAuthManager to enterprise (#12586) * VAULT-42796 Move config to enterprise files (#12604) * VAULT-42796 Move config to enterprise files * more build failures * fix test util * two more compilatin things * VAULT42796 - fix missing return (#12617) * VAULT-42796 Move config to enterprise files * more build failures * fix test util * two more compilatin things * VAUlt-42796 fix test * Fix linting for rar_ent (#12618) * VAULT-42796 Move config to enterprise files * more build failures * fix test util * two more compilatin things * VAUlt-42796 fix test * Fix linter for rar code * fix authresults * typo * return error * fix method signature --------- Signed-off-by: Arnab Chatterjee <arnab.chatterjee@hashicorp.com> Co-authored-by: Violet Hynes <violet.hynes@hashicorp.com> Co-authored-by: Arnab Chatterjee <arnab.chatterjee@hashicorp.com> Co-authored-by: Arnab Chatterjee <arnabkaycee@gmail.com> Co-authored-by: Bianca <48203644+biazmoreira@users.noreply.github.com> Co-authored-by: davidadeleon <ddeleon@hashicorp.com> Co-authored-by: Bianca Moreira <bianca.moreira@hashicorp.com>
This commit is contained in:
parent
b25410c747
commit
f5dbe55f55
10
sdk/helper/consts/token_consts_ce.go
Normal file
10
sdk/helper/consts/token_consts_ce.go
Normal file
@ -0,0 +1,10 @@
|
||||
// Copyright IBM Corp. 2016, 2025
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build !enterprise
|
||||
|
||||
package consts
|
||||
|
||||
func GetEnterpriseTokenPrefix() string {
|
||||
return "unimplemented"
|
||||
}
|
||||
@ -137,6 +137,9 @@ type Request struct {
|
||||
// hashed.
|
||||
ClientToken string `json:"client_token" structs:"client_token" mapstructure:"client_token" sentinel:""`
|
||||
|
||||
// EnterpriseTokenMetadata is used to store metadata related to enterprise token based requests.
|
||||
EnterpriseTokenMetadata string `json:"enterprise_token_metadata" structs:"enterprise_token_metadata" mapstructure:"enterprise_token_metadata" sentinel:""`
|
||||
|
||||
// ClientTokenAccessor is provided to the core so that the it can get
|
||||
// logged as part of request audit logging.
|
||||
ClientTokenAccessor string `json:"client_token_accessor" structs:"client_token_accessor" mapstructure:"client_token_accessor" sentinel:""`
|
||||
|
||||
@ -36,6 +36,8 @@ const (
|
||||
// TokenTypeDefault is sent back by the mount, create Batch tokens
|
||||
TokenTypeDefaultBatch
|
||||
|
||||
TokenTypeEnt
|
||||
|
||||
// ClientIDTWEDelimiter Delimiter between the string fields used to generate a client
|
||||
// ID for tokens without entities. This is the 0 character, which
|
||||
// is a non-printable string. Please see unicode.IsPrint for details.
|
||||
@ -67,6 +69,8 @@ func (t *TokenType) UnmarshalJSON(b []byte) error {
|
||||
*t = TokenTypeDefaultService
|
||||
case `"default-batch"`:
|
||||
*t = TokenTypeDefaultBatch
|
||||
case `"ent"`:
|
||||
*t = TokenTypeEnt
|
||||
default:
|
||||
return fmt.Errorf("unknown token type %q", s)
|
||||
}
|
||||
@ -75,6 +79,8 @@ func (t *TokenType) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// TokenEntry is used to represent a given token
|
||||
type TokenEntry struct {
|
||||
EntToken
|
||||
|
||||
Type TokenType `json:"type" mapstructure:"type" structs:"type" sentinel:""`
|
||||
|
||||
// ID of this entry, generally a random UUID
|
||||
@ -253,7 +259,7 @@ func (te *TokenEntry) SentinelGet(key string) (interface{}, error) {
|
||||
case "type":
|
||||
teType := te.Type
|
||||
switch teType {
|
||||
case TokenTypeBatch, TokenTypeService:
|
||||
case TokenTypeBatch, TokenTypeService, TokenTypeEnt:
|
||||
case TokenTypeDefault:
|
||||
teType = TokenTypeService
|
||||
default:
|
||||
|
||||
10
sdk/logical/token_ce.go
Normal file
10
sdk/logical/token_ce.go
Normal file
@ -0,0 +1,10 @@
|
||||
// Copyright IBM Corp. 2016, 2025
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build !enterprise
|
||||
|
||||
package logical
|
||||
|
||||
type (
|
||||
EntToken struct{}
|
||||
)
|
||||
@ -6,9 +6,9 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const _TokenTypeName = "defaultservicebatchdefault-servicedefault-batch"
|
||||
const _TokenTypeName = "defaultservicebatchdefault-servicedefault-batchent"
|
||||
|
||||
var _TokenTypeIndex = [...]uint8{0, 7, 14, 19, 34, 47}
|
||||
var _TokenTypeIndex = [...]uint8{0, 7, 14, 19, 34, 47, 50}
|
||||
|
||||
func (i TokenType) String() string {
|
||||
if i >= TokenType(len(_TokenTypeIndex)-1) {
|
||||
@ -17,7 +17,7 @@ func (i TokenType) String() string {
|
||||
return _TokenTypeName[_TokenTypeIndex[i]:_TokenTypeIndex[i+1]]
|
||||
}
|
||||
|
||||
var _TokenTypeValues = []TokenType{0, 1, 2, 3, 4}
|
||||
var _TokenTypeValues = []TokenType{0, 1, 2, 3, 4, 5}
|
||||
|
||||
var _TokenTypeNameToValueMap = map[string]TokenType{
|
||||
_TokenTypeName[0:7]: 0,
|
||||
@ -25,6 +25,7 @@ var _TokenTypeNameToValueMap = map[string]TokenType{
|
||||
_TokenTypeName[14:19]: 2,
|
||||
_TokenTypeName[19:34]: 3,
|
||||
_TokenTypeName[34:47]: 4,
|
||||
_TokenTypeName[47:50]: 5,
|
||||
}
|
||||
|
||||
// TokenTypeString retrieves an enum value from the enum constants string name.
|
||||
|
||||
@ -26,6 +26,8 @@ import (
|
||||
// ACL is used to wrap a set of policies to provide
|
||||
// an efficient interface for access control.
|
||||
type ACL struct {
|
||||
entAcl
|
||||
|
||||
// exactRules contains the path policies that are exact
|
||||
exactRules *radix.Tree
|
||||
|
||||
@ -49,6 +51,7 @@ type PolicyCheckOpts struct {
|
||||
}
|
||||
|
||||
type AuthResults struct {
|
||||
entAuthResults
|
||||
ACLResults *ACLResults
|
||||
SentinelResults *SentinelResults
|
||||
Allowed bool
|
||||
@ -358,6 +361,11 @@ func (a *ACL) Capabilities(ctx context.Context, path string) []string {
|
||||
|
||||
// AllowOperation is used to check if the given operation is permitted.
|
||||
func (a *ACL) AllowOperation(ctx context.Context, req *logical.Request, capCheckOnly bool) (ret *ACLResults) {
|
||||
ret = a.performEnterpriseAclChecks(ctx, req, capCheckOnly)
|
||||
if ret != nil {
|
||||
return ret
|
||||
}
|
||||
|
||||
ret = new(ACLResults)
|
||||
|
||||
// Fast-path root
|
||||
|
||||
21
vault/acl_ce.go
Normal file
21
vault/acl_ce.go
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright IBM Corp. 2016, 2025
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
//go:build !enterprise
|
||||
|
||||
package vault
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
)
|
||||
|
||||
type (
|
||||
entAcl struct{}
|
||||
entAuthResults struct{}
|
||||
)
|
||||
|
||||
func (a *ACL) performEnterpriseAclChecks(_ context.Context, _ *logical.Request, _ bool) (ret *ACLResults) {
|
||||
return nil
|
||||
}
|
||||
@ -101,8 +101,6 @@ func (c *Core) fetchEntityAndDerivedPolicies(ctx context.Context, tokenNS *names
|
||||
|
||||
policies := make(map[string][]string)
|
||||
if !skipDeriveEntityPolicies {
|
||||
// c.logger.Debug("entity successfully fetched; adding entity policies to token's policies to create ACL")
|
||||
|
||||
// Attach the policies on the entity
|
||||
if len(entity.Policies) != 0 {
|
||||
policies[entity.NamespaceID] = append(policies[entity.NamespaceID], entity.Policies...)
|
||||
@ -239,12 +237,33 @@ func (c *Core) fetchACLTokenEntryAndEntity(ctx context.Context, req *logical.Req
|
||||
return nil, nil, nil, nil, ErrInternalError
|
||||
}
|
||||
|
||||
var secondEntity *identity.Entity
|
||||
if IsEnterpriseToken(req.ClientToken) {
|
||||
isValidEnterpriseToken, tokenMetadataContainer, entity, entity2, err := c.validateEnterpriseTokenAndFetchEntity(ctx, req.ClientToken)
|
||||
if err != nil {
|
||||
c.logger.Error("failed to validate enterprise token", "error", err)
|
||||
}
|
||||
if !isValidEnterpriseToken {
|
||||
return nil, nil, nil, nil, logical.ErrPermissionDenied
|
||||
}
|
||||
req.EnterpriseTokenMetadata = getEnterpriseTokenMetadata(tokenMetadataContainer)
|
||||
secondEntity = entity2
|
||||
err = c.createAndStoreEnterpriseTokenEntry(ctx, req, tokenMetadataContainer, entity)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, multierror.Append(err, errors.New("failed in processing enterprise token"))
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve the token policy
|
||||
var te *logical.TokenEntry
|
||||
switch req.TokenEntry() {
|
||||
case nil:
|
||||
var err error
|
||||
te, err = c.tokenStore.Lookup(ctx, req.ClientToken)
|
||||
if IsEnterpriseToken(req.ClientToken) {
|
||||
te, err = c.tokenStore.Lookup(ctx, getEnterpriseTokenId(req.EnterpriseTokenMetadata))
|
||||
} else {
|
||||
te, err = c.tokenStore.Lookup(ctx, req.ClientToken)
|
||||
}
|
||||
if err != nil {
|
||||
c.logger.Error("failed to lookup acl token", "error", err)
|
||||
return nil, nil, nil, nil, ErrInternalError
|
||||
@ -304,6 +323,21 @@ func (c *Core) fetchACLTokenEntryAndEntity(ctx context.Context, req *logical.Req
|
||||
policyNames[nsID] = policyutil.SanitizePolicies(append(policyNames[nsID], nsPolicies...), false)
|
||||
}
|
||||
|
||||
var secondEntityPolicyNames map[string][]string
|
||||
if secondEntity != nil {
|
||||
c.logger.Debug("building separate ACL for second entity", "entity_id", secondEntity.ID)
|
||||
secondEntityPolicyNames = make(map[string][]string)
|
||||
_, secondEntityIdentityPolicies, err := c.fetchEntityAndDerivedPolicies(ctx, tokenNS, secondEntity.ID, false)
|
||||
if err != nil {
|
||||
c.logger.Error("failed to fetch second entity policies", "error", err)
|
||||
return nil, nil, nil, nil, ErrInternalError
|
||||
}
|
||||
// Store second entity policies separately - do NOT merge with primary entity's policies
|
||||
for nsID, nsPolicies := range secondEntityIdentityPolicies {
|
||||
secondEntityPolicyNames[nsID] = policyutil.SanitizePolicies(nsPolicies, false)
|
||||
}
|
||||
}
|
||||
|
||||
// Attach token's namespace information to the context. Wrapping tokens by
|
||||
// should be able to be used anywhere, so we also special case behavior.
|
||||
var tokenCtx context.Context
|
||||
@ -344,6 +378,14 @@ func (c *Core) fetchACLTokenEntryAndEntity(ctx context.Context, req *logical.Req
|
||||
return nil, nil, nil, nil, ErrInternalError
|
||||
}
|
||||
|
||||
if secondEntity != nil && len(secondEntityPolicyNames) > 0 {
|
||||
newAcl, err := c.performSecondaryEntityTokenChecks(tokenCtx, acl, secondEntity, secondEntityPolicyNames)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
acl = newAcl
|
||||
}
|
||||
|
||||
return acl, te, entity, identityPolicies, nil
|
||||
}
|
||||
|
||||
@ -804,7 +846,7 @@ func (c *Core) handleCancelableRequest(ctx context.Context, req *logical.Request
|
||||
// We don't care if the token is a server side consistent token or not. Either way, we're going
|
||||
// to be returning it for these paths instead of the short token stored in vault.
|
||||
requestBodyToken = token.(string)
|
||||
if IsSSCToken(token.(string)) {
|
||||
if IsSSCToken(token.(string)) && !IsEnterpriseToken(token.(string)) {
|
||||
token, err = c.CheckSSCToken(ctx, token.(string), c.isLoginRequest(ctx, req), c.perfStandby)
|
||||
// If we receive an error from CheckSSCToken, we can assume the token is bad somehow, and the client
|
||||
// should receive a 403 bad token error like they do for all other invalid tokens, unless the error
|
||||
@ -1096,12 +1138,15 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp
|
||||
return
|
||||
}
|
||||
|
||||
var auth *logical.Auth
|
||||
var te *logical.TokenEntry
|
||||
var ctErr error
|
||||
// Validate the token
|
||||
auth, te, ctErr := c.CheckToken(ctx, req, false)
|
||||
if ctErr == logical.ErrRelativePath {
|
||||
auth, te, ctErr = c.CheckToken(ctx, req, false)
|
||||
if errors.Is(ctErr, logical.ErrRelativePath) {
|
||||
return logical.ErrorResponse(ctErr.Error()), nil, ctErr
|
||||
}
|
||||
if ctErr == logical.ErrPerfStandbyPleaseForward {
|
||||
if errors.Is(ctErr, logical.ErrPerfStandbyPleaseForward) {
|
||||
return nil, nil, ctErr
|
||||
}
|
||||
|
||||
@ -2693,7 +2738,7 @@ func (c *Core) LocalUpdateUserFailedLoginInfo(ctx context.Context, userKey Faile
|
||||
|
||||
// PopulateTokenEntry looks up req.ClientToken in the token store and uses
|
||||
// it to set other fields in req. Does nothing if ClientToken is empty
|
||||
// or a JWT token, or for service tokens that don't exist in the token store.
|
||||
// or an Enterprise token, or for service tokens that don't exist in the token store.
|
||||
// Should be called with read stateLock held.
|
||||
func (c *Core) PopulateTokenEntry(ctx context.Context, req *logical.Request) error {
|
||||
if req.ClientToken == "" {
|
||||
@ -2704,7 +2749,7 @@ func (c *Core) PopulateTokenEntry(ctx context.Context, req *logical.Request) err
|
||||
// doesn't exist because the request may be to an unauthenticated
|
||||
// endpoint/login endpoint where a bad current token doesn't matter, or
|
||||
// a token from a Vault version pre-accessors. We ignore errors for
|
||||
// JWTs.
|
||||
// Enterprise tokens.
|
||||
token := req.ClientToken
|
||||
var err error
|
||||
req.InboundSSCToken = token
|
||||
@ -2754,8 +2799,6 @@ func (c *Core) PopulateTokenEntry(ctx context.Context, req *logical.Request) err
|
||||
if errors.Is(err, logical.ErrPerfStandbyPleaseForward) || errors.Is(err, logical.ErrMissingRequiredState) {
|
||||
return err
|
||||
}
|
||||
// If we have two dots but the second char is a dot it's a vault
|
||||
// token of the form s.SOMETHING.nsid, not a JWT
|
||||
if !IsJWT(token) {
|
||||
return fmt.Errorf("error performing token check: %w", err)
|
||||
}
|
||||
|
||||
30
vault/request_handling_ce.go
Normal file
30
vault/request_handling_ce.go
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright IBM Corp. 2016, 2025
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
//go:build !enterprise
|
||||
|
||||
package vault
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/hashicorp/vault/helper/identity"
|
||||
"github.com/hashicorp/vault/sdk/logical"
|
||||
)
|
||||
|
||||
func (c *Core) validateEnterpriseTokenAndFetchEntity(ctx context.Context, tokenString string) (bool, map[string]interface{}, *identity.Entity, *identity.Entity, error) {
|
||||
return false, nil, nil, nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (c *Core) createAndStoreEnterpriseTokenEntry(ctx context.Context, req *logical.Request, allClaims map[string]interface{}, entity *identity.Entity) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEnterpriseTokenMetadata(_ map[string]interface{}) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *Core) performSecondaryEntityTokenChecks(_ context.Context, _ *ACL, _ *identity.Entity, _ map[string][]string) (*ACL, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
@ -677,8 +677,8 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc
|
||||
return nil, false, false, fmt.Errorf("nil token entry")
|
||||
}
|
||||
|
||||
if te.Type != logical.TokenTypeService {
|
||||
return logical.ErrorResponse(`cubbyhole operations are only supported by "service" type tokens`), false, false, nil
|
||||
if te.Type != logical.TokenTypeService && te.Type != logical.TokenTypeEnt {
|
||||
return logical.ErrorResponse(`cubbyhole operations are only supported by "service" or enterprise type tokens`), false, false, nil
|
||||
}
|
||||
|
||||
switch {
|
||||
|
||||
@ -1107,9 +1107,11 @@ func (ts *TokenStore) create(ctx context.Context, entry *logical.TokenEntry) err
|
||||
}
|
||||
|
||||
switch entry.Type {
|
||||
case logical.TokenTypeDefault, logical.TokenTypeService:
|
||||
case logical.TokenTypeDefault, logical.TokenTypeService, logical.TokenTypeEnt:
|
||||
// In case it was default, force to service
|
||||
entry.Type = logical.TokenTypeService
|
||||
if entry.Type == logical.TokenTypeDefault {
|
||||
entry.Type = logical.TokenTypeService
|
||||
}
|
||||
|
||||
// Generate an ID if necessary
|
||||
userSelectedID := true
|
||||
@ -1126,7 +1128,7 @@ func (ts *TokenStore) create(ctx context.Context, entry *logical.TokenEntry) err
|
||||
}
|
||||
}
|
||||
|
||||
if userSelectedID {
|
||||
if userSelectedID && entry.Type != logical.TokenTypeEnt {
|
||||
switch {
|
||||
case strings.HasPrefix(entry.ID, consts.ServiceTokenPrefix):
|
||||
return fmt.Errorf("custom token ID cannot have the 'hvs.' prefix")
|
||||
@ -1151,7 +1153,10 @@ func (ts *TokenStore) create(ctx context.Context, entry *logical.TokenEntry) err
|
||||
entry.ID = fmt.Sprintf("%s.%s", entry.ID, tokenNS.ID)
|
||||
}
|
||||
|
||||
if tokenNS.ID != namespace.RootNamespaceID || strings.HasPrefix(entry.ID, consts.ServiceTokenPrefix) || strings.HasPrefix(entry.ID, consts.LegacyServiceTokenPrefix) {
|
||||
if tokenNS.ID != namespace.RootNamespaceID ||
|
||||
strings.HasPrefix(entry.ID, consts.ServiceTokenPrefix) ||
|
||||
strings.HasPrefix(entry.ID, consts.LegacyServiceTokenPrefix) ||
|
||||
strings.HasPrefix(entry.ID, consts.GetEnterpriseTokenPrefix()) {
|
||||
if entry.CubbyholeID == "" {
|
||||
cubbyholeID, err := base62.Random(TokenLength)
|
||||
if err != nil {
|
||||
@ -1163,7 +1168,7 @@ func (ts *TokenStore) create(ctx context.Context, entry *logical.TokenEntry) err
|
||||
|
||||
// If the user didn't specifically pick the ID, e.g. because they were
|
||||
// sudo/root, check for collision; otherwise trust the process
|
||||
if userSelectedID {
|
||||
if userSelectedID || entry.Type == logical.TokenTypeEnt {
|
||||
exist, _ := ts.lookupInternal(ctx, entry.ID, false, true)
|
||||
if exist != nil {
|
||||
return fmt.Errorf("cannot create a token with a duplicate ID")
|
||||
@ -1636,7 +1641,7 @@ func (ts *TokenStore) lookupInternal(ctx context.Context, id string, salted, tai
|
||||
// If possible, always use the token's namespace. If it doesn't match
|
||||
// the request namespace, ensure the request namespace is a child
|
||||
_, nsID := namespace.SplitIDFromString(id)
|
||||
if nsID != "" {
|
||||
if nsID != "" || strings.HasPrefix(id, consts.GetEnterpriseTokenPrefix()) {
|
||||
tokenNS, err := NamespaceByID(ctx, nsID, ts.core)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to look up namespace from the token: %w", err)
|
||||
@ -1753,6 +1758,11 @@ func (ts *TokenStore) lookupInternal(ctx context.Context, id string, salted, tai
|
||||
}
|
||||
}
|
||||
|
||||
// don't check for lease
|
||||
if entry.Type == logical.TokenTypeEnt {
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
le, err := ts.expiration.FetchLeaseTimesByToken(ctx, entry)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch lease times: %w", err)
|
||||
@ -2266,6 +2276,11 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data
|
||||
return fmt.Errorf("failed to fetch cubbyhole storage keys: %w", err)
|
||||
}
|
||||
|
||||
err = ts.handleTidyEnterpriseTokens(quitCtx, ns, tidyErrors)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var countParentEntries, deletedCountParentEntries, countParentList, deletedCountParentList int64
|
||||
|
||||
// Scan through the secondary index entries; if there is an entry
|
||||
@ -3321,6 +3336,9 @@ func (ts *TokenStore) handleRevokeTree(ctx context.Context, req *logical.Request
|
||||
}
|
||||
|
||||
func (ts *TokenStore) revokeCommon(ctx context.Context, req *logical.Request, data *framework.FieldData, id string) (*logical.Response, error) {
|
||||
if IsEnterpriseToken(id) {
|
||||
return logical.ErrorResponse("cannot revoke ent token"), nil
|
||||
}
|
||||
te, err := ts.Lookup(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -3365,6 +3383,10 @@ func (ts *TokenStore) handleRevokeOrphan(ctx context.Context, req *logical.Reque
|
||||
return logical.ErrorResponse("missing token ID"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
if IsEnterpriseToken(id) {
|
||||
return logical.ErrorResponse("enterprise token cannot be revoked"), nil
|
||||
}
|
||||
|
||||
// Do a lookup. Among other things, that will ensure that this is either
|
||||
// running in the same namespace or a parent.
|
||||
te, err := ts.Lookup(ctx, id)
|
||||
@ -3402,7 +3424,9 @@ func (ts *TokenStore) handleLookup(ctx context.Context, req *logical.Request, da
|
||||
if id == "" {
|
||||
return logical.ErrorResponse("missing token ID"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
if IsEnterpriseToken(id) {
|
||||
id = getEnterpriseTokenId(req.EnterpriseTokenMetadata)
|
||||
}
|
||||
lock := locksutil.LockForKey(ts.tokenLocks, id)
|
||||
lock.RLock()
|
||||
defer lock.RUnlock()
|
||||
@ -3514,6 +3538,9 @@ func (ts *TokenStore) handleRenew(ctx context.Context, req *logical.Request, dat
|
||||
if id == "" {
|
||||
return logical.ErrorResponse("missing token ID"), logical.ErrInvalidRequest
|
||||
}
|
||||
if IsEnterpriseToken(id) {
|
||||
return logical.ErrorResponse("enterprise tokens cannot be renewed"), nil
|
||||
}
|
||||
incrementRaw := data.Get("increment").(int)
|
||||
|
||||
// Convert the increment
|
||||
|
||||
21
vault/token_store_ce.go
Normal file
21
vault/token_store_ce.go
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright IBM Corp. 2016, 2025
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
//go:build !enterprise
|
||||
|
||||
package vault
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/vault/helper/namespace"
|
||||
)
|
||||
|
||||
func getEnterpriseTokenId(_ string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (ts *TokenStore) handleTidyEnterpriseTokens(_ context.Context, _ *namespace.Namespace, _ *multierror.Error) error {
|
||||
return nil
|
||||
}
|
||||
@ -199,6 +199,9 @@ func (c *Core) IsNewInstall(ctx context.Context) bool {
|
||||
return oldestVersion == "" && newestVersion == ""
|
||||
}
|
||||
|
||||
// IsJWT validates if a token is of JWT format, which was historically
|
||||
// a possibility for wrapping tokens, though not something we
|
||||
// encourage today.
|
||||
func IsJWT(token string) bool {
|
||||
return len(token) > 3 && strings.Count(token, ".") == 2 &&
|
||||
(token[3] != '.' && token[1] != '.')
|
||||
|
||||
10
vault/version_store_ce.go
Normal file
10
vault/version_store_ce.go
Normal file
@ -0,0 +1,10 @@
|
||||
// Copyright IBM Corp. 2016, 2025
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
//go:build !enterprise
|
||||
|
||||
package vault
|
||||
|
||||
func IsEnterpriseToken(token string) bool {
|
||||
return false
|
||||
}
|
||||
@ -348,8 +348,7 @@ DONELISTHANDLING:
|
||||
}
|
||||
|
||||
// validateWrappingToken checks whether a token is a wrapping token. The passed
|
||||
// in logical request will be updated if the wrapping token was provided within
|
||||
// a JWT token.
|
||||
// in logical request may be updated.
|
||||
func (c *Core) validateWrappingToken(ctx context.Context, req *logical.Request) (valid bool, err error) {
|
||||
if req == nil {
|
||||
return false, fmt.Errorf("invalid request")
|
||||
@ -409,13 +408,8 @@ func (c *Core) validateWrappingToken(ctx context.Context, req *logical.Request)
|
||||
}
|
||||
|
||||
// Check for it being a JWT. If it is, and it is valid, we extract the
|
||||
// internal client token from it and use that during lookup. The second
|
||||
// check is a quick check to verify that we don't consider a namespaced
|
||||
// token to be a JWT -- namespaced tokens have two dots too, but Vault
|
||||
// token types (for now at least) begin with a letter representing a type
|
||||
// and then a dot.
|
||||
// internal client token from it and use that during lookup.
|
||||
if IsJWT(token) {
|
||||
// Implement the jose library way
|
||||
parsedJWT, err := jwt.ParseSigned(token)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("wrapping token could not be parsed: %w", err)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user