This document shows how the system protects sensitive tables like api_users from unauthorized access.
Setup:
- User "john" has role "readonly"
- Role "readonly" has wildcard permissions:
'*' => ['list', 'read'] - But explicitly denies:
'api_users' => []
# Admin has full access to all tables including api_users
curl -u admin:secret \
"http://localhost/PHP-CRUD-API-Generator/public/index.php?action=list&table=api_users"
# Expected Response: 200 OK
{
"data": [
{"id": 1, "username": "admin", "role": "admin", ...},
{"id": 2, "username": "john", "role": "readonly", ...}
],
"meta": {"page": 1, "total": 2, ...}
}✅ PASS - Admin should see all users
# Readonly user tries to list api_users table
curl -u john:password123 \
"http://localhost/PHP-CRUD-API-Generator/public/index.php?action=list&table=api_users"
# Expected Response: 403 Forbidden
{
"error": "Forbidden: readonly cannot list on api_users"
}✅ PASS - Readonly user is blocked
# Same user tries to list a regular table (e.g., posts, products)
curl -u john:password123 \
"http://localhost/PHP-CRUD-API-Generator/public/index.php?action=list&table=posts"
# Expected Response: 200 OK
{
"data": [...],
"meta": {...}
}✅ PASS - Regular tables work fine
# User with API key tries to access api_users
curl -H "X-API-Key: johns-readonly-api-key" \
"http://localhost/PHP-CRUD-API-Generator/public/index.php?action=list&table=api_users"
# Expected Response: 403 Forbidden
{
"error": "Forbidden: readonly cannot list on api_users"
}✅ PASS - API key auth also respects RBAC
'roles' => [
'admin' => [
'*' => ['list', 'read', 'create', 'update', 'delete']
],
'readonly' => [
'*' => ['list', 'read'], // Wildcard allows all tables
'api_users' => [], // But DENY api_users explicitly
'api_key_usage' => [], // And other system tables
],
],public function isAllowed(string $role, string $table, string $action): bool
{
// ...
// Check for explicit DENY (takes precedence over wildcards)
if (isset($perms[$table])) {
if (empty($perms[$table])) {
return false; // ← Empty array = DENY
}
// ...
}
// Wildcard only applies if table not explicitly defined
// ...
}Every API action calls enforceRbac():
case 'list':
$this->enforceRbac('list', $query['table']); // ← Checks RBAC
$result = $this->api->list($query['table'], $opts);By default, these system tables should be protected:
| Table | Purpose | Who Can Access |
|---|---|---|
api_users |
User credentials & API keys | Admin only |
api_key_usage |
Usage tracking | Admin only |
Any table starting with _system |
Internal system tables | Admin only |
To protect additional tables, add them to readonly role config:
'readonly' => [
'*' => ['list', 'read'],
'api_users' => [], // Deny
'api_key_usage' => [], // Deny
'audit_logs' => [], // Deny
'payment_methods' => [], // Deny
'internal_notes' => [], // Deny
],- ✅ Default Deny - Explicitly list protected tables
- ✅ Least Privilege - Give users minimum permissions needed
- ✅ Test Thoroughly - Run these tests after any RBAC changes
- ✅ Monitor Access - Log all attempts to access sensitive tables
- ✅ Regular Audits - Review role permissions quarterly
Run all tests at once:
# Save as test_rbac.sh or test_rbac.ps1
echo "Test 1: Admin access to api_users..."
curl -u admin:secret \
"http://localhost/PHP-CRUD-API-Generator/public/index.php?action=list&table=api_users"
echo -e "\n\nTest 2: Readonly blocked from api_users..."
curl -u john:password123 \
"http://localhost/PHP-CRUD-API-Generator/public/index.php?action=list&table=api_users"
echo -e "\n\nTest 3: Readonly can access regular tables..."
curl -u john:password123 \
"http://localhost/PHP-CRUD-API-Generator/public/index.php?action=list&table=posts"- Readonly users CANNOT list
api_userstable - Readonly users CANNOT read specific
api_usersrecords - Readonly users CANNOT create/update/delete
api_users - Admin users CAN access
api_usersnormally - Readonly users CAN access non-protected tables
- RBAC works with both Basic Auth and API Keys
- Monitoring logs access attempts to protected tables
If you need to debug or temporarily allow access:
// config/api.php
'readonly' => [
'*' => ['list', 'read'],
// 'api_users' => [], // ← Comment out to allow access
],Security Issue Resolved: ✅ System tables are now protected from unauthorized access via RBAC.