Skip to content

(A9) [PAM-193] Access Connectors: API - #8607

Merged
patriksvensson merged 12 commits into
mainfrom
pam/access-connectors-api
Oct 19, 2026
Merged

patriksvensson merged 12 commits into
mainfrom
pam/access-connectors-api

Conversation

@patriksvensson

@patriksvensson patriksvensson commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

PAM-193, bundle A9 of the pam/uat → main extraction under PAM-173. Builds on the #8279 scaffold and the rotation persistence from #8584. Originating work on pam/uat: the rotation daemon (#7926) and PAM-276.

📔 Objective

The organizations/{orgId}/access-connectors handlers on main throw NotImplementedException. This PR implements them, so organization admins can register, list, inspect, enable, disable and delete access connectors and assign them to target systems. Each route calls one command or query.

  • Registering creates a dbo.ApiKey row with no service account, scoped to api.pam.rotation, that holds the organization key the client wrapped. The client secret is returned once, and the server never sees the organization key unwrapped.
  • Assignment needs an enabled connector and an automatic target system, both in the route's organization.
  • Deleting uses PamAccessConnector_DeleteById from [PAM-192] PAM Rotation: domain, persistence & schema #8584, which releases the connector's claimed jobs and deletes its assignments and ApiKey row in one transaction.
  • IsConnected comes from the last heartbeat and AccessConnectorOfflineAfter. The details read adds the connector's 50 most recent jobs within the 90-day history window.

Every command writes Attempt and Outcome audit events with kinds already on main. ManageAccessConnectorRequirement still admits only Owners and Admins behind FeatureFlagKeys.PamAccessConnector, and the new AccessConnectorAuthorizationTests checks that through the request pipeline.

The connector-facing /access-connectors/rotation/* group moves to Policies.AccessConnector from #8520 and gets AccessConnectorHeartbeatEndpointFilter, which updates LastHeartbeatDate at most once per HeartbeatMinInterval. Its handlers stay stubs until A12, and connectors can't get tokens until B4b (PAM-291).

PamRotationOptions is bound from globalSettings:pam:rotation, so AddPamServices now takes IConfiguration, a one-line change in src/Api/Startup.cs.

Scope

Every file matches pam/uat or differs only by omission.

  • Pulled forward: PamRotationOptions from A11 (this PR reads two of its settings), and the constructors on PamRotationJobResponseModel and PamRotationAttemptResponseModel, which the details response uses.
  • Left out: the ManageRotation arm of ManageAccessConnectorRequirement and its tests go with B2, so these endpoints stay Owner/Admin-only. The target system, rotation config and job wiring go with A10 to A12.

The OpenAPI routes and schemas don't change. The five connector models get new descriptions from their rewritten doc comments, which also correct the documented client id to access-connector.<ApiKeyId>.

What changed

  • Cut the bold run-in labels, the seven-row route table in A9 and the AddClientProvider code block in B4b.
  • Cut details the diff already shows:
    • the heartbeat filter's 404 and its place in the filter chain
    • the CreateAccessTokenCommand hashing note
    • "with defaults for an unconfigured environment"
    • "each carrying only the attempts that connector made"
    • the sentence on what the provider tests cover
    • "(matched by */Pam)"
    • the client id prefix commit in A9's list of originating work
  • Folded B4b's Auth-owned bullets and lock file paragraph into two sentences, and A9's Scope lists into two bullets.
  • B4b drops from 268 to 189 words, and A9 from 430 to 298.

@abergs abergs added the ai-review Request a Claude code review label Oct 15, 2026
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

This PR implements the organizations/{orgId}/access-connectors admin commands and queries (register, list, details, enable, disable, delete, assign, unassign), adds AccessConnectorHeartbeatEndpointFilter to the connector-facing group under Policies.AccessConnector, and binds PamRotationOptions from globalSettings:pam:rotation in src/Api/Startup.cs. Each command and the details query returns 404 unless the connector, and the target system where one is involved, belongs to the route organization. PamPlanGuard gates register, enable and assign, and assign requires Edit and ViewPassword on every cipher the target system rotates. No new findings met the reporting threshold. One related point on the open thread about disable not releasing jobs: the Pam_AccessConnectors_Disable description in AccessConnectorEndpoints.cs:46-48 says running jobs are released, but SetAccessConnectorStatusCommand doesn't release them. Whichever way that thread is resolved, the description should be updated to match.

