Skip to content

[PM-40526] Access Leasing: persistence & schema#8002

Draft
Hinton wants to merge 4 commits into
pam/access-leasing-domainfrom
pam/access-leasing-data
Draft

[PM-40526] Access Leasing: persistence & schema#8002
Hinton wants to merge 4 commits into
pam/access-leasing-domainfrom
pam/access-leasing-data

Conversation

@Hinton

@Hinton Hinton commented Jul 17, 2026

Copy link
Copy Markdown
Member

🎟️ Tracking

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

📔 Objective

Persistence & schema for the PAM access-request/lease domain (introduced in #8001), across both ORMs:

  • MSSQL / Dapper — the AccessRequest, AccessDecision, and AccessLease SSDT tables; the 22 AccessRequest_* / AccessLease_* stored procedures; the Dapper AccessRequestRepository and AccessLeaseRepository; repository integration tests; and one consolidated migration (2026-07-16_00_AddAccessRequestAndLease.sql) that folds the POC's incremental scripts into a single create matching the final SSDT shape verbatim.
  • EF Core (PostgreSQL / MySQL / SQLite) — EF entity models + AutoMapper profiles, inline DatabaseContext configuration and DbSets, the EF repositories implementing the domain interfaces with LINQ matching the stored-procedure behaviour, DI registrations, and generated migrations for all three providers.
  • Relocates Collection_SetAccessRuleAssociations to the AdminConsole SSDT folder so collection sprocs stay under the table's owning team (per PR review).

The two race-guarded writes (lease mint, approved extension) use a Serializable transaction as the cross-provider stand-in for the MSSQL procs' UPDLOCK/HOLDLOCK; a losing concurrent writer may surface a provider serialization error rather than a clean outcome enum. Cross-provider parity is covered by the [DatabaseData] integration tests.

Stacked PR — targets pam/access-leasing-domain (#8001). Review/merge that first.

@Hinton
Hinton requested review from a team as code owners July 17, 2026 10:32
@Hinton Hinton added the ai-review Request a Claude code review label Jul 17, 2026
@Hinton
Hinton requested a review from a team as a code owner July 17, 2026 10:32
@Hinton Hinton added the t:feature Change Type - Feature Development label Jul 17, 2026
@Hinton
Hinton requested a review from eliykat July 17, 2026 10:32
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: REQUEST CHANGES

Reviewed the PAM access-request/lease persistence layer across both ORMs: the SSDT tables, 22 stored procedures, the consolidated MSSQL migration, the Dapper and EF Core repositories, the EF DatabaseContext configuration/migrations, and the integration tests. Schema parity between src/Sql/dbo/ and the dated migration is verbatim, and the dual-ORM read/write behaviour is closely mirrored with thorough inline rationale. The concurrency approach (MSSQL UPDLOCK/HOLDLOCK vs. EF Serializable transactions) is well documented, including the caller-visible difference under contention.

One correctness divergence between the two ORMs remains open (see below).

Code Review Details
  • ❌ : EF CreateApprovedExtensionAsync never assigns the audit AccessDecision.AccessRequestId, so it is written with Guid.Empty — diverging from the MSSQL proc (which inserts the decision with the new extension request's Id) and violating the FK_AccessDecision_AccessRequest/EF cascade FK. Unlike CreateAutoApprovedAsync/RevokeAsync, the extension path omits decisionEntity.AccessRequestId = request.Id; the test's BuildAutoDecision also leaves it unset. Already tracked in the open thread on this line.
    • src/Infrastructure.EntityFramework/Pam/Repositories/AccessRequestRepository.cs:1113 (diff)

Comment on lines +376 to +383
var decisionEntity = Mapper.Map<EfDecision>(decision);
decisionEntity.DeciderKind = AccessDeciderKind.Automatic;
decisionEntity.ApproverId = null;
decisionEntity.ConditionKind = null;
decisionEntity.Verdict = AccessDecisionVerdict.Approve;
decisionEntity.Comment = null;
decisionEntity.EvaluationContext = null;
decisionEntity.CreationDate = now;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ IMPORTANT: EF extension decision never sets AccessRequestId, diverging from the MSSQL proc and risking an FK violation.

Details and fix

The MSSQL AccessRequest_CreateApprovedExtension proc inserts the AccessDecision with @AccessRequestId (the new extension request's Id). Here the decision entity is mapped from decision but AccessRequestId is never assigned — unlike CreateAutoApprovedAsync (line 39: decisionEntity.AccessRequestId = request.Id).

The integration test's BuildAutoDecision leaves AccessRequestId at Guid.Empty, so on the EF providers the decision row gets AccessRequestId = Guid.Empty, which references no AccessRequest and violates FK_AccessDecision_AccessRequest at SaveChangesAsync. Unlike the mint path, this method has no DbUpdateException catch, so the extension throws on EF while succeeding on MSSQL — a cross-provider parity break.

var decisionEntity = Mapper.Map<EfDecision>(decision);
decisionEntity.AccessRequestId = requestEntity.Id;
decisionEntity.DeciderKind = AccessDeciderKind.Automatic;

@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 6.84411% with 735 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.73%. Comparing base (3c540d8) to head (b8f1900).

Files with missing lines Patch % Lines
...mework/Pam/Repositories/AccessRequestRepository.cs 0.00% 319 Missing ⚠️
...Dapper/Pam/Repositories/AccessRequestRepository.cs 0.00% 180 Missing ⚠️
...ramework/Pam/Repositories/AccessLeaseRepository.cs 0.00% 145 Missing ⚠️
...e.Dapper/Pam/Repositories/AccessLeaseRepository.cs 0.00% 87 Missing ⚠️
...ucture.Dapper/DapperServiceCollectionExtensions.cs 0.00% 2 Missing ⚠️
...tructure.EntityFramework/Pam/Models/AccessLease.cs 80.00% 1 Missing ⚠️
...ucture.EntityFramework/Pam/Models/AccessRequest.cs 83.33% 1 Missing ⚠️
Additional details and impacted files
@@                      Coverage Diff                      @@
##           pam/access-leasing-domain    #8002      +/-   ##
=============================================================
- Coverage                      66.68%   61.73%   -4.96%     
=============================================================
  Files                           2313     2320       +7     
  Lines                         100411   101200     +789     
  Branches                        9068     9120      +52     
=============================================================
- Hits                           66959    62475    -4484     
- Misses                         31172    36544    +5372     
+ Partials                        2280     2181      -99     

☔ 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 marked this pull request as draft July 17, 2026 11:20
@Hinton
Hinton force-pushed the pam/access-leasing-data branch from 49577aa to 6124c0d Compare July 17, 2026 13:36
@Hinton
Hinton force-pushed the pam/access-leasing-domain branch from d15334c to b7eebb4 Compare July 17, 2026 13:36
@Hinton
Hinton removed request for a team and eliykat July 17, 2026 13:43
Hinton and others added 3 commits July 17, 2026 15:44
…Lease

Extract the MSSQL/Dapper persistence for the PAM access-request/lease flow:
the AccessRequest, AccessDecision, and AccessLease SSDT tables; the 22
AccessRequest_*/AccessLease_* stored procedures; the Dapper AccessRequestRepository
and AccessLeaseRepository (registered in DapperServiceCollectionExtensions); the
repository integration tests; and one consolidated migration
(2026-07-16_00_AddAccessRequestAndLease.sql) that folds poc's incremental scripts
into a single create, matching the final SSDT shape verbatim.

EF Core parity for the non-MSSQL providers lands in the next commit.
Author from scratch the non-MSSQL (PostgreSQL/MySQL/SQLite) persistence for the
PAM access-request/lease flow, since the POC was Dapper/MSSQL-only. Adds the EF
entity models + AutoMapper profiles, inline DatabaseContext configuration and
DbSets, the EF AccessRequestRepository and AccessLeaseRepository implementing the
domain interfaces with LINQ matching the stored-procedure behaviour, the DI
registrations, and generated migrations for all three providers
(AddAccessRequestAndLease, matching the MSSQL migration name).

The two race-guarded writes (lease mint, approved extension) use a Serializable
transaction as the cross-provider stand-in for the MSSQL procs' UPDLOCK/HOLDLOCK;
a losing concurrent writer may surface a provider serialization error rather than
a clean outcome enum. Cross-provider parity is covered by the [DatabaseData]
integration tests.
@Hinton
Hinton force-pushed the pam/access-leasing-data branch from 6124c0d to b9fe7d1 Compare July 17, 2026 13:45
@Hinton
Hinton force-pushed the pam/access-leasing-domain branch from b7eebb4 to 3c540d8 Compare July 17, 2026 13:45
Comment on lines +217 to +223
foreach (var detail in details)
{
if (decisionsByRequest.TryGetValue(detail.Id, out var decisions))
{
detail.Decisions = decisions;
}
}
Comment thread src/Infrastructure.EntityFramework/Pam/Models/AccessDecision.cs Dismissed
Comment thread src/Infrastructure.EntityFramework/Pam/Models/AccessLease.cs Dismissed
Comment thread src/Infrastructure.EntityFramework/Pam/Models/AccessRequest.cs Dismissed
…AM tests

CoreHelpers.GenerateComb() is obsolete; PAM code standardizes on
Bit.Core.Utilities.CombGuid.Generate() (already used by the domain entities).
@Hinton
Hinton force-pushed the pam/access-leasing-data branch from a615a81 to b8f1900 Compare July 17, 2026 14:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Request a Claude code review t:feature Change Type - Feature Development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant