VAULT-42657: Merge feature branch for OAuth and Agent Registry into main (#12587) (#12754)

* 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:
Vault Automation 2026-03-05 10:25:59 -05:00 committed by GitHub
parent b25410c747
commit f5dbe55f55
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 219 additions and 32 deletions

View 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"
}

View File

@ -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:""`

View File

@ -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
View File

@ -0,0 +1,10 @@
// Copyright IBM Corp. 2016, 2025
// SPDX-License-Identifier: MPL-2.0
//go:build !enterprise
package logical
type (
EntToken struct{}
)

View File

@ -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.

View File

@ -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
View 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
}

View File

@ -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)
}

View 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")
}

View File

@ -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 {

View File

@ -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
View 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
}

View File

@ -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
View 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
}

View File

@ -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)