@abergs
abergs marked this pull request as ready for review October 16, 2026 11:36
@abergs
abergs requested a review from a team as a code owner October 16, 2026 11:36
@codecov

codecov Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.55224% with 49 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.88%. Comparing base (07aeab1) to head (74b7413).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
...points/Handlers/AccessConnectorEndpointsHandler.cs 33.33% 28 Missing ⚠️
...s/Response/RegisterAccessConnectorResponseModel.cs 0.00% 9 Missing ⚠️
...onnector/Api/Endpoints/AccessConnectorEndpoints.cs 54.54% 5 Missing ⚠️
...cessConnector/Queries/ListAccessConnectorsQuery.cs 79.16% 5 Missing ⚠️
.../Response/PamAccessConnectorDetailResponseModel.cs 66.66% 0 Missing and 1 partial ⚠️
...ssConnector/Queries/CanEditRotationCiphersQuery.cs 96.96% 0 Missing and 1 partial ⚠️

❗ There is a different number of reports uploaded between BASE (07aeab1) and HEAD (74b7413). Click for more details.

HEAD has 1 upload less than BASE
Flag BASE (07aeab1) HEAD (74b7413)
2 1
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8607      +/-   ##
==========================================
- Coverage   73.07%   66.88%   -6.19%     
==========================================
  Files        2647     2661      +14     
  Lines      113218   113707     +489     
  Branches    10277    10307      +30     
==========================================
- Hits        82730    76049    -6681     
- Misses      27888    35164    +7276     
+ Partials     2600     2494     -106     

☔ 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.

@abergs abergs added the t:feature Change Type - Feature Development label Oct 16, 2026
abergs
abergs previously approved these changes Oct 17, 2026

@abergs abergs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approved

@abergs
abergs enabled auto-merge (squash) October 17, 2026 04:40
public async Task AssignAsync(Guid organizationId, Guid actingUserId, Guid accessConnectorId, Guid targetSystemId)
{
var accessConnector = await _accessConnectorRepository.GetByIdAsync(accessConnectorId);
if (accessConnector is null || accessConnector.OrganizationId != organizationId)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
if (accessConnector is null || accessConnector.OrganizationId != organizationId)
if (accessConnector?.OrganizationId != organizationId)

{
private const string AccessConnectorScope = "[\"api.pam.rotation\"]";
private const int ClientSecretLength = 30;
// dbo.ApiKey.Name is VARCHAR, so a non-Latin connector name would not survive; the name lives on the connector.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What's up with this comment?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think we use NVARCHAR, so we drop certain chars.

}

var now = _timeProvider.GetUtcNow().UtcDateTime;
var assignments = await _accessConnectorRepository.GetAssignmentsByOrganizationIdAsync(organizationId);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These queries are quite inffective in that we do a filter in c# and not the db.

@Hinton Hinton Oct 18, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Possibly blocking: @abergs Disabling an access connector doesn't release it's current jobs.

Comment thread src/Api/Startup.cs
services.AddCommercialSecretsManagerServices();
services.AddSecretsManagerEfRepositories();
services.AddPamServices();
services.Configure<PamRotationOptions>(Configuration.GetSection("globalSettings:pam:rotation"));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this be here or in AddPamServices?

@Hinton
Hinton disabled auto-merge October 18, 2026 23:41
Hinton
Hinton previously approved these changes Oct 18, 2026

@Hinton Hinton left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Disabled auto merge, decide what to do with the comments and either fix it before merge or file issues so we resolve it later.

…gisterAccessConnectorCommand.cs

Co-authored-by: Oscar Hinton <Hinton@users.noreply.github.com>
@abergs
abergs dismissed stale reviews from Hinton and themself via 74b7413 October 19, 2026 15:37
@patriksvensson
patriksvensson merged commit 2834574 into main Oct 19, 2026
51 checks passed
@patriksvensson
patriksvensson deleted the pam/access-connectors-api branch October 19, 2026 16:46
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.

3 participants