mirror of
https://github.com/openziti/ziti.git
synced 2026-10-26 21:47:05 +00:00
ff619272ba
The router's CheckConnections reaper enforced only JWT expiry, so a revoked OIDC api-session, or a disabled/deleted identity, kept its live circuits and hosted terminators until the access token expired. The router now enforces the RouterDataModel revocations directly, tightening access-loss propagation to the reaper interval. - adds a Type field to DataState_Revocation and the raft Revocation command proto, mirroring rest_model.RevocationTypeEnum (API_SESSION/IDENTITY/JTI) so the management API, OIDC producers, sync, and router enforcement share one vocabulary; the common.RevocationType* constants are compile-time bound to the enum to prevent drift - adds an IssuedBefore cutoff so an identity revocation invalidates only sessions issued before it; a session re-authenticated after the cutoff survives the still-lingering revocation. Persists IssuedBefore on the db and model Revocation and carries it (plus the Type) through the single and batched raft marshalling - adds RouterDataModel.IsApiSessionRevoked and IsIdentityRevoked and enforces both in CheckConnections, closing a revoked session's connections - revokes a deleted or disabled identity's live OIDC sessions via an IdentityRevocationConstraint in the db package, run as a store pre-commit constraint so the revocation is written in the same transaction as the identity change and cannot be skipped (self-contained OIDC JWTs aren't otherwise reachable). NewIdentityManager installs it with the revocation type and lifetime - has the OIDC end-session (TerminateSessionFromRequest) revoke the specific api-session named by the z_asid claim, with an identity-scoped fallback; sets IssuedBefore on the identity fallback and the management revocation API; adds RevocationManager.CreateOrReplace, routed through by both the OIDC paths and the management revocation API, so a repeat logout/termination/revocation refreshes the cutoff rather than colliding on the reused id. Expiry derives from the longest configured token duration via a shared common.MaxTokenDuration helper - adds tests/revocation_enforcement_oidc_test.go covering api-session revocation (and a fresh session staying unaffected), identity disable and delete, and the identity cutoff (a post-cutoff session surviving the lingering revocation)
86 lines
2.5 KiB
Go
86 lines
2.5 KiB
Go
/*
|
|
Copyright NetFoundry Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
https://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package db
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/openziti/ziti/v2/controller/storage/ast"
|
|
"github.com/openziti/ziti/v2/controller/storage/boltz"
|
|
)
|
|
|
|
const (
|
|
FieldRevocationExpiresAt = "expiresAt"
|
|
FieldRevocationType = "type"
|
|
FieldRevocationIssuedBefore = "issuedBefore"
|
|
)
|
|
|
|
type Revocation struct {
|
|
boltz.BaseExtEntity
|
|
ExpiresAt time.Time `json:"expiresAt"`
|
|
Type string `json:"type"`
|
|
IssuedBefore time.Time `json:"issuedBefore"`
|
|
}
|
|
|
|
func (r Revocation) GetEntityType() string {
|
|
return EntityTypeRevocations
|
|
}
|
|
|
|
var _ RevocationStore = (*revocationStoreImpl)(nil)
|
|
|
|
type RevocationStore interface {
|
|
Store[*Revocation]
|
|
}
|
|
|
|
func newRevocationStore(stores *stores) *revocationStoreImpl {
|
|
store := &revocationStoreImpl{}
|
|
store.baseStore = newBaseStore[*Revocation](stores, store)
|
|
store.InitImpl(store)
|
|
return store
|
|
}
|
|
|
|
type revocationStoreImpl struct {
|
|
*baseStore[*Revocation]
|
|
}
|
|
|
|
func (store *revocationStoreImpl) initializeLocal() {
|
|
store.AddExtEntitySymbols()
|
|
store.AddSymbol(FieldRevocationExpiresAt, ast.NodeTypeDatetime)
|
|
store.AddSymbol(FieldRevocationType, ast.NodeTypeString)
|
|
store.AddSymbol(FieldRevocationIssuedBefore, ast.NodeTypeDatetime)
|
|
}
|
|
|
|
func (store *revocationStoreImpl) initializeLinked() {}
|
|
|
|
func (store *revocationStoreImpl) NewEntity() *Revocation {
|
|
return &Revocation{}
|
|
}
|
|
|
|
func (store *revocationStoreImpl) FillEntity(entity *Revocation, bucket *boltz.TypedBucket) {
|
|
entity.LoadBaseValues(bucket)
|
|
entity.ExpiresAt = bucket.GetTimeOrError(FieldRevocationExpiresAt)
|
|
entity.Type = bucket.GetStringWithDefault(FieldRevocationType, "")
|
|
entity.IssuedBefore = bucket.GetTimeOrDefault(FieldRevocationIssuedBefore, time.Time{})
|
|
}
|
|
|
|
func (store *revocationStoreImpl) PersistEntity(entity *Revocation, ctx *boltz.PersistContext) {
|
|
entity.SetBaseValues(ctx)
|
|
ctx.SetTimeP(FieldRevocationExpiresAt, &entity.ExpiresAt)
|
|
ctx.SetString(FieldRevocationType, entity.Type)
|
|
ctx.SetTimeP(FieldRevocationIssuedBefore, &entity.IssuedBefore)
|
|
}
|