Skip to content

[PM-29129] Add Policy Update Event README#7159

Open
JimmyVo16 wants to merge 9 commits intomainfrom
ac/pm-29129/add-policy-update-event-readme
Open

[PM-29129] Add Policy Update Event README#7159
JimmyVo16 wants to merge 9 commits intomainfrom
ac/pm-29129/add-policy-update-event-readme

Conversation

@JimmyVo16
Copy link
Contributor

🎟️ Tracking

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

📔 Objective

  1. Add README so devs and AI can easily implement new handlers to hook into Policy Update Events.

📸 Screenshots

No code changes, just a markdown file.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Logo
Checkmarx One – Scan Summary & Details6c51acc4-47cc-47a7-b04e-c5e4f515d636


New Issues (3) Checkmarx found the following issues in this Pull Request
# Severity Issue Source File / Package Checkmarx Insight
1 MEDIUM CSRF /src/Api/Auth/Controllers/AccountsController.cs: 217
detailsMethod at line 217 of /src/Api/Auth/Controllers/AccountsController.cs gets a parameter from a user request from model. This parameter value flow...
Attack Vector
2 MEDIUM CSRF /src/Api/Auth/Controllers/AccountsController.cs: 291
detailsMethod at line 291 of /src/Api/Auth/Controllers/AccountsController.cs gets a parameter from a user request from model. This parameter value flow...
Attack Vector
3 MEDIUM CSRF /src/Api/Auth/Controllers/AccountsController.cs: 452
detailsMethod at line 452 of /src/Api/Auth/Controllers/AccountsController.cs gets a parameter from a user request from model. This parameter value flow...
Attack Vector

Fixed Issues (3) Great job! The following issues were fixed in this Pull Request
Severity Issue Source File / Package
MEDIUM CSRF /src/Api/KeyManagement/Controllers/AccountsKeyManagementController.cs: 146
MEDIUM CSRF /src/Api/AdminConsole/Controllers/OrganizationUsersController.cs: 531
MEDIUM CSRF /src/Api/KeyManagement/Controllers/AccountsKeyManagementController.cs: 105

@codecov
Copy link

codecov bot commented Mar 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 56.87%. Comparing base (fa5fde5) to head (65b8b6a).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #7159   +/-   ##
=======================================
  Coverage   56.87%   56.87%           
=======================================
  Files        2028     2028           
  Lines       88827    88827           
  Branches     7917     7917           
=======================================
+ Hits        50523    50524    +1     
+ Misses      36472    36471    -1     
  Partials     1832     1832           

☔ View full report in Codecov by Sentry.
📢 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.

@JimmyVo16 JimmyVo16 self-assigned this Mar 6, 2026
@JimmyVo16 JimmyVo16 marked this pull request as ready for review March 6, 2026 16:42
@JimmyVo16 JimmyVo16 requested a review from a team as a code owner March 6, 2026 16:42
@JimmyVo16 JimmyVo16 requested a review from eliykat March 6, 2026 16:42
@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 6, 2026

Copy link
Member

@eliykat eliykat left a comment

Choose a reason for hiding this comment

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

Thanks for coming back to this!

Comment on lines +11 to +13
When an organization policy is created or updated, the save workflow runs a series of ordered steps. Each step acts like a hook that a handler may listen to by implementing the particular policy event interface.

Note: If you don’t want to hook into these events, you don’t need to create a handler, and your policy will simply upsert to the database with log events.
Copy link
Member

Choose a reason for hiding this comment

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

I think the terminology could be clearer here: hook, handler, event, etc.

I suggest:

  • the policy getting saved is the event
  • the class that does something is a handler

Therefore: IEnforceDependentPoliciesHandler, IPolicyValidationHandler, etc.

The current wording makes it sound like the class is the event. But I think the class is the thing that handles the event (which is also how you describe it here).


