A comprehensive, production-ready authorization system with role-based access control (RBAC), audit logging, encryption, and high availability features.
- Role-Based Access Control (RBAC) - Hierarchical user, role, and permission management
- Multiple Storage Backends - SQLite (development) and PostgreSQL (production)
- Dual Interface - REST API and Python library
- JWT Authentication - Secure token-based authentication
- Data Encryption - Optional encryption for sensitive data fields
- Audit Logging - Comprehensive audit trail for compliance
- Workflow Permissions - APScheduler integration for workflow permission checking
- UUID4-based client authentication
- JWT token-based authorization
- Deterministic field-level encryption (AES-256-CTR) - Queryable encrypted data
- Comprehensive audit logging with timestamps
- Input validation and sanitization
- CORS configuration
- Connection pooling with retry logic
- Circuit breaker pattern for fault tolerance
- Configurable CORS settings
- Health check endpoint
- Consistent API response formats
- Extensive test coverage (152 tests)
- Python 3.9+
- PostgreSQL (for production) or SQLite (for development/testing)
pip install -r requirements.txtDevelopment (SQLite):
python -m auth.mainProduction (PostgreSQL):
export AUTH_DATABASE_TYPE=postgresql
export AUTH_POSTGRESQL_URL=postgresql://username:password@localhost:5432/auth_db
export AUTH_JWT_SECRET_KEY=your_secure_secret_key
export AUTH_ENABLE_ENCRYPTION=true
export AUTH_ENCRYPTION_KEY=your_encryption_key
python -m auth.mainThe server will start on http://127.0.0.1:5000
Run the showcase script to test all API endpoints:
bash showcase_api.shimport uuid
from auth import Authorization
# Generate a client key (UUID4)
client_key = str(uuid.uuid4())
# Create authorization instance
auth = Authorization(client_key)
# Create roles
auth.add_role('admin', description='Administrator role')
auth.add_role('editor', description='Content editor role')
auth.add_role('viewer', description='Read-only role')
# Add permissions to roles
auth.add_permission('admin', 'manage_users')
auth.add_permission('admin', 'edit_content')
auth.add_permission('admin', 'view_content')
auth.add_permission('editor', 'edit_content')
auth.add_permission('editor', 'view_content')
auth.add_permission('viewer', 'view_content')
# Add users to roles
auth.add_membership('alice@example.com', 'admin')
auth.add_membership('bob@example.com', 'editor')
auth.add_membership('charlie@example.com', 'viewer')# Check if user has specific permission
if auth.user_has_permission('alice@example.com', 'manage_users'):
print("Alice can manage users")
# Check if user belongs to a role
if auth.has_membership('bob@example.com', 'editor'):
print("Bob is an editor")
# Check if role has permission
if auth.has_permission('viewer', 'view_content'):
print("Viewers can view content")# Get all permissions for a user
permissions = auth.get_user_permissions('alice@example.com')
print(f"Alice's permissions: {[p['name'] for p in permissions]}")
# Get all roles for a user
roles = auth.get_user_roles('bob@example.com')
print(f"Bob's roles: {[r['role'] for r in roles]}")
# Get all users with a specific permission
users = auth.which_users_can('edit_content')
print(f"Users who can edit: {[u['user'] for u in users]}")
# Get all roles with a specific permission
roles = auth.which_roles_can('manage_users')
print(f"Roles that can manage users: {[r['role'] for r in roles]}")# Get all members of a role
members = auth.get_role_members('admin')
print(f"Admin users: {[m['user'] for m in members]}")
# Get all permissions for a role
permissions = auth.get_permissions('editor')
print(f"Editor permissions: {[p['name'] for p in permissions]}")
# Get all roles
all_roles = auth.roles
print(f"All roles: {[r['role'] for r in all_roles]}")# Remove permission from role
auth.del_permission('editor', 'edit_content')
# Remove user from role
auth.del_membership('charlie@example.com', 'viewer')
# Delete role (also removes all memberships and permissions)
auth.del_role('viewer')All API endpoints require a valid UUID4 Bearer token in the Authorization header.
import uuid
from auth.client import EnhancedAuthClient
# Generate a client key
client_key = str(uuid.uuid4())
# Create client instance
client = EnhancedAuthClient(
api_key=client_key,
service_url='http://127.0.0.1:5000'
)
# Create a role
response = client.create_role('admin')
print(response)
# Add permission to role
response = client.add_permission('admin', 'manage_users')
print(response)
# Add user to role
response = client.add_membership('alice@example.com', 'admin')
print(response)
# Check user permission
response = client.user_has_permission('alice@example.com', 'manage_users')
print(response)Health Check:
curl http://localhost:5000/pingCreate a Role:
CLIENT_KEY=$(uuidgen)
curl -X POST \
http://localhost:5000/api/role/admin \
-H "Authorization: Bearer $CLIENT_KEY" \
-H "Content-Type: application/json"Add Permission to Role:
curl -X POST \
http://localhost:5000/api/permission/admin/manage_users \
-H "Authorization: Bearer $CLIENT_KEY" \
-H "Content-Type: application/json"Add User to Role:
curl -X POST \
http://localhost:5000/api/membership/alice@example.com/admin \
-H "Authorization: Bearer $CLIENT_KEY" \
-H "Content-Type: application/json"Check User Permission:
curl -X GET \
http://localhost:5000/api/has_permission/alice@example.com/manage_users \
-H "Authorization: Bearer $CLIENT_KEY"Get User Permissions:
curl -X GET \
http://localhost:5000/api/user_permissions/alice@example.com \
-H "Authorization: Bearer $CLIENT_KEY"Get User Roles:
curl -X GET \
http://localhost:5000/api/user_roles/alice@example.com \
-H "Authorization: Bearer $CLIENT_KEY"Get Role Members:
curl -X GET \
http://localhost:5000/api/members/admin \
-H "Authorization: Bearer $CLIENT_KEY"Find Users with Permission:
curl -X GET \
http://localhost:5000/api/which_users_can/manage_users \
-H "Authorization: Bearer $CLIENT_KEY"Find Roles with Permission:
curl -X GET \
http://localhost:5000/api/which_roles_can/manage_users \
-H "Authorization: Bearer $CLIENT_KEY"| Method | Endpoint | Description |
|---|---|---|
| GET | /ping |
Health check endpoint |
| GET | /api/roles |
List all roles |
| POST | /api/role/{role} |
Create a new role |
| DELETE | /api/role/{role} |
Delete a role |
| GET | /api/permission/{group}/{name} |
Check if role has permission |
| POST | /api/permission/{group}/{name} |
Add permission to role |
| DELETE | /api/permission/{group}/{name} |
Remove permission from role |
| GET | /api/membership/{user}/{group} |
Check if user is in role |
| POST | /api/membership/{user}/{group} |
Add user to role |
| DELETE | /api/membership/{user}/{group} |
Remove user from role |
| GET | /api/has_permission/{user}/{name} |
Check if user has permission |
| GET | /api/user_permissions/{user} |
Get all permissions for user |
| GET | /api/user_roles/{user} |
Get all roles for user |
| GET | /api/role_permissions/{role} |
Get all permissions for role |
| GET | /api/members/{role} |
Get all members of role |
| GET | /api/which_roles_can/{name} |
Get roles with specific permission |
| GET | /api/which_users_can/{name} |
Get users with specific permission |
All API responses follow this format:
{
"success": true,
"code": 200,
"message": "Operation completed successfully",
"data": { ... },
"timestamp": "2025-11-21T12:34:56.789012"
}{
"success": false,
"code": 400,
"message": "Invalid input",
"error": "Detailed error message",
"timestamp": "2025-11-21T12:34:56.789012"
}| Variable | Description | Default |
|---|---|---|
AUTH_DATABASE_TYPE |
Database type (sqlite or postgresql) | sqlite |
AUTH_DATABASE_URL |
Full database connection URL (overrides other settings) | - |
AUTH_POSTGRESQL_URL |
PostgreSQL connection string | - |
AUTH_SQLITE_PATH |
SQLite database path | ~/.auth.sqlite3 |
AUTH_JWT_SECRET_KEY |
Secret key for JWT tokens | (auto-generated) |
AUTH_JWT_ALGORITHM |
JWT algorithm | HS256 |
AUTH_JWT_ACCESS_TOKEN_EXPIRE_MINUTES |
Token expiration time | 1440 |
AUTH_JWT_REFRESH_TOKEN_EXPIRE_DAYS |
Refresh token expiration time | 7 |
AUTH_ENABLE_ENCRYPTION |
Enable data encryption | false |
AUTH_ENCRYPTION_KEY |
Encryption key for sensitive data | - |
AUTH_SERVER_HOST |
Server host | 127.0.0.1 |
AUTH_SERVER_PORT |
Server port | 8000 |
AUTH_DEBUG_MODE |
Debug mode | false |
AUTH_ALLOW_CORS |
Enable CORS | true |
AUTH_CORS_ORIGINS |
Allowed CORS origins | * |
AUTH_ENABLE_AUDIT_LOGGING |
Enable audit logging | true |
You can also use a .env file:
AUTH_DATABASE_TYPE=postgresql
AUTH_POSTGRESQL_URL=postgresql://user:pass@localhost:5432/authdb
AUTH_JWT_SECRET_KEY=your-secret-key-here
AUTH_ENABLE_ENCRYPTION=true
AUTH_ENCRYPTION_KEY=your-encryption-key-here
AUTH_SERVER_PORT=8000pip install waitress
waitress-serve --host=0.0.0.0 --port=5000 --threads=10 auth.main:apppip install gunicorn
gunicorn -w 4 -b 0.0.0.0:5000 auth.main:appdocker-compose up -dRun the full test suite:
python -m pytest tests/ -vRun specific test categories:
# Flask API tests
python -m pytest tests/test_flask.py -v
# Authorization tests
python -m pytest tests/test_authorization.py -v
# Database tests
python -m pytest tests/test_db_*.py -v┌─────────────────────────────────────────────┐
│ API Layer (Flask) │
│ - REST endpoints │
│ - Request validation │
│ - Response formatting │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ Service Layer (Business Logic) │
│ - Authorization rules │
│ - Permission checking │
│ - Audit logging │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ Data Access Layer (DAL) │
│ - SQLAlchemy ORM │
│ - Database abstraction │
│ - Encryption/Decryption │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ Database (SQLite/PostgreSQL) │
│ - User data │
│ - Role & Permission mappings │
│ - Audit logs │
└─────────────────────────────────────────────┘
The Auth system supports deterministic field-level encryption for sensitive data using AES-256-CTR with HMAC-derived initialization vectors.
Deterministic Encryption:
- Same plaintext always produces the same ciphertext
- Enables database queries on encrypted fields (user lookups, permission checks)
- Uses AES-256-CTR with PBKDF2-derived keys (100,000 iterations)
- HMAC-based IV generation ensures determinism
What's Encrypted:
- User names in
auth_membershiptable - Permission names in
auth_permissiontable - Optional: Role descriptions
Example:
# Without encryption
Database stores: "john", "jane", "admin"
# With deterministic encryption enabled
Database stores: "xxqjTSaj0YGZD7v8khExdKkV+dA=", "sJ4Yaz56uRxmNF0mj3wOwUNE8Y8=", ...- Generate a secure encryption key:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"- Update
.envfile:
AUTH_ENABLE_ENCRYPTION=true
AUTH_ENCRYPTION_KEY=your_generated_key_here- Restart the service - encryption is applied automatically to all new data
Deterministic vs Non-Deterministic:
- Deterministic (current): Same value = same ciphertext (required for queries)
- Trade-off: Pattern analysis possible (acceptable for usernames/permissions)
- Benefit: Full database functionality with encrypted data
Best Practices:
- Use strong encryption keys (32+ characters)
- Rotate keys periodically
- Store keys securely (environment variables, secrets management)
- Never commit encryption keys to version control
- Use PostgreSQL in Production - SQLite is for development only
- Enable Encryption - Set
AUTH_ENABLE_ENCRYPTION=truefor sensitive data - Secure JWT Secret - Use a strong, unique
AUTH_JWT_SECRET_KEY - Use HTTPS - Always use TLS/SSL in production
- Rotate Keys - Regularly rotate encryption and JWT keys
- Audit Logs - Monitor audit logs for suspicious activity
- Client Keys - Keep UUID4 client keys secure and confidential
See the showcase_api.sh script for a complete example demonstrating all API features.
If you encounter database connection errors:
# For PostgreSQL
export AUTH_POSTGRESQL_URL=postgresql://user:pass@localhost:5432/dbname
# Check PostgreSQL is running
psql -U user -d dbname -c "SELECT 1"Ensure your client key is a valid UUID4:
import uuid
client_key = str(uuid.uuid4()) # Correct formatIf encryption is enabled, ensure the encryption key is set:
export AUTH_ENABLE_ENCRYPTION=true
export AUTH_ENCRYPTION_KEY=your-32-character-key-here- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
# Run all tests
python -m pytest tests/ -v
# Run with coverage
python -m pytest tests/ --cov=auth --cov-report=html
# Run specific test file
python -m pytest tests/test_flask.py -vMIT License - see LICENSE file for details
© Farshid Ashouri @RODMENA LIMITED
For issues and questions, please open an issue on GitHub.