Laravel backend API for SecPal - Digital guard book and security service management
SecPal API is the backend service for the SecPal platform, built with Laravel 12 and PostgreSQL. It provides a RESTful API for managing security service operations, guard books, and related functionality.
- Framework: Laravel 12
- Database: PostgreSQL
- Testing: PEST
- Code Style: Laravel Pint (PSR-12)
- Static Analysis: PHPStan (Level Max) with Larastan
- PHP Version: 8.4+
Comprehensive RBAC system with temporal role assignments and direct permission management.
Features:
- 5 Predefined Roles: Admin, Manager, Guard, Client, Works Council
- 52 Permissions across 7 resources (employees, shifts, work_instructions, roles, permissions, works_council, reports)
- Temporal Role Assignments: Assign roles with
valid_from/valid_untildates for automatic expiration - Direct Permissions: Assign permissions directly to users, bypassing roles for fine-grained control
- Permission Inheritance: User permissions = Role permissions ∪ Direct permissions
- Idempotent Seeder: Predefined roles auto-recreate if deleted
- 16 REST API Endpoints: Full CRUD for roles, permissions, assignments, and direct permissions
API Examples:
# List all roles with counts
GET /v1/roles
# Assign role to user with expiration
POST /v1/users/{id}/roles
{
"role": "Manager",
"valid_from": "2025-11-15T00:00:00Z",
"valid_until": "2025-12-31T23:59:59Z"
}
# Assign direct permission (bypass role)
POST /v1/users/{id}/permissions
{
"permissions": ["employees.export", "reports.generate"]
}
# List user's all permissions (role + direct)
GET /v1/users/{id}/permissions
# Returns: { "via_roles": [...], "direct": [...], "all": [...] }Documentation:
- PHP 8.4 or higher
- Composer 2.x
- PostgreSQL 15+ or 16+
- Extensions:
mbstring,xml,ctype,iconv,intl,pdo_pgsql
git clone https://github.com/SecPal/api.git
cd apicomposer installcp .env.example .env
php artisan key:generateEdit .env and configure your database:
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=secpal
DB_USERNAME=your_username
DB_PASSWORD=your_passwordphp artisan migrateSecPal uses envelope encryption for sensitive data. You must generate a KEK file before running the application:
# Create the keys directory if it doesn't exist
mkdir -p storage/keys
# Generate a random 256-bit KEK and store it securely
php -r "file_put_contents('storage/keys/kek.key', random_bytes(32));"
chmod 0600 storage/keys/kek.keyIMPORTANT: Never commit the KEK file! It's already in .gitignore.
Add the KEK path to your .env:
KEK_PATH=storage/keys/kek.keyFor development, use the relative path above. In production, set
KEK_PATHto the absolute path of your KEK file (ideally outside the web root), and ensure file permissions are0600.
SecPal provides Artisan commands for key lifecycle management:
# Generate new tenant with envelope keys
php artisan keys:generate-tenant
# Rotate KEK and re-wrap all tenant keys (creates backup)
php artisan keys:rotate-kek
# Rotate DEK for specific tenant (re-encrypts all data)
php artisan keys:rotate-dek {tenant_id}
# Rebuild blind indexes for specific tenant
php artisan idx:rebuild {tenant_id}Best Practices:
- Rotate KEK annually or after suspected compromise
- Rotate tenant DEKs when offboarding users with access
- Keep KEK backups (created by
keys:rotate-kek) in secure offline storage - Test rotation procedures in staging before production
# Install pre-commit hooks
./scripts/setup-pre-commit.sh
# Install pre-push hooks
./scripts/setup-pre-push.shphp artisan serveThe API will be available at http://localhost:8000.
Code Style (Laravel Pint):
# Check code style
./vendor/bin/pint --test
# Auto-fix code style
./vendor/bin/pintStatic Analysis (PHPStan):
./vendor/bin/phpstan analyseTesting (PEST):
# Run all tests
./vendor/bin/pest
# Run tests in parallel
./vendor/bin/pest --parallel
# Run specific test
./vendor/bin/pest --filter=ExampleTest
# Run with coverage (requires pcov or xdebug extension)
./vendor/bin/pest --coverage --min=80Note: Coverage requires pcov (preferred) or xdebug. Install via:
# For pcov (faster, recommended)
pecl install pcov
sudo sh -c 'echo "extension=pcov.so" > /etc/php/$(php -r "echo PHP_MAJOR_VERSION.\".\".PHP_MINOR_VERSION;")/cli/conf.d/99-pcov.ini'
# If you do not have root privileges, add to your user-level php.ini:
# Find your php.ini location: php --ini
# Add: extension=pcov.soCI workflows automatically have coverage enabled.
Before committing, the following checks run automatically:
- REUSE compliance
- Code formatting (Prettier, markdownlint)
- YAML linting
Before pushing, the preflight script runs:
- All pre-commit checks
- Laravel Pint code style check
- PHPStan static analysis
- PEST tests with ≥80% coverage
- PR size check (600 lines)
To run manually:
./scripts/preflight.shTo bypass (not recommended):
git push --no-verifyBefore each commit/PR, ensure:
- ✅ KEK file exists at
storage/keys/kek.keywith permissions0600 - ✅
.envhasKEK_PATHset correctly - ✅ Database connection is configured and migrations ran
- ✅
./vendor/bin/pintpasses (PSR-12) - ✅
./vendor/bin/phpstan analysepasses (level max) - ✅
./vendor/bin/pest --coverage --min=80passes - ✅ No hardcoded secrets in code
- ✅ REUSE compliance (all files have license headers)
api/
├── app/ # Application code
│ ├── Http/ # Controllers, Middleware, Requests, Resources
│ ├── Models/ # Eloquent models
│ └── Providers/ # Service providers
├── config/ # Configuration files
├── database/ # Migrations, factories, seeders
├── routes/ # API routes
├── tests/ # PEST tests
│ ├── Unit/ # Unit tests
│ └── Feature/ # Feature/Integration tests
├── scripts/ # Development scripts
└── storage/ # Logs, cache, uploads
API documentation is maintained separately in the contracts repository using OpenAPI 3.1 specification.
SecPal implements a comprehensive Role-Based Access Control (RBAC) system with temporal role assignments and direct permission management.
- Role-based access control with predefined roles (Admin, Manager, Guard, Client, Works Council)
- Temporal role assignments with automatic expiration for time-limited access
- Direct permissions allowing exceptions without creating new roles
- All roles are equal and fully manageable (no system/custom distinction)
- Idempotent seeder recreates deleted predefined roles
All roles are equal - predefined roles (Admin, Manager, etc.) can be deleted if not assigned to users. Deleted predefined roles are automatically recreated on next seeder run. This approach provides simplicity and flexibility without artificial distinctions. See ADR-005 for rationale.
Users can have permissions assigned directly, bypassing roles entirely. This allows exceptional access without creating single-use roles. Permission hierarchy: User Permissions = Role Permissions ∪ Direct Permissions. See docs/guides/direct-permissions.md for detailed patterns.
Role and permission assignments are permanent by default. Temporal constraints (valid_from, valid_until) are optional for time-limited access (vacation coverage, projects, events). See docs/guides/temporal-roles.md for use cases.
Assign Permanent Role:
POST /v1/users/{id}/roles
{"role": "manager"}Assign Temporal Role:
POST /v1/users/{id}/roles
{
"role": "manager",
"valid_until": "2025-12-14T23:59:59Z"
}Assign Direct Permission:
POST /v1/users/{id}/permissions
{"permissions": ["employees.export"]}- Architecture Overview:
docs/rbac-architecture.md - Direct Permissions Guide:
docs/guides/direct-permissions.md - Temporal Roles Guide:
docs/guides/temporal-roles.md - Design Decisions: ADR-005
- API Documentation: Issue #140
This repository uses automated project board management. Issues and PRs are automatically added to the SecPal Roadmap with status based on labels and PR state.
Quick Start:
# Create issue (auto-added to project board)
gh issue create --label "enhancement" --title "..."
# Draft PR workflow (recommended)
gh pr create --draft --body "Closes #123" # → 🚧 In Progress
gh pr ready <PR> # → 👀 In Review
gh pr merge <PR> --squash # → ✅ DoneSee Project Automation docs for details.
Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests.
The main branch is protected with the following rules:
- Required status checks must pass
- Pull request reviews required
- Conversations must be resolved
- Force pushes are disabled
- Deletions are disabled
- Branch protections apply to administrators
- Keep PRs small (< 600 lines changed)
- Write descriptive commit messages
- Include tests for new features
- Update documentation as needed
- Ensure all CI checks pass
This project uses a dual-licensing model:
Licensed under AGPL-3.0-or-later for:
- Open source projects compliant with AGPL
- Personal use and experimentation
- Educational purposes
- Community contributions
For use cases incompatible with AGPL, commercial licenses are available. Contact us for details.
See LICENSE for full details.
SecPal implements multi-tenant envelope encryption with the following security properties:
Key Hierarchy:
- KEK (Key Encryption Key): Master key stored in
storage/keys/kek.key(mode 0600) - Per-Tenant DEK: Data Encryption Key for encrypting PII fields (email, phone, notes)
- Per-Tenant idx_key: Index key for generating blind indexes (searchable without decryption)
Encrypted Fields:
email_enc,phone_enc,note_enc- Encrypted with tenant DEK using XChaCha20-Poly1305- Stored as JSON:
{"ciphertext": "base64", "nonce": "base64"}
Blind Indexes:
email_idx,phone_idx- HMAC-SHA256 of normalized values using idx_key- Enable equality search without decryption
- Tenant-isolated (same email in different tenants produces different indexes)
✅ What SecPal Protects Against:
- Database compromise (all PII encrypted at rest)
- Cross-tenant data access (tenant-specific keys + middleware isolation)
- Unauthorized API access (Sanctum PAT authentication + Spatie RBAC)
- Full-Text Search Leakage: The
note_tsvfield contains plaintext tokens for FTS. If FTS on notes is required, accept this trade-off or implement separate FTS infrastructure. - Blind Index Frequency Analysis: Repeated values (e.g., common email domains) can be detected through blind index frequency patterns.
- Application-Level Access: Authenticated users with proper permissions can decrypt data (by design).
🔒 Operational Security:
- Never commit KEK file (already in
.gitignore) - Store production KEK outside web root with 0600 permissions
- Use key rotation commands regularly (see "Key Rotation" section above)
- Monitor
storage/logsfor any accidental PII leakage (tests enforce this) - Backup KEK securely before rotation (kept by
keys:rotate-kek)
See SECURITY.md for information about reporting security vulnerabilities.
This project adheres to the Contributor Covenant Code of Conduct.
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: Project Wiki
SecPal uses Translation.io for managing translations. Translation.io offers free unlimited accounts for open source projects.
- SecPal/.github - Organization-wide settings
- SecPal/contracts - OpenAPI specifications
- SecPal/frontend - React frontend application
SecPal - Empowering security services with digital solutions.