Skip to content

Release Kerberos v2 - #1

Merged
Alex-Dolid merged 63 commits into
mainfrom
feature/kerberos-v2
May 31, 2026
Merged

Release Kerberos v2#1
Alex-Dolid merged 63 commits into
mainfrom
feature/kerberos-v2

Conversation

@Alex-Dolid

@Alex-Dolid Alex-Dolid commented May 31, 2026

Copy link
Copy Markdown
Contributor

Version 2 turns Kerberos.js from a resource-policy engine into a full,
Cerbos-style authorization runtime: three policy types, pluggable validation,
pluggable logging, and cache-agnostic dynamic policies backed by an eval-free,
security-first expression codec. The previously "WIP" features (outputs, scopes,
metadata) are now complete.

Breaking change: the package is published as 2.0.0. The core isAllowed
/ checkResources API is backward compatible, but the module layout,
exports, and policy-resolution order have changed (see Changed below).

Added

Policy types

  • PrincipalPolicy — Cerbos-style, principal-specific overrides bound to a
    single principal and targeting resource + action directly.
  • RolePolicy — role-centric allowlist policies bound to a single role,
    targeting resource + allowActions, with parentRoles inheritance (a child
    role keeps only actions also allowed by every locally defined parent role).
  • Mixed policy evaluation — when multiple policy types are loaded, each
    action is resolved in order: PrincipalPolicyRolePolicyResourcePolicy
    → default EFFECT_DENY (with EFFECT_DENY winning ties within the role layer).

