requestguard

package
v0.0.0-...-1b2c9a0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 19, 2026 License: LGPL-2.1 Imports: 17 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ResourceScopeTenant  = ResourceScope("tenant")
	ResourceScopeOrg     = ResourceScope("org")
	ResourceScopeOrgUnit = ResourceScope("orgunit")
	ResourceScopePrivate = ResourceScope("private")
)
View Source
const Wildcard = "*"

Wildcard is the token that matches any action or any resource in an entitlement expression.

Variables

This section is empty.

Functions

func AssertPermission

func AssertPermission(ctx corectx.Context, requiredPerm Perm) *ft.ClientErrors

AssertPermission answers whether the caller may perform requiredPerm.

It holds no rules of its own: it asks CandidateExpressions which stored expressions would answer, and checks whether the caller holds any of them. The SQL matcher and the permission probe ask the same function, which is what keeps the three of them from drifting into disagreeing about the same question.

func BuildExpression

func BuildExpression(actionCode string, resourceCode string, scope ResourceScope, scopeId *model.Id) string

An entitlement expression has the shape `{action}:{resource}:{scope}[/{scopeId}]` where action and resource may be the wildcard `*`, and the scope segment carries an optional id for the org / orgunit scopes.

BuildExpression is the ONLY producer of expressions in the codebase: the entitlement model persists what it returns, the guard matches against what it returns, and the SQL matcher builds its IN-list from it. Any second implementation is a defect - the two sides silently drift apart (see plan D1/D4).

func CandidateExpressions

func CandidateExpressions(required Perm, evalCtx EvalContext) []string

CandidateExpressions returns every stored expression that would satisfy the required permission for this caller. Holding ANY of them means "allowed".

This is the single definition of the evaluation semantics. The in-memory guard tests set membership against it; the SQL matcher turns it into an IN-list; the permission probe range-scans the cache with it. Because all three consume the same slice they cannot disagree - which is the whole point, since a permission system whose "can I?" and "may I?" answers differ is a security defect, not an inconsistency.

Scope widening runs tenant > org > orgunit: a wider grant satisfies a narrower requirement. There is deliberately NO inheritance between org units - a grant on a parent unit does not reach its children, which is the documented entitlement semantics and what makes a unit grant auditable.

func IsKnownScope

func IsKnownScope(scope ResourceScope) bool

IsKnownScope reports whether the scope is one this system evaluates.

func OmnipotentExpression

func OmnipotentExpression() string

OmnipotentExpression grants everything, everywhere. It is the only expression whose scope segment is a wildcard.

Types

type EvalContext

type EvalContext struct {
	// Orgs the caller belongs to.
	UserOrgIds []model.Id
	// The org unit the caller belongs to, if any.
	OrgUnitId *model.Id
	// The org that OrgUnitId belongs to. Needed for the orgunit -> org fallback.
	OrgUnitOrgId *model.Id
}

EvalContext is what the evaluator knows about the *caller*, as opposed to Perm which describes the *record* being reached for. Both sides are needed: a bare `org` grant only answers when the caller is a member of the record's org.

func EvalContextFrom

func EvalContextFrom(userPerm corectx.ContextPermissions) EvalContext

EvalContextFrom lifts the caller's org and unit membership out of the request context, so callers of CandidateExpressions do not each reach into it.

type ExtGetUserEntitlementsQuery

type ExtGetUserEntitlementsQuery struct {
	UserId    *model.Id `json:"user_id"`
	UserEmail *string   `json:"user_email"`
}

func (ExtGetUserEntitlementsQuery) CqrsRequestType

func (ExtGetUserEntitlementsQuery) CqrsRequestType() cqrs.RequestType

type ExtGetUserEntitlementsResultData

type ExtGetUserEntitlementsResultData struct {
	IsOwner      bool                 `json:"is_owner"`
	Entitlements []string             `json:"entitlements"`
	OrgUnitId    *model.Id            `json:"org_unit_id"`
	OrgUnitOrgId *model.Id            `json:"org_unit_org_id"`
	UserId       model.Id             `json:"user_id"`
	UserOrgIds   []model.Id           `json:"user_org_ids"`
	User         dmodel.DynamicFields `json:"user"`
}

type GetUserEntitlementsQuery

type GetUserEntitlementsQuery = ExtGetUserEntitlementsQuery

type GetUserEntitlementsResult

type GetUserEntitlementsResult = dyn.OpResult[GetUserEntitlementsResultData]

type GetUserEntitlementsResultData

type GetUserEntitlementsResultData = ExtGetUserEntitlementsResultData

type ParsedExpression

type ParsedExpression struct {
	ActionCode   string
	ResourceCode string
	Scope        ResourceScope
	ScopeId      *model.Id
}

ParsedExpression is the decomposition of an entitlement expression.

func ParseExpression

func ParseExpression(expr string) (*ParsedExpression, error)

