Move 9 init/seed scripts into scripts/seed/ and 7 validation scripts (+ validators/ subfolder) into scripts/validate/ to reduce clutter in the root scripts/ directory. Update all references across Makefile, CI/CD configs, pre-commit hooks, docs (~40 files), and Python imports. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
653 lines
24 KiB
Markdown
653 lines
24 KiB
Markdown
# Plan: Flexible Role & Permission Management with Platform Controls
|
|
|
|
## Status: READY FOR APPROVAL
|
|
|
|
## Summary
|
|
|
|
Design a flexible role/permission management system that:
|
|
1. **Modules define permissions** - Each module declares its available permissions
|
|
2. **Platforms control availability** - Platforms can restrict which permissions stores can use
|
|
3. **Stores customize roles** - Stores create custom roles within platform constraints
|
|
4. **Multi-tier hierarchy** - Platform → Store → User permission inheritance
|
|
|
|
---
|
|
|
|
## Current State Analysis
|
|
|
|
### What Exists Today
|
|
|
|
| Component | Location | Description |
|
|
|-----------|----------|-------------|
|
|
| **Role Model** | `app/modules/tenancy/models/store.py` | `store_id`, `name`, `permissions` (JSON array) |
|
|
| **StoreUser Model** | Same file | Links user → store with `role_id` |
|
|
| **PermissionDiscoveryService** | `app/modules/tenancy/services/permission_discovery_service.py` | Discovers permissions from modules |
|
|
| **StoreTeamService** | `app/modules/tenancy/services/store_team_service.py` | Manages team invitations, role assignment |
|
|
| **Role Presets** | In discovery service code | Hardcoded `ROLE_PRESETS` dict |
|
|
| **Platform Model** | `models/database/platform.py` | Multi-platform support |
|
|
| **PlatformModule** | `models/database/platform_module.py` | Controls which modules are enabled per platform |
|
|
| **StorePlatform** | `models/database/store_platform.py` | Store-platform relationship with `tier_id` |
|
|
|
|
### Current Gaps
|
|
|
|
1. **No platform-level permission control** - Platforms cannot restrict which permissions stores can assign
|
|
2. **No custom role CRUD API** - Roles are created implicitly when inviting team members
|
|
3. **Presets are code-only** - Cannot customize role templates per platform
|
|
4. **No role templates table** - Platform admins cannot define default roles for their stores
|
|
|
|
---
|
|
|
|
## Proposed Architecture
|
|
|
|
### Tier 1: Module-Defined Permissions (Exists)
|
|
|
|
Each module declares permissions in `definition.py`:
|
|
|
|
```python
|
|
permissions=[
|
|
PermissionDefinition(
|
|
id="products.view",
|
|
label_key="catalog.permissions.products_view",
|
|
category="products",
|
|
),
|
|
PermissionDefinition(
|
|
id="products.create",
|
|
label_key="catalog.permissions.products_create",
|
|
category="products",
|
|
),
|
|
]
|
|
```
|
|
|
|
**Discovery Service** aggregates all permissions at runtime.
|
|
|
|
### Tier 2: Platform Permission Control (New)
|
|
|
|
New `PlatformPermissionConfig` model to control:
|
|
- Which permissions are available to stores on this platform
|
|
- Default role templates for store onboarding
|
|
- Permission bundles based on subscription tier
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ PLATFORM │
|
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
│ │ PlatformPermissionConfig │ │
|
|
│ │ - platform_id │ │
|
|
│ │ - allowed_permissions: ["products.*", "orders.*"] │ │
|
|
│ │ - blocked_permissions: ["team.manage"] │ │
|
|
│ │ - tier_restrictions: {free: [...], pro: [...]} │ │
|
|
│ └─────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
│ │ PlatformRoleTemplate │ │
|
|
│ │ - platform_id │ │
|
|
│ │ - name: "Manager", "Staff", etc. │ │
|
|
│ │ - permissions: [...] │ │
|
|
│ │ - is_default: bool (create for new stores) │ │
|
|
│ └─────────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Tier 3: Store Role Customization (Enhanced)
|
|
|
|
Stores can:
|
|
- View roles available (from platform templates or custom)
|
|
- Create custom roles (within platform constraints)
|
|
- Edit role permissions (within allowed set)
|
|
- Assign roles to team members
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ STORE │
|
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
│ │ Role (existing model, enhanced) │ │
|
|
│ │ - store_id │ │
|
|
│ │ - name │ │
|
|
│ │ - permissions: [...] (validated against platform) │ │
|
|
│ │ - is_from_template: bool │ │
|
|
│ │ - source_template_id: FK (nullable) │ │
|
|
│ └─────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
│ │ StoreUser (existing, unchanged) │ │
|
|
│ │ - user_id │ │
|
|
│ │ - store_id │ │
|
|
│ │ - role_id │ │
|
|
│ └─────────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Data Model Changes
|
|
|
|
### New Models
|
|
|
|
#### 1. PlatformPermissionConfig
|
|
|
|
```python
|
|
# app/modules/tenancy/models/platform_permission_config.py
|
|
|
|
class PlatformPermissionConfig(Base):
|
|
"""Platform-level permission configuration"""
|
|
__tablename__ = "platform_permission_configs"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
platform_id: Mapped[int] = mapped_column(ForeignKey("platforms.id"), unique=True)
|
|
|
|
# Permissions this platform allows stores to use
|
|
# Empty = all discovered permissions allowed
|
|
allowed_permissions: Mapped[list[str]] = mapped_column(JSON, default=list)
|
|
|
|
# Explicit blocklist (takes precedence over allowed)
|
|
blocked_permissions: Mapped[list[str]] = mapped_column(JSON, default=list)
|
|
|
|
# Tier-based restrictions: {"free": ["products.view"], "pro": ["products.*"]}
|
|
tier_permissions: Mapped[dict] = mapped_column(JSON, default=dict)
|
|
|
|
created_at: Mapped[datetime] = mapped_column(default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(onupdate=func.now())
|
|
|
|
# Relationships
|
|
platform: Mapped["Platform"] = relationship(back_populates="permission_config")
|
|
```
|
|
|
|
#### 2. PlatformRoleTemplate
|
|
|
|
```python
|
|
# app/modules/tenancy/models/platform_role_template.py
|
|
|
|
class PlatformRoleTemplate(Base):
|
|
"""Role templates defined at platform level"""
|
|
__tablename__ = "platform_role_templates"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
platform_id: Mapped[int] = mapped_column(ForeignKey("platforms.id"))
|
|
|
|
name: Mapped[str] = mapped_column(String(50)) # "Manager", "Staff", etc.
|
|
display_name: Mapped[str] = mapped_column(String(100)) # i18n key or display name
|
|
description: Mapped[str | None] = mapped_column(String(255))
|
|
|
|
# Permissions for this template
|
|
permissions: Mapped[list[str]] = mapped_column(JSON, default=list)
|
|
|
|
# Configuration
|
|
is_default: Mapped[bool] = mapped_column(default=False) # Auto-create for new stores
|
|
is_system: Mapped[bool] = mapped_column(default=False) # Cannot be deleted
|
|
order: Mapped[int] = mapped_column(default=100) # Display order
|
|
|
|
created_at: Mapped[datetime] = mapped_column(default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(onupdate=func.now())
|
|
|
|
# Relationships
|
|
platform: Mapped["Platform"] = relationship(back_populates="role_templates")
|
|
|
|
__table_args__ = (
|
|
UniqueConstraint("platform_id", "name", name="uq_platform_role_template"),
|
|
)
|
|
```
|
|
|
|
### Enhanced Existing Models
|
|
|
|
#### Role Model Enhancement
|
|
|
|
```python
|
|
# Add to existing Role model in app/modules/tenancy/models/store.py
|
|
|
|
class Role(Base):
|
|
# ... existing fields ...
|
|
|
|
# NEW: Track template origin
|
|
source_template_id: Mapped[int | None] = mapped_column(
|
|
ForeignKey("platform_role_templates.id"),
|
|
nullable=True
|
|
)
|
|
is_custom: Mapped[bool] = mapped_column(default=False) # Store-created custom role
|
|
|
|
# Relationship
|
|
source_template: Mapped["PlatformRoleTemplate"] = relationship()
|
|
```
|
|
|
|
---
|
|
|
|
## Service Layer Changes
|
|
|
|
### 1. PlatformPermissionService (New)
|
|
|
|
```python
|
|
# app/modules/tenancy/services/platform_permission_service.py
|
|
|
|
class PlatformPermissionService:
|
|
"""Manages platform-level permission configuration"""
|
|
|
|
def get_allowed_permissions(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
tier_id: int | None = None
|
|
) -> set[str]:
|
|
"""
|
|
Get permissions allowed for a platform/tier combination.
|
|
|
|
1. Start with all discovered permissions
|
|
2. Filter by platform's allowed_permissions (if set)
|
|
3. Remove blocked_permissions
|
|
4. Apply tier restrictions (if tier_id provided)
|
|
"""
|
|
pass
|
|
|
|
def validate_permissions(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
tier_id: int | None,
|
|
permissions: list[str]
|
|
) -> tuple[list[str], list[str]]:
|
|
"""
|
|
Validate permissions against platform constraints.
|
|
Returns (valid_permissions, invalid_permissions)
|
|
"""
|
|
pass
|
|
|
|
def update_platform_config(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
allowed_permissions: list[str] | None = None,
|
|
blocked_permissions: list[str] | None = None,
|
|
tier_permissions: dict | None = None
|
|
) -> PlatformPermissionConfig:
|
|
"""Update platform permission configuration"""
|
|
pass
|
|
```
|
|
|
|
### 2. PlatformRoleTemplateService (New)
|
|
|
|
```python
|
|
# app/modules/tenancy/services/platform_role_template_service.py
|
|
|
|
class PlatformRoleTemplateService:
|
|
"""Manages platform role templates"""
|
|
|
|
def get_templates(self, db: Session, platform_id: int) -> list[PlatformRoleTemplate]:
|
|
"""Get all role templates for a platform"""
|
|
pass
|
|
|
|
def create_template(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
name: str,
|
|
permissions: list[str],
|
|
is_default: bool = False
|
|
) -> PlatformRoleTemplate:
|
|
"""Create a new role template (validates permissions)"""
|
|
pass
|
|
|
|
def create_default_roles_for_store(
|
|
self,
|
|
db: Session,
|
|
store: Store
|
|
) -> list[Role]:
|
|
"""
|
|
Create store roles from platform's default templates.
|
|
Called during store onboarding.
|
|
"""
|
|
pass
|
|
|
|
def seed_default_templates(self, db: Session, platform_id: int):
|
|
"""Seed platform with standard role templates (Manager, Staff, etc.)"""
|
|
pass
|
|
```
|
|
|
|
### 3. Enhanced StoreTeamService
|
|
|
|
```python
|
|
# Updates to app/modules/tenancy/services/store_team_service.py
|
|
|
|
class StoreTeamService:
|
|
|
|
def get_available_permissions(
|
|
self,
|
|
db: Session,
|
|
store: Store
|
|
) -> list[PermissionDefinition]:
|
|
"""
|
|
Get permissions available to this store based on:
|
|
1. Platform constraints
|
|
2. Store's subscription tier
|
|
"""
|
|
platform_perm_service = PlatformPermissionService()
|
|
store_platform = db.query(StorePlatform).filter(...).first()
|
|
|
|
allowed = platform_perm_service.get_allowed_permissions(
|
|
db,
|
|
store_platform.platform_id,
|
|
store_platform.tier_id
|
|
)
|
|
|
|
# Return PermissionDefinitions filtered to allowed set
|
|
all_perms = permission_discovery_service.get_all_permissions()
|
|
return [p for p in all_perms if p.id in allowed]
|
|
|
|
def create_custom_role(
|
|
self,
|
|
db: Session,
|
|
store: Store,
|
|
name: str,
|
|
permissions: list[str]
|
|
) -> Role:
|
|
"""
|
|
Create a custom role for the store.
|
|
Validates permissions against platform constraints.
|
|
"""
|
|
# Validate permissions
|
|
valid, invalid = self.platform_permission_service.validate_permissions(
|
|
db, store.platform_id, store.tier_id, permissions
|
|
)
|
|
if invalid:
|
|
raise InvalidPermissionsException(invalid)
|
|
|
|
role = Role(
|
|
store_id=store.id,
|
|
name=name,
|
|
permissions=valid,
|
|
is_custom=True
|
|
)
|
|
db.add(role)
|
|
return role
|
|
|
|
def update_role(
|
|
self,
|
|
db: Session,
|
|
store: Store,
|
|
role_id: int,
|
|
name: str | None = None,
|
|
permissions: list[str] | None = None
|
|
) -> Role:
|
|
"""Update an existing role (validates permissions)"""
|
|
pass
|
|
|
|
def delete_role(
|
|
self,
|
|
db: Session,
|
|
store: Store,
|
|
role_id: int
|
|
) -> bool:
|
|
"""Delete a custom role (cannot delete if in use)"""
|
|
pass
|
|
```
|
|
|
|
---
|
|
|
|
## API Endpoints
|
|
|
|
### Platform Admin Endpoints (Admin Panel)
|
|
|
|
```python
|
|
# app/modules/tenancy/routes/admin/platform_permissions.py
|
|
|
|
@router.get("/platforms/{platform_id}/permissions")
|
|
def get_platform_permission_config(platform_id: int):
|
|
"""Get platform permission configuration"""
|
|
|
|
@router.put("/platforms/{platform_id}/permissions")
|
|
def update_platform_permission_config(platform_id: int, config: PermissionConfigUpdate):
|
|
"""Update platform permission configuration"""
|
|
|
|
@router.get("/platforms/{platform_id}/role-templates")
|
|
def list_role_templates(platform_id: int):
|
|
"""List role templates for a platform"""
|
|
|
|
@router.post("/platforms/{platform_id}/role-templates")
|
|
def create_role_template(platform_id: int, template: RoleTemplateCreate):
|
|
"""Create a new role template"""
|
|
|
|
@router.put("/platforms/{platform_id}/role-templates/{template_id}")
|
|
def update_role_template(platform_id: int, template_id: int, template: RoleTemplateUpdate):
|
|
"""Update a role template"""
|
|
|
|
@router.delete("/platforms/{platform_id}/role-templates/{template_id}")
|
|
def delete_role_template(platform_id: int, template_id: int):
|
|
"""Delete a role template"""
|
|
```
|
|
|
|
### Store Dashboard Endpoints
|
|
|
|
```python
|
|
# app/modules/tenancy/routes/api/store_roles.py
|
|
|
|
@router.get("/roles")
|
|
def list_store_roles():
|
|
"""List all roles for current store"""
|
|
|
|
@router.post("/roles")
|
|
def create_custom_role(role: RoleCreate):
|
|
"""Create a custom role (validates permissions against platform)"""
|
|
|
|
@router.put("/roles/{role_id}")
|
|
def update_role(role_id: int, role: RoleUpdate):
|
|
"""Update a role"""
|
|
|
|
@router.delete("/roles/{role_id}")
|
|
def delete_role(role_id: int):
|
|
"""Delete a custom role"""
|
|
|
|
@router.get("/available-permissions")
|
|
def get_available_permissions():
|
|
"""Get permissions available to this store (filtered by platform/tier)"""
|
|
```
|
|
|
|
---
|
|
|
|
## Permission Flow Diagram
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ PERMISSION FLOW │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
1. MODULE DEFINES PERMISSIONS
|
|
┌──────────────┐
|
|
│ catalog │ → products.view, products.create, products.edit, ...
|
|
│ orders │ → orders.view, orders.manage, orders.refund, ...
|
|
│ team │ → team.view, team.manage, team.invite, ...
|
|
└──────────────┘
|
|
↓
|
|
|
|
2. DISCOVERY SERVICE AGGREGATES
|
|
┌────────────────────────────────────────────────┐
|
|
│ PermissionDiscoveryService │
|
|
│ get_all_permissions() → 50+ permissions │
|
|
└────────────────────────────────────────────────┘
|
|
↓
|
|
|
|
3. PLATFORM FILTERS PERMISSIONS
|
|
┌────────────────────────────────────────────────┐
|
|
│ PlatformPermissionConfig │
|
|
│ allowed: ["products.*", "orders.view"] │
|
|
│ blocked: ["orders.refund"] │
|
|
│ tier_permissions: │
|
|
│ free: ["products.view", "orders.view"] │
|
|
│ pro: ["products.*", "orders.*"] │
|
|
└────────────────────────────────────────────────┘
|
|
↓
|
|
|
|
4. STORE CREATES/USES ROLES
|
|
┌────────────────────────────────────────────────┐
|
|
│ Role (store-specific) │
|
|
│ Manager: [products.*, orders.view] │
|
|
│ Staff: [products.view, orders.view] │
|
|
└────────────────────────────────────────────────┘
|
|
↓
|
|
|
|
5. USER GETS PERMISSIONS VIA ROLE
|
|
┌────────────────────────────────────────────────┐
|
|
│ StoreUser │
|
|
│ user_id: 123 │
|
|
│ role_id: 5 (Staff) │
|
|
│ → permissions: [products.view, orders.view] │
|
|
└────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Database Migrations
|
|
|
|
### Migration 1: Add Platform Permission Config
|
|
|
|
```sql
|
|
CREATE TABLE platform_permission_configs (
|
|
id SERIAL PRIMARY KEY,
|
|
platform_id INTEGER NOT NULL UNIQUE REFERENCES platforms(id),
|
|
allowed_permissions JSONB DEFAULT '[]',
|
|
blocked_permissions JSONB DEFAULT '[]',
|
|
tier_permissions JSONB DEFAULT '{}',
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP
|
|
);
|
|
```
|
|
|
|
### Migration 2: Add Platform Role Templates
|
|
|
|
```sql
|
|
CREATE TABLE platform_role_templates (
|
|
id SERIAL PRIMARY KEY,
|
|
platform_id INTEGER NOT NULL REFERENCES platforms(id),
|
|
name VARCHAR(50) NOT NULL,
|
|
display_name VARCHAR(100) NOT NULL,
|
|
description VARCHAR(255),
|
|
permissions JSONB DEFAULT '[]',
|
|
is_default BOOLEAN DEFAULT FALSE,
|
|
is_system BOOLEAN DEFAULT FALSE,
|
|
"order" INTEGER DEFAULT 100,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP,
|
|
UNIQUE (platform_id, name)
|
|
);
|
|
```
|
|
|
|
### Migration 3: Enhance Roles Table
|
|
|
|
```sql
|
|
ALTER TABLE roles
|
|
ADD COLUMN source_template_id INTEGER REFERENCES platform_role_templates(id),
|
|
ADD COLUMN is_custom BOOLEAN DEFAULT FALSE;
|
|
```
|
|
|
|
---
|
|
|
|
## Implementation Phases
|
|
|
|
### Phase 1: Data Models (Foundation)
|
|
|
|
**Files to create:**
|
|
- `app/modules/tenancy/models/platform_permission_config.py`
|
|
- `app/modules/tenancy/models/platform_role_template.py`
|
|
- `migrations/versions/xxx_add_platform_permission_tables.py`
|
|
- `migrations/versions/xxx_enhance_roles_table.py`
|
|
|
|
**Files to modify:**
|
|
- `app/modules/tenancy/models/__init__.py` - Export new models
|
|
- `app/modules/tenancy/models/store.py` - Add Role enhancement
|
|
|
|
### Phase 2: Service Layer
|
|
|
|
**Files to create:**
|
|
- `app/modules/tenancy/services/platform_permission_service.py`
|
|
- `app/modules/tenancy/services/platform_role_template_service.py`
|
|
|
|
**Files to modify:**
|
|
- `app/modules/tenancy/services/store_team_service.py` - Add role CRUD, permission validation
|
|
|
|
### Phase 3: API Endpoints
|
|
|
|
**Files to create:**
|
|
- `app/modules/tenancy/routes/admin/platform_permissions.py`
|
|
- `app/modules/tenancy/routes/api/store_roles.py`
|
|
- `app/modules/tenancy/schemas/platform_permissions.py`
|
|
- `app/modules/tenancy/schemas/roles.py`
|
|
|
|
**Files to modify:**
|
|
- `app/modules/tenancy/routes/__init__.py` - Register new routers
|
|
|
|
### Phase 4: Store Onboarding Integration
|
|
|
|
**Files to modify:**
|
|
- `app/modules/tenancy/services/store_service.py` - Create default roles from templates during store creation
|
|
|
|
### Phase 5: Admin UI (Optional, Future)
|
|
|
|
**Files to create/modify:**
|
|
- Admin panel for platform permission configuration
|
|
- Admin panel for role template management
|
|
- Store dashboard for custom role management
|
|
|
|
---
|
|
|
|
## Verification
|
|
|
|
1. **App loads:** `python -c "from main import app; print('OK')"`
|
|
|
|
2. **Migrations run:** `make migrate-up`
|
|
|
|
3. **Architecture validation:** `python scripts/validate/validate_architecture.py -v`
|
|
|
|
4. **Unit tests:** Test permission filtering logic
|
|
- Platform with no config → all permissions allowed
|
|
- Platform with allowed list → only those permissions
|
|
- Platform with blocked list → all except blocked
|
|
- Tier restrictions → correct subset per tier
|
|
|
|
5. **Integration tests:**
|
|
- Create store → gets default roles from platform templates
|
|
- Create custom role → validates against platform constraints
|
|
- Assign role → user gets correct permissions
|
|
- Change tier → available permissions update
|
|
|
|
6. **API tests:**
|
|
- Platform admin can configure permissions
|
|
- Store owner can create/edit custom roles
|
|
- Invalid permissions are rejected
|
|
|
|
---
|
|
|
|
## Files Summary
|
|
|
|
### New Files (9)
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `app/modules/tenancy/models/platform_permission_config.py` | Platform permission config model |
|
|
| `app/modules/tenancy/models/platform_role_template.py` | Platform role template model |
|
|
| `app/modules/tenancy/services/platform_permission_service.py` | Platform permission logic |
|
|
| `app/modules/tenancy/services/platform_role_template_service.py` | Role template logic |
|
|
| `app/modules/tenancy/routes/admin/platform_permissions.py` | Admin API endpoints |
|
|
| `app/modules/tenancy/routes/api/store_roles.py` | Store API endpoints |
|
|
| `app/modules/tenancy/schemas/platform_permissions.py` | Pydantic schemas |
|
|
| `app/modules/tenancy/schemas/roles.py` | Role schemas |
|
|
| `migrations/versions/xxx_platform_permission_tables.py` | Database migration |
|
|
|
|
### Modified Files (5)
|
|
|
|
| File | Changes |
|
|
|------|---------|
|
|
| `app/modules/tenancy/models/__init__.py` | Export new models |
|
|
| `app/modules/tenancy/models/store.py` | Enhance Role model |
|
|
| `app/modules/tenancy/services/store_team_service.py` | Add role CRUD, validation |
|
|
| `app/modules/tenancy/services/store_service.py` | Create default roles on store creation |
|
|
| `app/modules/tenancy/routes/__init__.py` | Register new routers |
|
|
|
|
---
|
|
|
|
## Key Design Decisions
|
|
|
|
1. **Wildcard support in permissions** - `products.*` matches `products.view`, `products.create`, etc.
|
|
|
|
2. **Tier inheritance** - Higher tiers include all permissions of lower tiers
|
|
|
|
3. **Template-based store roles** - Default roles created from platform templates, but store can customize
|
|
|
|
4. **Soft validation** - Invalid permissions in existing roles are not automatically removed (audit trail)
|
|
|
|
5. **Backward compatible** - Existing roles without `source_template_id` continue to work
|