feat: add organization domain foundation

This commit is contained in:
2026-05-04 16:15:53 -04:00
parent 802668fb0b
commit 7d3f495472
55 changed files with 2995 additions and 115 deletions

View File

@@ -2,7 +2,7 @@
## Status
Draft
Ready for initial backend implementation.
## Goal
@@ -173,6 +173,32 @@ Workspace-level screens remain centered on the selected workspace.
- Workspace APIs must preserve workspace scoping and account for inherited organization permissions.
- New backend contracts require OpenAPI regeneration while the backend is running.
## Implementation Readiness
The initial implementation should proceed through the task files in `docs/TASKS/organizations/`.
Recommended order:
1. `001-organization-domain-foundation.md`
2. `002-organization-membership-permissions.md`
3. `003-organization-settings-ui.md`
4. `004-workspace-selector-organization-switcher.md`
Task 001 should establish the organization table, workspace ownership, current-user organization read APIs, and development bootstrap data. It should not attempt the full inherited permission model beyond enough access data to prove a user can access their organizations.
Task 002 should introduce organization memberships and explicit organization permissions before frontend settings or switcher work relies on permission-gated data.
Frontend tasks should start only after backend contracts have been regenerated into `shared/openapi/openapi.json` and `frontend/src/api/schema.d.ts`.
Initial backend routes:
```txt
GET /api/organizations
GET /api/organizations/{organizationId}
```
Workspace responses should include `organizationId` once workspaces are owned by organizations.
## Out Of Scope For Initial Implementation
- Preserving existing local data through migration. Development data can be wiped.

View File

@@ -17,12 +17,14 @@ Existing local data does not need to be preserved.
## Scope
- Add an `Organizations` backend module or follow the existing ownership pattern if organization code belongs with `Workspaces`.
- Add an organization persistence model with name, slug/display identity, timestamps, and basic audit fields matching local conventions.
- Add an organization persistence model with `Id`, `Name`, `Slug`, `OwnerUserId`, and `CreatedAt`, matching local conventions.
- Require every workspace to belong to exactly one organization.
- Update workspace create/list/detail APIs to include organization ownership.
- Add APIs for current user organization list and organization detail.
- Add current-user organization read APIs:
- `GET /api/organizations`
- `GET /api/organizations/{organizationId}`
- Add backend validation that users cannot access organizations they have no relationship with.
- Seed or development bootstrap data should create at least one organization and owned workspace.
- Seed or development bootstrap data should create at least one organization and owned workspace when local development data is empty.
- Update OpenAPI after backend contracts change.
## Constraints
@@ -32,8 +34,21 @@ Existing local data does not need to be preserved.
- Do not implement billing provider integration in this task.
- Do not implement connector storage in this task.
- Do not implement full organization membership override behavior in this task.
- Do not implement organization settings UI in this task.
- Existing development data may be wiped; do not spend scope on compatibility migration behavior.
## Implementation Notes
- Place organization-owned backend code under `backend/src/Socialize.Api/Modules/Organizations`.
- Add `DbSet<Organization>` and `ConfigureOrganizationsModule()` to `AppDbContext`.
- Keep `Workspace.OwnerUserId` for the existing creator/owner convention unless a later task explicitly replaces it.
- Add a required `Workspace.OrganizationId` property and database index.
- The first implementation may grant organization access through `Organization.OwnerUserId == currentUserId` and existing manager/administrator access. Full organization membership belongs to task 002.
- `CreateWorkspaceRequest` should require `OrganizationId`; reject creation when the user cannot manage that organization.
- `WorkspaceDto` should include `OrganizationId`.
- Slugs should keep the existing lowercase kebab-case validation used for workspaces.
- Use tests for unauthorized organization detail access and workspace creation under an inaccessible organization.
## Likely Files
- `backend/src/Socialize.Api/Data/AppDbContext.cs`
@@ -46,15 +61,15 @@ Existing local data does not need to be preserved.
## Done When
- [ ] Organization entity is persisted.
- [ ] Workspace requires `OrganizationId`.
- [ ] Workspace APIs expose organization ownership.
- [ ] Current user can list accessible organizations.
- [ ] Current user can get accessible organization details.
- [ ] Unauthorized organization access is rejected.
- [ ] Development seed data creates an organization with owned workspaces.
- [ ] Backend build and tests pass.
- [ ] OpenAPI and generated frontend schema are updated.
- [x] Organization entity is persisted.
- [x] Workspace requires `OrganizationId`.
- [x] Workspace APIs expose organization ownership.
- [x] Current user can list accessible organizations.
- [x] Current user can get accessible organization details.
- [x] Unauthorized organization access is rejected.
- [x] Development seed data creates an organization with owned workspaces.
- [x] Backend build and tests pass.
- [x] OpenAPI and generated frontend schema are updated.
## Validation Commands

View File

@@ -36,6 +36,40 @@ Users have global accounts. A user can have rights in multiple organizations and
- External collaborators must not become organization members automatically.
- Keep permission names explicit; avoid magic strings where local patterns provide constants.
## Permission Model
Use explicit constants in the Organizations module rather than raw strings in handlers.
Initial organization permissions:
- `ManageOrganizationSettings`
- `ManageOrganizationMembers`
- `CreateWorkspaces`
- `ManageWorkspaces`
- `ManageBilling`
- `ManageConnectors`
- `AccessOwnedWorkspaces`
Initial organization roles should map to permissions in code:
- `Owner`: all organization permissions.
- `Admin`: organization settings, organization members, workspace creation, workspace administration, connector management, and owned workspace access. Billing is not included unless explicitly assigned.
- `BillingManager`: billing and owned workspace access.
- `ConnectorManager`: connector management and owned workspace access.
- `Member`: owned workspace access only.
Workspace-specific permissions may be overridden at the workspace level after inherited organization access is resolved. Billing and connector permissions must never be granted from workspace-level overrides.
Direct workspace members who are not organization members should be labeled `External Collaborator` in workspace membership responses. Organization members with inherited or direct workspace access should be labeled `Organization Member`.
## Implementation Notes
- Add an `OrganizationMembership` persistence model with `OrganizationId`, `UserId`, role/permission data, and `CreatedAt`.
- Prefer a small Organizations access service for organization access checks and inherited workspace permission calculation instead of adding ad hoc queries to every handler.
- Update JWT claims only if a task proves claims are needed; permission checks can query current database state first.
- Preserve existing global Identity roles while introducing organization-scoped roles. Do not reuse global `manager`, `client`, or `provider` roles as organization roles.
- Add unit tests for role-to-permission mapping and handler/integration tests for access rejection where existing test infrastructure supports it.
## Likely Files
- `backend/src/Socialize.Api/Modules/Organizations/**`
@@ -46,12 +80,12 @@ Users have global accounts. A user can have rights in multiple organizations and
## Done When
- [ ] Organization memberships are persisted.
- [ ] Organization roles/permissions include billing manager.
- [ ] Organization-level access can grant inherited access to owned workspaces.
- [ ] Direct workspace-only external collaborators remain supported.
- [ ] Workspace-level overrides apply to workspace-specific permissions.
- [ ] Billing and connector permissions cannot be granted through workspace overrides.
- [x] Organization memberships are persisted.
- [x] Organization roles/permissions include billing manager.
- [x] Organization-level access can grant inherited access to owned workspaces.
- [x] Direct workspace-only external collaborators remain supported.
- [x] Workspace-level overrides apply to workspace-specific permissions.
- [x] Billing and connector permissions cannot be granted through workspace overrides.
- [ ] Backend tests cover inherited, direct, external collaborator, and override access paths.
## Validation Commands