ParseExpression decomposes an entitlement expression. It is strict on purpose: callers hand it untrusted input (the permission probe accepts an expression in a request body), so every malformed shape must come back as a plain error the transport turns into a 400, never a panic.

func (ParsedExpression) HasWildcard

func (this ParsedExpression) HasWildcard() bool

HasWildcard reports whether the expression grants across all actions or all resources. A *question* asked of the permission probe must never have one: a real requirement is always concrete, and a wildcard question would turn the probe into a grant-enumeration tool.

func (ParsedExpression) String

func (this ParsedExpression) String() string

String rebuilds the canonical expression, so that parsing then rebuilding is the identity for every valid input.

type Perm

type Perm struct {
	ResourceCode string
	ActionCode   string
	Scope        ResourceScope
	// This is Org Unit ID to which the resource belongs (if any).
	// If this is not nil, then OrgId must be this Org Unit's Org ID.
	OrgUnitId *model.Id

	// This is the Org ID to which the resource belongs (if any)
	// Or, this can be the Org Unit's Org ID (if the resource belongs to an org unit)
	OrgId *model.Id

	// IsRecordOwnedByCaller answers the private scope: the caller may act on their
	// own record. Callers whose scope is private must set it; leaving it false
	// simply means "not the caller's record", which denies.
	IsRecordOwnedByCaller bool
}

func PermFor

func PermFor(actionCode string, resourceCode string, scope ResourceScope) Perm

PermFor starts a Perm for the given action on the given resource. Use the InOrg / InOrgUnit / OwnedByCaller builders to attach the record's context - an org- or unit-scoped check without it can only ever match an exact or tenant grant, which is how org-scoped checks silently degraded before.

func (Perm) InOrg

func (this Perm) InOrg(orgId *model.Id) Perm

InOrg names the org the record belongs to.

func (Perm) InOrgUnit

func (this Perm) InOrgUnit(orgUnitId *model.Id) Perm

InOrgUnit names the org unit the record belongs to. Pass the unit's org as well via InOrg so that the org-level fallback can apply.

func (Perm) OwnedByCaller

func (this Perm) OwnedByCaller(owned bool) Perm

OwnedByCaller marks the record as the caller's own, which is what a private scope grant is about.

type PermissionContext

type PermissionContext struct {
	IsOwner      bool
	Entitlements []string
}

type RequestGuardService

type RequestGuardService interface {
	// Calculate a fingerprint for the request that can be used to identify the request.
	// This can be used for caching or to prevent replay attacks.
	CalcRequestFingerprint(ctx corectx.Context, request *http.Request) (fingerprint string, err error)
	GetCorsMiddleware(ctx corectx.Context) (echo.MiddlewareFunc, error)
	GetUserEntitlements(ctx corectx.Context, query GetUserEntitlementsQuery) (*GetUserEntitlementsResult, error)
	VerifyJwt(ctx corectx.Context, request *http.Request) (result *VerifyRequestResult, err error)
}

func NewStaticRequestGuardServiceImpl

func NewStaticRequestGuardServiceImpl(params StaticRequestGuardServiceParams) RequestGuardService

type ResourceScope

type ResourceScope string

type StaticRequestGuardServiceImpl

type StaticRequestGuardServiceImpl struct {
	// contains filtered or unexported fields
}

func (*StaticRequestGuardServiceImpl) CalcRequestFingerprint

func (this *StaticRequestGuardServiceImpl) CalcRequestFingerprint(_ corectx.Context, request *http.Request) (fingerprint string, err error)

func (*StaticRequestGuardServiceImpl) GetCorsMiddleware

func (this *StaticRequestGuardServiceImpl) GetCorsMiddleware(_ corectx.Context) (echo.MiddlewareFunc, error)

func (*StaticRequestGuardServiceImpl) GetUserEntitlements

func (*StaticRequestGuardServiceImpl) VerifyJwt

func (*StaticRequestGuardServiceImpl) VerifyJwtDpop

func (this *StaticRequestGuardServiceImpl) VerifyJwtDpop(ctx corectx.Context, request *http.Request) (*VerifyRequestResult, error)

Verify JWT DPoP (OAuth2 Demonstraing Proof of Possession)

func (*StaticRequestGuardServiceImpl) VerifySessionBlacklist

func (this *StaticRequestGuardServiceImpl) VerifySessionBlacklist(ctx corectx.Context, request *http.Request) (*VerifyRequestResult, error)

type StaticRequestGuardServiceParams

type StaticRequestGuardServiceParams struct {
	dig.In

	ConfigSvc config.ConfigService
	CqrsBus   cqrs.CqrsBus
	TokenSvc  coretoken.AuthTokenService
}

type VerifyRequestResult

type VerifyRequestResult struct {
	IsOk        bool
	JwtClaims   jwt.Claims
	ClientError *ft.ClientErrorItem
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL