c77_rbac PostgreSQL Extension
A PostgreSQL extension that provides Role-Based Access Control (RBAC) with Row-Level Security (RLS) for enterprise applications. This extension pushes authorization logic to the database layer, ensuring consistent security across all application frameworks and direct database access.
Features
- Database-Centric Authorization: Authorization rules enforced at the database level
- Row-Level Security: Fine-grained access control on individual rows
- Scope-Based Permissions: Support for department, region, or any custom scope
- Global Admin Support: Special
global/allscope for administrative access - Framework Agnostic: Works with any application framework (Laravel, Rails, Django, etc.)
- Dynamic Schema Support: Works with any PostgreSQL schema
- Performance Optimized: Includes indexes and efficient access checks
Requirements
- PostgreSQL 14 or later
- Superuser access for initial installation
Installation
-
Copy extension files to PostgreSQL directory:
sudo cp c77_rbac.control /usr/share/postgresql/14/extension/ sudo cp c77_rbac--1.0.sql /usr/share/postgresql/14/extension/ -
Install the extension (requires superuser):
-- Connect as superuser CREATE DATABASE your_db; CREATE USER app_user WITH PASSWORD 'secure_password'; -- Install extension \c your_db CREATE EXTENSION c77_rbac; -- Grant necessary privileges to application user GRANT CREATE ON DATABASE your_db TO app_user; GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_user; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO app_user; GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_user;
Core Concepts
1. Subjects (Users)
- Identified by an
external_id(typically your application's user ID) - Can have multiple roles with different scopes
2. Roles
- Named collections of features (permissions)
- Can be scoped to specific contexts (department, region, etc.)
3. Features
- Specific permissions that can be checked in policies
- Examples:
view_reports,edit_users,delete_records
4. Scopes
- Context for role assignments
- Examples:
department/engineering,region/north,global/all
Basic Usage
1. Define Features and Roles
-- Define features (permissions)
SELECT public.c77_rbac_grant_feature('manager', 'view_reports');
SELECT public.c77_rbac_grant_feature('manager', 'edit_reports');
SELECT public.c77_rbac_grant_feature('admin', 'manage_users');
-- Admin roles should have all specific features
SELECT public.c77_rbac_grant_feature('admin', 'view_reports');
SELECT public.c77_rbac_grant_feature('admin', 'edit_reports');
2. Assign Users to Roles
-- Assign user to manager role for engineering department
SELECT public.c77_rbac_assign_subject('123', 'manager', 'department', 'engineering');
-- Assign admin with global access
SELECT public.c77_rbac_assign_subject('1', 'admin', 'global', 'all');
3. Apply Row-Level Security
-- Apply RLS policy to a table
SELECT public.c77_rbac_apply_policy(
'myschema.reports', -- table name (can be schema-qualified)
'view_reports', -- required feature
'department', -- scope type
'department_id' -- column containing scope value
);
4. Set User Context
-- Set the current user for RLS checks
SET "c77_rbac.external_id" TO '123';
-- Now queries automatically filter based on permissions
SELECT * FROM myschema.reports; -- Only shows reports for user's department
Admin Management
Administrators with global/all scope need explicit feature grants. Use helper functions to manage this:
-- Sync all features to admin role
SELECT public.c77_rbac_sync_admin_features();
-- Or sync to all roles with global/all scope
SELECT public.c77_rbac_sync_global_admin_features();
Integration Examples
Laravel Integration
// Middleware to set user context
public function handle($request, Closure $next)
{
if (Auth::check()) {
DB::statement('SET "c77_rbac.external_id" TO ?', [Auth::id()]);
}
return $next($request);
}
// Check permissions
$canView = DB::selectOne("
SELECT public.c77_rbac_can_access(?, ?, ?, ?) AS allowed
", ['view_reports', Auth::id(), 'department', 'engineering'])->allowed;
Schema-Aware Usage
-- Works with any schema
CREATE SCHEMA finance;
CREATE TABLE finance.accounts (...);
-- Apply RLS with schema qualification
SELECT public.c77_rbac_apply_policy(
'finance.accounts',
'view_finance',
'department',
'dept_id'
);
Available Functions
Core Functions
c77_rbac_assign_subject(external_id, role, scope_type, scope_id)- Assign role to userc77_rbac_grant_feature(role, feature)- Grant feature to rolec77_rbac_can_access(feature, external_id, scope_type, scope_id)- Check accessc77_rbac_apply_policy(table, feature, scope_type, column)- Apply RLS policy
Admin Helper Functions
c77_rbac_sync_admin_features()- Sync all features to admin rolec77_rbac_sync_global_admin_features()- Sync features to all global/all roles
Maintenance Functions
c77_rbac_show_dependencies()- Show all dependencies on the extensionc77_rbac_remove_all_policies()- Remove all RLS policiesc77_rbac_cleanup_for_removal(remove_data)- Prepare for extension removal
Uninstallation
-
Check dependencies:
SELECT * FROM public.c77_rbac_show_dependencies(); -
Remove policies and optionally data:
-- Just remove policies SELECT public.c77_rbac_remove_all_policies(); -- Or remove policies and all RBAC data SELECT public.c77_rbac_cleanup_for_removal(true); -
Drop the extension:
DROP EXTENSION c77_rbac CASCADE;
Best Practices
-
Feature Naming Convention:
- Use prefixes:
view_*,edit_*,delete_*,manage_* - Be specific:
view_financial_reportsvsview_reports
- Use prefixes:
-
Admin Setup:
- Always grant specific features to admin roles
- Use sync functions after adding new features
- Document all features in your application
-
Performance:
- The extension includes optimized indexes
- Use explain analyze to verify query plans
- Consider materialized views for complex permission checks
-
Security:
- Always use parameterized queries
- Reset session variables in connection pools
- Audit role assignments regularly
Troubleshooting
No Data Returned
- Check if
c77_rbac.external_idis set correctly - Verify user has the required role and features
- Ensure RLS is enabled on the table
- Check that policies reference the correct columns
Policy Not Working
- Verify column names match between table and policy
- Check feature names match exactly
- Ensure scope types and IDs align
Performance Issues
- Verify indexes exist on RBAC tables
- Check query plans with EXPLAIN ANALYZE
- Consider caching permission checks in your application
Contributing
This extension is designed to be framework-agnostic. When contributing:
- Keep the core extension simple and focused
- Add framework-specific features to companion extensions
- Include tests for new functionality
- Update documentation for new features
License
MIT License - See LICENSE file for details
Support
- Create an issue for bugs or feature requests
- Check existing issues before creating new ones
- Include PostgreSQL version and reproduction steps for bugs
For framework-specific extensions, see:
- c77_rbac_laravel - Laravel integration