php-tenant/TODO.md
Snider a35cbc9306 security: encrypt 2FA secrets and hash invitation tokens
- Add encrypted cast to UserTwoFactorAuth secret and recovery_codes
- Hash invitation tokens on creation using Hash::make()
- Update token verification to use Hash::check()
- Add migration commands for existing data:
  - security:encrypt-2fa-secrets
  - security:hash-invitation-tokens
- Add tests for encryption and hashing

Fixes SEC-003, SEC-004 from security audit.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 12:20:53 +00:00

542 lines
15 KiB
Markdown

# 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:** Open
**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.
**Acceptance Criteria:**
- Add rate limiting middleware to all Blesta API routes
- Configure sensible limits (e.g., 60 requests/minute per IP)
- Log rate limit violations for security monitoring
---
### SEC-002: Validate API authentication on EntitlementApiController routes
**Status:** Open
**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.
**Acceptance Criteria:**
- Confirm all entitlement API routes require authentication
- Add API key validation middleware if missing
- Document required scopes for each endpoint
---
### SEC-003: Encrypt 2FA secrets at rest
**Status:** Open
**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:** Open
**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:** Open
**File:** `Controllers/Api/EntitlementWebhookController.php`
The `test` endpoint triggers an outbound HTTP request. Ensure it cannot be abused as a server-side request forgery (SSRF) vector.
**Acceptance Criteria:**
- Validate webhook URL against allowlist or blocklist
- Prevent requests to internal IP ranges (127.0.0.0/8, 10.0.0.0/8, etc.)
- Add timeout and response size limits
---
### SEC-006: Validate workspace_id in RequireWorkspaceContext middleware
**Status:** Open
**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.
**Acceptance Criteria:**
- Always validate user has access to the resolved workspace
- Make `validate` parameter the default behaviour
- Log workspace access attempts for security monitoring
---
## P2 - High Priority
### DX-001: Add strict_types declaration to all PHP files
**Status:** Open
**Files:** Multiple files missing declaration
Several files are missing `declare(strict_types=1);`:
- `Models/Workspace.php`
- `Models/User.php`
- `Services/EntitlementService.php`
**Acceptance Criteria:**
- Add strict_types to all PHP files
- Run tests to verify no type coercion issues
---
### DX-002: Document EntitlementService public API
**Status:** Open
**File:** `Services/EntitlementService.php`
The EntitlementService is the core API for entitlement checks but lacks comprehensive PHPDoc. External consumers need clear documentation.
**Acceptance Criteria:**
- Add complete PHPDoc to all public methods
- Document exception conditions
- Add @throws annotations where applicable
- Create usage examples in documentation
---
### TEST-001: Add tests for namespace-level entitlements
**Status:** Open
**File:** `tests/Feature/EntitlementServiceTest.php`
The test file covers workspace-level entitlements but not namespace-level (`canForNamespace`, `recordNamespaceUsage`, etc.).
**Acceptance Criteria:**
- Test `canForNamespace()` with various ownership scenarios
- Test entitlement cascade (namespace -> workspace -> user tier)
- Test `provisionNamespacePackage()` and `provisionNamespaceBoost()`
- Test namespace cache invalidation
---
### TEST-002: Add integration tests for EntitlementApiController
**Status:** Open
**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)
---
### PERF-001: Optimise EntitlementService cache invalidation
**Status:** Open
**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
---
### PERF-002: Add database indexes for common queries
**Status:** Open
**File:** `Migrations/0001_01_01_000000_create_tenant_tables.php`
Missing indexes identified:
- `users.tier` (for tier-based queries)
- `namespaces.slug` (currently only unique in combination)
- `entitlement_usage_records.user_id`
**Acceptance Criteria:**
- Create migration adding missing indexes
- Verify query plan improvements with EXPLAIN
---
### 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._
<!-- Example:
### SEC-001: Add rate limiting to API
**Status:** Done (2026-01-29)
**PR:** #123
-->