Skip to content

[PM-40336] Access Rules: domain, persistence & schema#7981

Draft
Hinton wants to merge 4 commits into
mainfrom
pam/access-rule
Draft

[PM-40336] Access Rules: domain, persistence & schema#7981
Hinton wants to merge 4 commits into
mainfrom
pam/access-rule

Conversation

@Hinton

@Hinton Hinton commented Jul 14, 2026

Copy link
Copy Markdown
Member

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-40336

📔 Objective

Add the domain and persistence layer for org-scoped PAM Access Rules — the
reusable policy objects that gate PAM credential leasing. This is the first of two
stacked PRs; the commercial CRUD API that consumes this layer follows in #7983
(stacked on this branch).

Access-rule data (name, description, conditions) is organization configuration
metadata, not Vault Data — no zero-knowledge surface is touched. Nothing here is
reachable at runtime until the API lands and the pm-37044-pam-v-0
(FeatureFlagKeys.Pam) flag is enabled.

Builds on the Data base library extracted in #7976 (already merged to main);
Pam.Domain references it for ITableObject/IRepository/CombGuid. The
dependency arrow points inward — Core does not reference Pam.Domain;
infrastructure and the Pam service depend on it directly.

What's included

Domain — new Pam.Domain library (src/Pam.Domain)

  • AccessRule entity: org-scoped rule with lease semantics — SingleActiveLease,
    DefaultLeaseDurationSeconds, MaxLeaseDurationSeconds, Enabled,
    AllowsExtensions / MaxExtensionDurationSeconds, and LastEditedBy.
  • Conditions stored as a JSON AccessCondition tree (validated in [PM-40336] Access Rules: API #7983).
  • AccessRuleDetails (rule + governed collection IDs), IAccessRuleRepository,
    and the AccessAuditEventData / AccessAuditEventKind / AccessAuditEventPhase
    audit contract.
  • IAccessAuditEventEmitter audit-write seam with a Noop default — both in
    Pam.Domain (the commercial implementation in the Pam service persists to the
    PAM audit store).

Persistence

  • Dapper (Infrastructure.Dapper) and EF Core (Infrastructure.EntityFramework)
    AccessRuleRepository implementations, registered in DI.
  • SSDT: AccessRule table + stored procedures (_Create, _Update, _DeleteById,
    _ReadById, _ReadByOrganizationId, _ReadDetailsById,
    _ReadDetailsByOrganizationId, Collection_SetAccessRuleAssociations).
  • MSSQL migration (2026-07-14_00_AddAccessRule.sql) and EF migrations +
    snapshots for Postgres/MySQL/SQLite.
  • Repository integration tests.

Collection association

  • Collection.AccessRuleId FK (null = leasing disabled for that collection);
    updated Collection_* stored procedures and the Collection table.
    (Touches AdminConsole-owned files — flagging for that team's review.)

📸 Screenshots

N/A — backend only.

Comment thread bitwarden_license/src/Services/Pam/Services/AccessRuleValidator.cs Fixed
Comment thread src/Infrastructure.Dapper/Pam/Repositories/AccessRuleRepository.cs Dismissed
Comment thread bitwarden_license/src/Services/Pam/Services/AccessRuleValidator.cs Fixed
Comment thread src/Infrastructure.EntityFramework/Pam/Models/AccessRule.cs Dismissed
@Hinton Hinton force-pushed the pam/access-rule branch from 93100c7 to 79f7f4f Compare July 14, 2026 17:33
@Hinton Hinton changed the title [PM-40336] Access Rules [PM-40336] Access Rules: domain, persistence & schema Jul 14, 2026
@Hinton Hinton force-pushed the pam/access-rule branch from 79f7f4f to 51d6075 Compare July 14, 2026 17:57
@Hinton Hinton added the t:feature Change Type - Feature Development label Jul 14, 2026
@Hinton

Hinton commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

Reviewed the automated code-quality suggestions. Decisions below:

1. AccessRuleValidator.ValidateIpAllowlist — "use .Where" — declining.
The predicate treats null/whitespace CIDRs as invalid. A .Where(...).FirstOrDefault() rewrite returns null both for "no invalid entry found" and when the offending entry is itself null, so a null CIDR would be silently accepted. Keeping the explicit foreach to preserve correctness.

2. Dapper AccessRuleRepository.GetManyDetailsByOrganizationIdAsync — "use .Where" — declining.
The loop performs a property mutation (rule.CollectionIds = ...) guarded by TryGetValue. Filtering with .Where(r => collectionIdsByRule.ContainsKey(r.Id)) adds a redundant second dictionary lookup, and using .Where to drive a side effect is an anti-pattern. The foreach + TryGetValue is the intended form.

3. AccessRuleValidator.Validate — "use .Select" — optional/acceptable.
conditions.Select(ValidateCondition).FirstOrDefault(r => !r.IsValid) is behavior-preserving (deferred + short-circuits). Purely cosmetic vs. the current early-return foreach; will fold in if this file is retouched.

4. EF AccessRule : Bit.Pam.Entities.AccessRule — "rename to AccessRuleEntity" — declining.
This matches the established EF-model convention across the repo: every EF model shares its domain entity's name and inherits from it (Collection : Core.Entities.Collection, Organization : Core.AdminConsole.Entities.Organization, Event : Core.Entities.Event, and ~30 others). Renaming would make PAM the sole exception.

Net: keeping (1), (2), and (4) as-is by design; (3) is an optional cosmetic tweak.

@Hinton Hinton force-pushed the pam/access-rule branch 2 times, most recently from f5a81b6 to 74777a5 Compare July 14, 2026 18:16
@codecov

codecov Bot commented Jul 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 49.12281% with 116 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.54%. Comparing base (04c886b) to head (268b892).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
...Framework/Pam/Repositories/AccessRuleRepository.cs 51.04% 45 Missing and 2 partials ⚠️
...re.Dapper/Pam/Repositories/AccessRuleRepository.cs 45.76% 31 Missing and 1 partial ⚠️
src/Pam.Domain/Models/AccessRuleDetails.cs 5.26% 18 Missing ⚠️
src/Pam.Domain/Models/AccessAuditEventData.cs 0.00% 17 Missing ⚠️
...structure.EntityFramework/Pam/Models/AccessRule.cs 83.33% 1 Missing ⚠️
...Pam.Domain/Services/NoopAccessAuditEventEmitter.cs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7981      +/-   ##
==========================================
- Coverage   66.58%   66.54%   -0.04%     
==========================================
  Files        2277     2284       +7     
  Lines       99452    99680     +228     
  Branches     8983     8992       +9     
==========================================
+ Hits        66220    66332     +112     
- Misses      30963    31076     +113     
- Partials     2269     2272       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Hinton Hinton force-pushed the pam/access-rule branch from 74777a5 to 268b892 Compare July 14, 2026 19:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t:feature Change Type - Feature Development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant