-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[PM-30298] Initial documentation for OrganizationAbility pattern #6781
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
30fd0c2
42eb3ee
0db61da
ba6c736
29b335a
27f6081
2e47117
5955464
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| # Organization Ability Flags | ||
|
|
||
| ## Overview | ||
|
|
||
| Many Bitwarden features are tied to specific subscription plans. For example, SCIM and SSO are Enterprise features, | ||
| while Event Logs are available to Teams and Enterprise plans. When developing features that require plan-based access | ||
| control, we use **Organization Ability Flags** (or simply _abilities_) โ explicit boolean properties on the Organization | ||
| entity that indicate whether an organization can use a specific feature. | ||
|
|
||
| ## The Rule | ||
|
|
||
| **Never check plan types to control feature access.** Always use a dedicated ability flag on the Organization entity. | ||
|
|
||
| ### โ Don't Do This | ||
|
|
||
| ```csharp | ||
| // Checking plan type directly | ||
| if (organization.PlanType == PlanType.Enterprise || | ||
| organization.PlanType == PlanType.Teams || | ||
| organization.PlanType == PlanType.Family) | ||
| { | ||
| // allow feature... | ||
| } | ||
| ``` | ||
|
|
||
| ### โ Don't Do This | ||
|
|
||
| ```csharp | ||
| // Piggybacking off another feature's ability | ||
| if (organization.PlanType == PlanType.Enterprise && organization.UseEvents) | ||
| { | ||
| // assume they can use some other feature... | ||
| } | ||
| ``` | ||
|
|
||
| ### โ Do This Instead | ||
|
|
||
| ```csharp | ||
| // Check the explicit ability flag | ||
| if (organization.UseEvents) | ||
| { | ||
| // allow UseEvents feature... | ||
| } | ||
| ``` | ||
|
|
||
| ## Why This Pattern Matters | ||
|
|
||
| Using explicit ability flags instead of plan type checks provides several benefits: | ||
|
|
||
| 1. **Simplicity** โ A single boolean check is cleaner and less error-prone than maintaining lists of plan types. | ||
|
|
||
| 2. **Centralized Control** โ Feature access is managed in one place: the ability assignment during organization | ||
| creation/upgrade. No need to hunt through the codebase for scattered plan type checks. | ||
|
|
||
| 3. **Flexibility** โ Abilities can be set independently of plan type, enabling: | ||
|
|
||
| - Early access programs for features not yet tied to a plan | ||
| - Trial access to help customers evaluate a feature before upgrading | ||
| - Custom arrangements for specific customers | ||
| - A/B testing of features across different cohorts | ||
|
|
||
| 4. **Safe Refactoring** โ When plans change (e.g., adding a new plan tier, renaming plans, or moving features between | ||
| tiers), we only update the ability assignment logicโnot every place the feature is used. | ||
|
|
||
| 5. **Graceful Downgrades** โ When an organization downgrades, we update their abilities. All feature checks | ||
| automatically respect the new access level. | ||
|
|
||
| ## How It Works | ||
|
|
||
| ### Ability Assignment at Signup/Upgrade | ||
|
|
||
| When an organization is created or changes plans, the ability flags are set based on the plan's capabilities: | ||
|
|
||
| ```csharp | ||
| // During organization creation or plan change | ||
| organization.UseGroups = plan.HasGroups; | ||
| organization.UseSso = plan.HasSso; | ||
| organization.UseScim = plan.HasScim; | ||
| organization.UsePolicies = plan.HasPolicies; | ||
| organization.UseEvents = plan.HasEvents; | ||
| // ... etc | ||
| ``` | ||
|
|
||
| ### Modifying Abilities for Existing Organizations | ||
|
|
||
| To change abilities for existing organizations (e.g., rolling out a feature to a new plan tier), create a database | ||
| migration that updates the relevant flag: | ||
|
|
||
| ```sql | ||
| -- Example: Enable UseEvents for all Teams organizations | ||
| UPDATE [dbo].[Organization] | ||
| SET UseEvents = 1 | ||
| WHERE PlanType IN (17, 18) -- TeamsMonthly = 17, TeamsAnnually = 18 | ||
| ``` | ||
|
|
||
| Then update the plan-to-ability assignment code so new organizations get the correct value. | ||
|
|
||
| ## Adding a New Ability | ||
|
|
||
| When developing a new plan-gated feature: | ||
|
|
||
| 1. **Add the ability to the Organization and OrganizationAbility entities** โ Create a `Use[FeatureName]` boolean | ||
| property. | ||
|
|
||
| 2. **Add a database migration** โ Add the new column to the Organization table. | ||
|
|
||
| 3. **Update plan definitions** โ Add a corresponding `Has[FeatureName]` property to the Plan model and configure which | ||
| plans include it. | ||
|
|
||
| 4. **Update organization creation/upgrade logic** โ Ensure the ability is set based on the plan. | ||
|
|
||
| 5. **Update the organization license claims** (if applicable) - to make the feature available on self-hosted instances. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. โ QUESTION: What does "Update the organization license claims (if applicable)" mean and when is it applicable? ContextThis step lacks specificity for developers unfamiliar with the self-hosted licensing system. Based on the codebase, it appears to involve:
However, the documentation doesn't explain:
Could you clarify this step or provide more specific guidance? This would help developers know exactly what to do for self-hosted deployments.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are going to discuss this with Billing Team and provide more detail here in a separate PR. |
||
|
|
||
| 6. **Implement checks throughout client and server** โ Use the ability consistently everywhere the feature is accessed. | ||
| - Clients: get the organization object from `OrganizationService`. | ||
| - Server: if you already have the full `Organization` object in scope, you can use it directly. If not, use the | ||
| `IApplicationCacheService` to retrieve the `OrganizationAbility`, which is a simplified, cached representation | ||
| of the organization ability flags. Note that some older flags may be missing from `OrganizationAbility` but | ||
| can be added if needed. | ||
|
|
||
| ## Existing Abilities | ||
|
|
||
| For reference, here are some current organization ability flags (not a complete list): | ||
|
|
||
| | Ability | Description | Plans | | ||
| |--------------------------|-------------------------------|-------------------| | ||
| | `UseGroups` | Group-based collection access | Teams, Enterprise | | ||
| | `UseDirectory` | Directory Connector sync | Teams, Enterprise | | ||
| | `UseEvents` | Event logging | Teams, Enterprise | | ||
| | `UseTotp` | Authenticator (TOTP) | Teams, Enterprise | | ||
| | `UseSso` | Single Sign-On | Enterprise | | ||
| | `UseScim` | SCIM provisioning | Teams, Enterprise | | ||
| | `UsePolicies` | Enterprise policies | Enterprise | | ||
| | `UseResetPassword` | Admin password reset | Enterprise | | ||
| | `UseOrganizationDomains` | Domain verification/claiming | Enterprise | | ||
|
|
||
| ## Questions? | ||
|
|
||
| If you're unsure whether your feature needs a new ability or which existing ability to use, reach out to your team lead | ||
| or members of the Admin Console or Architecture teams. When in doubt, adding an explicit ability is almost always the | ||
| right choiceโit's easy to do and keeps our access control clean and maintainable. | ||
Uh oh!
There was an error while loading. Please reload this page.