```
SaveAsync()
├─ 1. Validate org can use policies
Copy link
Member

Choose a reason for hiding this comment

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

Avoid contractions:

Suggested change
├─ 1. Validate org can use policies
├─ 1. Validate organization can use policies

Comment on lines +29 to +34
## Limitations

1. We don't have a way to keep this whole process idempotent, so if there is an exception at any point that is not being handled, the state will stay where the process failed.



Copy link
Member

Choose a reason for hiding this comment

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

Section appears to be unfinished? Also, this is a good callout, but I think it's more about it not being atomic than idempotent. A failure does not roll back any changes already made.

Comment on lines +39 to +50
### `IPolicyUpdateEvent`

The base interface that all policy event handlers must implement.

```csharp
public interface IPolicyUpdateEvent
{
PolicyType Type { get; }
}
```

Every handler declares which `PolicyType` it handles via `Type`. All other event interfaces extend this one.
Copy link
Member

Choose a reason for hiding this comment

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

I suggest this can be omitted given that no other team will ever have to implement this directly.

Comment on lines +65 to +66
- **Enabling**: Each `PolicyType` in `RequiredPolicies` must already be enabled, otherwise a `BadRequestException` is thrown.
- **Disabling a required policy**: If any other policy has this policy listed as a requirement and is currently enabled, the disable action is blocked.
Copy link
Member

Choose a reason for hiding this comment

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

Suggestion: these lines are not required, you've already explained it above.


1. Create a class in `PolicyValidators/` implementing `IPolicyUpdateEvent` and any combination of the event interfaces above.
2. Set `Type` to the appropriate `PolicyType`.
3. Register the class as `IPolicyUpdateEvent` (and the legacy interfaces if needed) in `PolicyServiceCollectionExtensions.AddPolicyUpdateEvents()`.
Copy link
Member

Choose a reason for hiding this comment

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

Are we still using the legacy interfaces - is there any need to register them?


### Example

`AutomaticUserConfirmationPolicyEventHandler` is a good reference. It requires `SingleOrg`, validates org compliance before enabling, and removes emergency access grants as a pre-save side effect.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
`AutomaticUserConfirmationPolicyEventHandler` is a good reference. It requires `SingleOrg`, validates org compliance before enabling, and removes emergency access grants as a pre-save side effect.
`AutomaticUserConfirmationPolicyEventHandler` is a good reference. It requires `SingleOrg`, validates organization compliance before enabling, and removes emergency access grants as a pre-save side effect.

Comment on lines +204 to +225
## Adding a New Event Interface

Use this when the existing interfaces don't cover your use case and you need a new hook in the save workflow.

### Step 1: Define the interface in `PolicyUpdateEvents/Interfaces/`:

```csharp
public interface IMyNewEvent : IPolicyUpdateEvent
{
Task ExecuteMyNewEventAsync(SavePolicyModel policyRequest, Policy? currentPolicy);
}
```

It must extend `IPolicyUpdateEvent`.

### Step 2: Add a step to `SavePolicyCommand.SaveAsync()` or `VNextSavePolicyCommand.SaveAsync()` during transition

1. Call your method at the appropriate position in the workflow
2. You can use the existing `ExecutePolicyEventAsync<T>` helper or have your method use `policyEventHandlerFactory` directly to retrieve the handlers.
3. **Note on cross-policy logic:** `IEnforceDependentPoliciesEvent` is a special case. It scans *all* registered handlers (not just the targeted policy's handler) to find dependents when disabling a policy. If your new interface requires similar cross-policy scanning, you will need to add that logic directly to `SavePolicyCommand` or `VNextSavePolicyCommand.SaveAsync()` during transition rather than using `ExecutePolicyEventAsync<T>`.

### Step 3: Document the interface in the [Interfaces](#interfaces) section of this README and add it to the workflow diagram.
Copy link
Member

Choose a reason for hiding this comment

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

I don't expect this to happen often if ever - the current interfaces are fairly comprehensive. I think we could omit this just so the documentation stays focused on adding new policy types.


---

# IPolicyValidator (Legacy)
Copy link
Member

Choose a reason for hiding this comment

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

This has been deprecated for a while. When can we get rid of it?

Comment on lines +3 to +5
This is the policy update pattern that we want our system’s end state to follow.
This directory contains the interfaces and infrastructure for the policy save workflow used by `IVNextSavePolicyCommand`.
Currently, we’re using `IVNextSavePolicyCommand` to transition from the old `IPolicyValidator` pattern.
Copy link
Member

@eliykat eliykat Mar 7, 2026

Choose a reason for hiding this comment

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

You have a separate section at the end to discuss the old pattern. I suggest this intro paragraph can just be focused on the new pattern.

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