Authorization features

  • Outputsoutput.when.ruleActivated / output.when.conditionNotMet
    expressions surfaced in checkResources responses, with a src that reflects
    the producing policy (e.g. resource.expense.vdefault#rule-name).
  • Scopes — hierarchical scope resolution with a most-specific-to-base search
    chain (e.g. acme.corp → acme → '') and scope normalization ('.' ≡ base).
  • Metadata — opt-in via includeMeta: true; exposes matchedPolicy,
    matchedRule, matchedScope, and effectiveDerivedRoles.
  • Constants alongside variables, available in the request context as C.
  • effectAsBoolean option for checkResources to return true/false
    instead of EFFECT_ALLOW/EFFECT_DENY.

Validation (pluggable backends)

  • Optional validation via Zod (z), JSON Schema + Ajv (ajv), or
    TypeBox + Ajv (ajv + typebox).
  • First-class schema builders (JsonSchemas, TypeBoxSchemas, ZodSchemas,
    KerberosJsonSchemas, ResourcePolicyJsonSchemas, PrincipalPolicyJsonSchemas,
    RolePolicyJsonSchemas, …) and helpers createAjvAdapter / registerAjvKeywords
    (custom Ajv keywords so function-bearing DSL fields validate at runtime).

Logging (pluggable)

  • logger: true keeps the legacy console audit flow (group + summary + table
    • debug JSON); a console-like object behaves the same.
  • A structured logger (e.g. Pino) receives one structured audit entry per
    evaluated action.
  • Lifecycle logs: *.start, *.error, *.finish with timing/duration.
  • When logging is enabled, validation/runtime errors are logged and converted to
    fallback results (isAllowed → false, checkResources → { results: [], … })
    instead of being thrown.

Caching / storing dynamic policies

  • Cache-agnostic CacheLike integration: pass any object with get(key)
    (keyv, cacheable, cache-manager, …); static policies stay in memory and are
    always checked first, the cache is only a fallback on a miss.
  • Documented cache-key layout for resource/principal/role/derived-role policies.
  • Safe AST expression codec (createSafeExprCodec, serializePolicy,
    deserializePolicy) built on a user-supplied, pre-configured jsep instance.
    Dynamic policies express conditions/variables/outputs as
    { "$expr": "..." } descriptors evaluated by a strict allowlist interpreter —
    no eval / new Function / fn.toString().
  • Fully pluggable codec: { jsep } (built-in evaluator), { deserialize }
    (custom), or omitted (cached JSON used as-is).

Auditing & request correlation

  • kerberosCallId generated per call (Node crypto.randomUUID, browser
    crypto.randomUUID, or a pseudo UUID v4 fallback) and included in responses
    and logs.
  • Customizable via the getCallId option.
  • reqId propagated through evaluation, responses, and audit entries.

Changed

  • Module layout reorganized: schema builders moved into per-module
    schemas/ folders plus a shared src/schemas/, and validation logic into
    per-module validation/ folders plus a shared src/validation/.
  • Public exports expanded: the package root now also exposes Constants,
    Conditions, Outputs, Variables, PrincipalPolicy, RolePolicy, the
    caching codec helpers, and all schema/validation builders.
  • Conditions now evaluates multiple strategies (all / any / none) in
    a single match object via an O(1) strategy dispatch.
  • Policy selection is now type-aware (resource by kind, principal by id,
    role by each principal.roles[]), each combined with policyVersion
    (default 'default') and the scope chain.

Performance

  • Safe-codec interpreter rewritten around O(1) dispatch tables for node
    types and binary/unary operators (replacing switch statements) on the hot
    expression-evaluation path; per-jsep AST cache via WeakMap.
  • typeof validation keyword reduced to an O(1) strategy lookup.
  • Policy check loops use Set-based role membership lookups and boolean
    effect flags instead of Array.prototype.includes scans; derived-role and
    role-policy resolution deduplicate via Set.

Security

  • Expression evaluation is eval-free and allowlist-based: identifiers
    resolve only against { P, R, V, C } plus curated safe builtins (Math,
    Date, parseInt, parseFloat, Number, String, Boolean, isNaN,
    isFinite); __proto__ / prototype / constructor access is blocked at the
    interpreter level regardless of how it is written.

Added exports for the Constants and Variables modules in index.js to make their contents available to consumers of the package.
Adds logging for denied access attempts when no policy is found for a request in the IsAllowed method. This improves auditability by ensuring all denied requests are logged, not just those with existing policies.
Introduces the KerberosZodSchemas and ZodSchemas classes to allow dynamic construction and injection of Zod schemas for validation. Refactors Kerberos to use these schemas, enabling more flexible and testable validation logic. Updates method signatures and internal logic to support optional schema and Zod instance parameters, and makes isAllowed and checkResources async. This change improves extensibility and decouples schema definitions from the core logic.
Refactored PrincipalMock to use a new PrincipalMockZodSchemas class extending ZodSchemas for schema construction and parsing. Updated constructor and parsing logic to support flexible schema validation. Also updated Kerberos.js to export KerberosZodSchemas.
Replaces direct use of RequestResourceSchema with a ResourceMockZodSchemas class that extends ZodSchemas. Updates ResourceMock to support flexible parsing with schema or zod instance, and encapsulates shape as a private field. This improves schema extensibility and parsing flexibility.
Introduces ZodSchemas-based static parsing and validation methods for Conditions, Constants, DerivedRoles, Variables, KerberosTest, KerberosTests, PrincipalsMock, and ResourcesMock. This change unifies and modernizes schema handling, enabling dynamic Zod schema construction and improved extensibility. Also updates related test and mock classes to use the new parsing approach.
Removed direct zod schema exports and instance schemas from multiple modules, consolidating schema logic into ZodSchemas-based classes. Updated ResourcePolicy to use ResourcePolicyZodSchemas for parsing and validation, and refactored related modules to rely on class-based schema construction. This streamlines schema management and reduces redundant exports.
Removed the 'zod' package from dependencies in package.json and updated the pnpm-lock.yaml to lockfileVersion 9.0. Also set the packageManager field to pnpm@10.10.0 for consistency.
Moved condition strategies to a private field and refactored their implementation to use direct function calls for improved clarity and performance. Simplified condition evaluation by removing the evaluateCondition method and integrating its logic into isFulfilled. Updated schemas to use more concise object definitions for condition strategies.
Introduces a public shape getter to both Conditions and Constants classes for easier access to the parsed shape. Also updates Constants to use a unified parseShape method for consistency.
Replaces schema parsing with a more flexible approach using zod schemas. Adds static parseShape method and updates constructor to handle both schema and zod options. Internal shape is now private and accessed via a getter.
Replaces Object.fromEntries and Object.entries with a for-in loop and hasOwnProperty check for better performance and compatibility.
Simplifies and restructures the role matching process in DerivedRoles.js by removing helper methods and integrating variable and constant population directly into the get() method. Improves clarity and maintainability of role evaluation.
Updated parseConstants, parseVariables, and parseConditions to accept and forward the 'z' option. The constructor now consistently passes 'z' when parsing constants, variables, and conditions, ensuring proper option propagation.
Refactored ResourcePolicy and DerivedRoles to improve parsing of constants, variables, and conditions by passing the zod context. Replaced isAllowed with check in Kerberos for more consistent action/effect evaluation. Updated rule and definition parsing to handle empty arrays and ensure conditions are properly processed with context.
Moved effectAsBoolean logic from Kerberos.js to ResourcePolicy.js, simplifying Kerberos's response parsing and transformation. ResourcePolicy.check now supports effectAsBoolean to return boolean values for effects, improving clarity and maintainability.
Updated Kerberos class methods to consistently pass the 'z' parameter when parsing policies, derived roles, requests, and arguments. Replaced array mapping with for-loops for better readability and control in internal map-building and logging methods. Improved effect handling in logging and resource checking to support boolean results.
Replaced forEach and Object.entries with for-in loops and explicit hasOwnProperty checks in PrincipalsMock, ResourcesMock, and Variables classes. This change ensures only own properties are processed, improving reliability and consistency.
Replaces public properties with private fields in KerberosTest for better encapsulation. Updates constructor and method logic to reference private fields, and improves validation of input parameters in the run method.
Simplifies the construction of PrincipalsMock and ResourcesMock by aggregating mocks into arrays before instantiation. Updates resource and principal lookup logic to use mock class methods directly, improving readability and maintainability.
Replaces public properties with private fields in KerberosTests for better encapsulation. Updates parsing methods to accept an optional 'z' parameter and ensures mocks are constructed with this parameter. Also exports KerberosTestsZodSchemas.
Added 'zod' to package.json for schema validation. Updated KerberosTests and several test files to use zod for stricter schema checks, and improved test coverage for invalid schema scenarios. Refactored ResourcePolicy tests to use the new 'check' method and actions array for effect mapping.
Eliminates Zod-based validation and parsing for checkResources responses in Kerberos.js. The method now directly returns the results array, simplifying the code and removing related schema construction and parsing logic.
Updated Conditions.isFulfilled to handle multiple strategies within a condition object, returning true only if all strategies are fulfilled. Adjusted internal strategy calls and updated related tests to reflect the new logic.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 80 out of 83 changed files in this pull request and generated 2 comments.

Comment thread src/Conditions/Conditions.js
Comment thread src/index.js
Treat condition objects with no strategy keys as non-matching to avoid vacuous true results (e.g. when a validation backend is absent). Add a unit test asserting an empty strategy object does not match. Also export Metadata schema implementations from src/index.js and add corresponding TypeScript declarations (MetadataZodSchemas, MetadataJsonSchemas, MetadataTypeBoxSchemas) to index.d.ts.
Extract the built-in test harness into a dedicated package subpath to keep dev-only code out of the main entry. Add an exports entry for "./tests" and include tests.js/tests.d.ts in files; tests.js re-exports ./src/Tests. Remove the Tests export from the root src index and delete the Tests namespace from index.d.ts. Update tests and fixtures to import from @alexify/kerberos/tests, and update README and CHANGELOG to document the new subpath and rationale (avoid pulling dev-only code into production bundles).
Update package.json packageManager from pnpm@10.10.0 to pnpm@11.5.0 to standardize the pnpm version used for installs. No other package.json changes included.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 88 out of 92 changed files in this pull request and generated 9 comments.

Comment thread src/Tests/Mocks/PrincipalMock.js
Comment thread src/Tests/Mocks/ResourceMock.js
Comment thread src/Tests/KerberosTests.js Outdated
Comment thread src/Tests/KerberosTests.js Outdated
Comment thread src/Conditions/Conditions.js
Comment thread src/ResourcePolicy/ResourcePolicy.js
Comment thread src/PrincipalPolicy/PrincipalPolicy.js
Comment thread src/RolePolicy/RolePolicy.js
Comment thread src/Tests/KerberosTest.js Outdated
Treat the documented base-scope alias '.' as an unset scope in PrincipalPolicy, ResourcePolicy and RolePolicy (return undefined) so Kerberos lookup and metadata normalization are consistent. Refactor KerberosTests to group policies with their tests (#policyGroups) and ensure principals/resources are derived from parsed policies. Improve test utilities and assertions: results are indexed by resource id to validate every expected resource, Zod schema for test actions now accepts Sets or arrays (and normalizes to Set), and several unit tests were added to cover '.' scope behavior, missing-checkResults handling, Zod-enabled construction, and per-policy test isolation.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 88 out of 92 changed files in this pull request and generated 3 comments.

Comment thread src/Conditions/Conditions.js
Comment thread src/caching/codec.js
Comment thread src/ResourcePolicy/schemas/index.js
Add stricter validation for expression descriptors and empty condition arrays. Conditions.js now treats empty any/all/none arrays as non-matching (returns false). caching/codec.js introduces hasExprDescriptorKey, validates that $expr is a string and throws KerberosExprError for non-string values, and updates isExprDescriptor/deepTransform accordingly. index.d.ts adds a Never type. Tests updated/added to cover the new behaviors in Caching.test.js and Conditions.test.js.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 88 out of 92 changed files in this pull request and generated 4 comments.

Comment thread src/Conditions/Conditions.js Outdated
Comment thread src/Conditions/Conditions.js
Comment thread src/Tests/Mocks/PrincipalMock.js
Comment thread src/Tests/Mocks/ResourceMock.js
Refine Conditions.isFulfilled behavior: use undefined as the default condition, treat non-object/null nested leaves as false (fail-closed), ignore unknown strategy keys for forward compatibility, and ensure empty or unknown-only condition sets evaluate to false to avoid vacuous true results or recursion. Add policyVersion and scope getters to PrincipalMock and ResourceMock, and update TypeScript declarations and tests to cover the new behavior.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 88 out of 92 changed files in this pull request and generated 1 comment.

Comment thread src/Tests/KerberosTests.js Outdated
Stop using a single shared principals/resources mock across all policies — instead build per-policy PrincipalsMock/ResourcesMock instances and store them with each policy group. Renamed helpers parsePrincipals/parseResources to buildPrincipalsMock/buildResourcesMock and changed them to return the mock instances. Updated KerberosTests constructor and run() to pass the per-policy mocks into tests (removed global shared mocks). Added a unit test that verifies fixture isolation when principal/resource names overlap across policies.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@Alex-Dolid
Alex-Dolid merged commit d9a98ff into main May 31, 2026
0 of 2 checks passed
@Alex-Dolid
Alex-Dolid deleted the feature/kerberos-v2 branch May 31, 2026 19:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants