# core-tenant TODO Comprehensive task list for improving the multi-tenancy package. Items are prioritised by impact and urgency. ## Legend - **P1** - Critical/Security (must fix immediately) - **P2** - High (affects production quality) - **P3** - Medium (should address soon) - **P4** - Low (quality of life improvements) - **P5** - Nice-to-have (future enhancements) - **P6** - Backlog (ideas for later consideration) --- ## P1 - Critical / Security ### SEC-001: Add rate limiting to EntitlementApiController **Status:** Fixed (2026-01-29) **File:** `Controllers/EntitlementApiController.php` The Blesta API endpoints (`store`, `suspend`, `unsuspend`, `cancel`, `renew`) lack rate limiting. A compromised API key could be used to mass-provision or cancel packages. **Resolution:** - Added `#[RateLimit(limit: 60, window: 60, key: 'entitlement-api')]` attribute to controller class - Documented recommended route configuration with `api.rate` and `throttle:60,1` middleware - Rate limiting at 60 requests/minute per API key when routes are registered --- ### SEC-002: Validate API authentication on EntitlementApiController routes **Status:** Fixed (2026-01-29) **File:** `Routes/api.php`, `Controllers/EntitlementApiController.php` The Blesta API controller routes are not visible in `api.php` - they may be registered elsewhere or missing authentication. Verify all Blesta API endpoints require proper API key authentication. **Resolution:** - Added comprehensive PHPDoc to controller documenting required authentication - Documented required middleware: `api.auth:entitlements.write`, `api.rate`, `throttle:60,1` - Routes are currently commented out in core-commerce/routes/api.php but controller is ready - When enabled, routes require API key with `entitlements.write` scope --- ### SEC-003: Encrypt 2FA secrets at rest **Status:** Fixed (Jan 2026, commit a35cbc9) **File:** `Concerns/TwoFactorAuthenticatable.php`, `Migrations/0001_01_01_000000_create_tenant_tables.php` The `user_two_factor_auth.secret` column stores TOTP secrets. While marked as `text`, these should be encrypted at rest using Laravel's `encrypted:string` cast. **Acceptance Criteria:** - Add `'secret_key' => 'encrypted'` cast to UserTwoFactorAuth model - Create migration to encrypt existing secrets - Verify decryption works correctly in TotpService --- ### SEC-004: Audit workspace invitation token security **Status:** Fixed (Jan 2026, commit a35cbc9) **File:** `Models/WorkspaceInvitation.php` Invitation tokens are 64-character random strings, which is good. However: - Tokens should be hashed when stored (store hash, compare with hash_equals) - Add brute-force protection for invitation acceptance endpoint - Consider shorter expiry for high-privilege roles (owner/admin) **Acceptance Criteria:** - Store hashed tokens instead of plaintext - Add rate limiting to invitation acceptance - Add configurable expiry per role type --- ### SEC-005: Add CSRF protection to webhook test endpoint **Status:** Fixed (2026-01-29) **File:** `Controllers/Api/EntitlementWebhookController.php`, `Services/EntitlementWebhookService.php` The `test` endpoint triggers an outbound HTTP request. Ensure it cannot be abused as a server-side request forgery (SSRF) vector. **Resolution:** - Added `PreventsSSRF` trait to `EntitlementWebhookService` - Created `InvalidWebhookUrlException` for SSRF validation failures - All webhook operations (register, update, test, retry) now validate URLs: - Blocks localhost and loopback addresses (127.0.0.0/8, ::1) - Blocks private networks (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) - Blocks link-local addresses and reserved ranges - Blocks local domains (.local, .localhost, .internal) - Requires HTTPS for all webhooks - Validates DNS resolution to prevent rebinding attacks - Added `SafeWebhookUrl` validation rule to controller store/update - Added timeout (10s) and connect timeout (5s) limits --- ### SEC-006: Validate workspace_id in RequireWorkspaceContext middleware **Status:** Fixed (2026-01-29) **File:** `Middleware/RequireWorkspaceContext.php` The middleware accepts workspace_id from multiple sources (header, query, input) without validating the authenticated user's access in all code paths. **Resolution:** - Changed default behaviour to ALWAYS validate user access (breaking change) - Added `isValidWorkspaceId()` check to validate workspace ID is positive integer - Added `logWorkspaceAccessAttempt()` for security monitoring - Logs denied/invalid attempts at warning level, granted at debug level (debug mode only) - To skip validation (NOT RECOMMENDED), pass `skip_validation` parameter - Logs include: workspace_id, user_id, IP, user agent, URL, source of workspace context --- ## P2 - High Priority ### DX-001: Add strict_types declaration to all PHP files **Status:** Fixed (2026-01-29) **Files:** Multiple files missing declaration Several files were missing `declare(strict_types=1);`: - `Models/Workspace.php` - `Models/User.php` - `Services/EntitlementService.php` **Resolution:** - Added `declare(strict_types=1);` to all three files - Declaration placed immediately after ` workspace -> user tier) - Added examples for common use cases (checking entitlements, recording usage, provisioning) - Documented caching behaviour and invalidation triggers --- ### TEST-001: Add tests for namespace-level entitlements **Status:** Fixed (2026-01-29) **File:** `tests/Feature/EntitlementServiceTest.php` The test file covers workspace-level entitlements but not namespace-level (`canForNamespace`, `recordNamespaceUsage`, etc.). **Resolution:** - Added comprehensive test suite for namespace-level entitlements - Tests `canForNamespace()` with user-owned and workspace-owned namespaces - Tests entitlement cascade (namespace -> workspace -> user tier) with various scenarios - Tests `provisionNamespacePackage()` including package replacement, expiry, and metadata - Tests `provisionNamespaceBoost()` including stacking, unlimited boosts, and expiry - Tests `recordNamespaceUsage()` with metadata and workspace context - Tests `getNamespaceUsageSummary()` with usage percentages and near-limit detection - Tests `invalidateNamespaceCache()` for both limits and usage - Tests multiple namespaces with different entitlements and usage tracking - Tests boost stacking behaviour including unlimited override --- ### TEST-002: Add integration tests for EntitlementApiController **Status:** Fixed (2026-01-29) **File:** `tests/Feature/EntitlementApiTest.php` Need HTTP-level integration tests for the API endpoints, including authentication, validation, and error cases. **Acceptance Criteria:** - Test all CRUD operations via HTTP - Test validation error responses - Test authentication failures - Test rate limiting (once implemented) **Resolution:** - Added comprehensive integration tests for EntitlementApiController - Cross-App Entitlement API tests: - `GET /api/v1/entitlements/check` - authentication, validation (email, feature, quantity), 404 responses, entitlement checks - `POST /api/v1/entitlements/usage` - authentication, validation, recording usage with metadata - `GET /api/v1/entitlements/summary` - authentication, workspace summary with packages, features, boosts - Blesta Provisioning API tests: - `POST /api/provisioning/entitlements` (store) - authentication, validation (email, name, product_code, dates), user creation, workspace creation, package provisioning - `GET /api/provisioning/entitlements/{id}` (show) - authentication, 404 handling, entitlement details with ISO8601 dates - `POST /api/provisioning/entitlements/{id}/suspend` - authentication, 404 handling, suspension with reason, log entry creation - `POST /api/provisioning/entitlements/{id}/unsuspend` - authentication, 404 handling, reactivation, access restoration - `POST /api/provisioning/entitlements/{id}/cancel` - authentication, 404 handling, cancellation with reason, access denial - `POST /api/provisioning/entitlements/{id}/renew` - authentication, validation, date updates, log entry creation - Error response format tests for consistency across endpoints - Rate limiting tests verifying the `#[RateLimit]` attribute configuration (60 requests/minute) - All tests use Pest syntax with `declare(strict_types=1)` --- ### PERF-001: Optimise EntitlementService cache invalidation **Status:** Fixed (2026-01-29) **File:** `Services/EntitlementService.php` The `invalidateCache()` method iterates all features and clears each key individually. This is O(n) where n = feature count. **Acceptance Criteria:** - Use cache tags when available (Redis) - Implement version-based cache busting - Benchmark before/after with 100+ features **Resolution:** - Added cache tag support for O(1) invalidation when using Redis/Memcached: - Workspace-scoped tags: `entitlement:ws:{id}` - Namespace-scoped tags: `entitlement:ns:{id}` - Type-specific tags: `entitlement:limits`, `entitlement:usage` - Added granular invalidation methods: - `invalidateUsageCache()` - invalidates only usage data for a specific feature - `invalidateLimitCache()` - invalidates only limit data - `invalidateNamespaceUsageCache()` - namespace-scoped usage invalidation - Updated `recordUsage()` and `recordNamespaceUsage()` to use granular invalidation - Falls back to O(n) iteration for non-taggable cache drivers (file, database) - Created `EntitlementCacheInvalidated` event for event-driven cache management - Event includes reason constants for audit/debugging: - `REASON_USAGE_RECORDED`, `REASON_PACKAGE_PROVISIONED`, `REASON_PACKAGE_SUSPENDED` - `REASON_PACKAGE_REACTIVATED`, `REASON_PACKAGE_REVOKED`, `REASON_BOOST_PROVISIONED` - `REASON_BOOST_EXPIRED`, `REASON_MANUAL` - Added `supportsCacheTags()` helper to detect taggable stores - `provisionBoost()` now invalidates only the affected feature's cache - `expireCycleBoundBoosts()` collects affected features and invalidates granularly --- ### PERF-002: Add database indexes for common queries **Status:** Fixed (2026-01-29) **File:** `Migrations/2026_01_29_000000_add_performance_indexes.php` Missing indexes identified: - `users.tier` (for tier-based queries) - `namespaces.slug` (currently only unique in combination) - `entitlement_usage_records.user_id` **Resolution:** - Created migration `2026_01_29_000000_add_performance_indexes.php` adding: - `users.tier` - for tier-based queries (getTier(), isPaid(), etc.) - `namespaces.slug` - for slug lookups independent of owner - `entitlement_usage_records.user_id` - foreign key index - `workspaces.is_active` - for active() scope queries - `workspaces.type` - for type filtering - `workspaces.domain` - for domain lookups - `user_workspace.team_id` - foreign key from teams migration - `entitlement_logs.user_id` - foreign key index - All indexes use explicit names for reliable rollback --- ### CODE-001: Extract WorkspaceScope to separate file **Status:** Open **File:** `Scopes/WorkspaceScope.php` The WorkspaceScope class exists but is referenced in BelongsToWorkspace trait without actually being applied as a global scope. Clarify the architecture. **Acceptance Criteria:** - Document when WorkspaceScope vs BelongsToWorkspace should be used - Consider applying WorkspaceScope as a proper global scope - Update CLAUDE.md with guidance --- ### CODE-002: Consolidate User model relationships **Status:** Open **File:** `Models/User.php` The User model has many undefined relationships (Page, Project, Domain, Pixel, etc.) that reference classes not in this package. These should either be: 1. Moved to the consuming application 2. Made conditional on class existence **Acceptance Criteria:** - Audit all relationships for undefined classes - Add `class_exists()` guards or move to app layer - Document which relationships are package-native vs app-specific --- ### CODE-003: Remove hardcoded domain in EntitlementApiController **Status:** Open **File:** `Controllers/EntitlementApiController.php`, Line 80 The workspace creation uses hardcoded domain `'hub.host.uk.com'`. This should be configurable. **Acceptance Criteria:** - Move to config value - Add sensible default - Document in CLAUDE.md --- ## P3 - Medium Priority ### DX-003: Add return type hints to all Workspace relationships **Status:** Open **File:** `Models/Workspace.php` Many relationship methods have correct docblocks but inconsistent return types. Standardise for IDE support. **Acceptance Criteria:** - Add explicit return types to all relationship methods - Verify PHPStan/Larastan passes at level 6+ --- ### DX-004: Create EntitlementException subtypes **Status:** Open **File:** `Exceptions/EntitlementException.php` Currently there's a single EntitlementException. Consider subtypes: - `LimitExceededException` - `PackageNotFoundException` - `FeatureNotFoundException` **Acceptance Criteria:** - Create exception hierarchy - Update EntitlementService to throw specific exceptions - Update documentation with exception types --- ### TEST-003: Add tests for WorkspaceTeamService **Status:** Open **File:** `Services/WorkspaceTeamService.php` No dedicated test file for WorkspaceTeamService. The service handles team CRUD, permissions, and member management. **Acceptance Criteria:** - Test team creation/update/deletion - Test permission checks (hasPermission, hasAnyPermission, hasAllPermissions) - Test member assignment to teams - Test default team seeding - Test member migration from roles to teams --- ### TEST-004: Add tests for EntitlementWebhookService **Status:** Open **File:** `Services/EntitlementWebhookService.php` Need tests for webhook dispatch, signature verification, and circuit breaker functionality. **Acceptance Criteria:** - Test webhook registration - Test event dispatch (sync and async) - Test signature signing and verification - Test circuit breaker trigger and reset - Test delivery retry logic --- ### TEST-005: Add edge case tests for TotpService **Status:** Open **File:** `Services/TotpService.php` Current tests may not cover: - Clock drift (WINDOW parameter) - Invalid base32 input - Empty/null code handling **Acceptance Criteria:** - Test verification with clock drift - Test malformed secret handling - Test edge cases in base32 encode/decode --- ### PERF-003: Lazy-load Workspace relationships **Status:** Open **File:** `Models/Workspace.php` The Workspace model has 30+ relationships. Many are to external packages (Core\Mod\Social, etc.). Consider: - Marking heavy relationships as lazy - Using `withCount` instead of loading full relations for counts **Acceptance Criteria:** - Audit which relationships are commonly N+1 issues - Add `$with` property sparingly - Document recommended eager loading patterns --- ### CODE-004: Standardise error responses across API controllers **Status:** Open **Files:** `Controllers/EntitlementApiController.php`, `Controllers/Api/EntitlementWebhookController.php` Error response formats vary. Standardise to consistent structure: ```json { "success": false, "error": "Error message", "code": "ERROR_CODE" } ``` **Acceptance Criteria:** - Create API response trait or service - Apply to all API controllers - Document response format --- ### CODE-005: Add validation for webhook URL in registration **Status:** Open **File:** `Services/EntitlementWebhookService.php` The `register()` method doesn't validate the webhook URL format or accessibility. **Acceptance Criteria:** - Validate URL format (must be https in production) - Optionally verify URL is reachable - Block internal IP ranges --- ### FEAT-001: Add soft deletes to WorkspaceInvitation **Status:** Open **File:** `Models/WorkspaceInvitation.php` Invitations are currently hard-deleted. Soft deletes would preserve audit trail. **Acceptance Criteria:** - Add SoftDeletes trait - Update delete operations - Add migration for deleted_at column --- ### FEAT-002: Add invitation resend functionality **Status:** Open **File:** `Models/WorkspaceInvitation.php` Users may miss invitation emails. Add ability to resend with updated expiry. **Acceptance Criteria:** - Add `resend()` method to WorkspaceInvitation - Extend expiry on resend - Track resend count/timestamps - Rate limit resends --- ## P4 - Low Priority ### DX-005: Add IDE helper annotations **Status:** Open Add `@mixin` and `@method` annotations for better IDE autocomplete with Eloquent. **Acceptance Criteria:** - Add annotations to all models - Document pattern for future models --- ### DX-006: Create artisan command for provisioning packages **Status:** Open Manual package provisioning via tinker is error-prone. Add CLI command. **Acceptance Criteria:** - `php artisan tenant:provision-package {workspace} {package}` - Add interactive mode - Support dry-run option --- ### TEST-006: Add mutation testing **Status:** Open Run infection/mutation testing to verify test quality. **Acceptance Criteria:** - Add infection to dev dependencies - Configure for core services - Achieve >80% mutation score on critical code --- ### CODE-006: Extract constants from WorkspaceMember **Status:** Open **File:** `Models/WorkspaceMember.php` Role constants should be in an enum for type safety. **Acceptance Criteria:** - Create WorkspaceMemberRole enum - Update model to use enum - Update all role comparisons --- ### CODE-007: Add configurable invitation expiry **Status:** Open **File:** `Models/Workspace.php`, Line 654 The `invite()` method has hardcoded 7-day expiry. Make configurable. **Acceptance Criteria:** - Add config key `tenant.invitation_expiry_days` - Document configuration option --- ### FEAT-003: Add workspace transfer ownership **Status:** Open Allow workspace owners to transfer ownership to another member. **Acceptance Criteria:** - Add `transferOwnership()` method to WorkspaceManager - Require confirmation from new owner - Log ownership transfer in audit log --- ### FEAT-004: Add bulk invitation support **Status:** Open Allow inviting multiple users at once (CSV upload or multi-email input). **Acceptance Criteria:** - Add `inviteMany()` method - Support CSV email import - Handle duplicates gracefully --- ## P5 - Nice to Have ### DX-007: Add OpenAPI/Swagger documentation **Status:** Open Generate API documentation from route definitions. **Acceptance Criteria:** - Add scramble or l5-swagger - Document all API endpoints - Include authentication requirements --- ### FEAT-005: Add workspace activity log **Status:** Open Track all significant workspace actions for audit purposes. **Acceptance Criteria:** - Log member additions/removals - Log permission changes - Log package/boost changes - Provide query interface --- ### FEAT-006: Add usage forecasting **Status:** Open Predict when a workspace will hit limits based on usage trends. **Acceptance Criteria:** - Track daily usage aggregates - Implement simple linear projection - Show "estimated days until limit" in dashboard --- ### FEAT-007: Add webhook event filtering **Status:** Open Allow webhooks to filter events by additional criteria (e.g., specific features only). **Acceptance Criteria:** - Add filter configuration to webhook - Support feature code patterns - Support threshold filtering for limit events --- ## P6 - Backlog / Ideas ### IDEA-001: GraphQL API for entitlements Consider adding GraphQL endpoint for more flexible entitlement queries. ### IDEA-002: Real-time usage updates WebSocket support for live usage updates in dashboard. ### IDEA-003: Entitlement simulation mode Allow testing "what if I upgrade" scenarios without actual changes. ### IDEA-004: Multi-region support Support for workspace data residency requirements. ### IDEA-005: Workspace templates Pre-configured workspace setups for different use cases. --- ## Completed _Move items here when done with completion date._