-
Notifications
You must be signed in to change notification settings - Fork 91
Cedarling PostgreSQL Extension
Haileyesus Ayanaw edited this page Aug 26, 2025
·
4 revisions
A PostgreSQL extension that provides fine-grained row-level access control using Cedarling policy engine and JWT tokens, designed for easy adoption with ORMs and modern application stacks.
The extension integrates Cedarling authorization with PostgreSQL's Row-Level Security (RLS) to automatically filter database rows based on JWT token claims and Cedar policies. It's designed to work seamlessly with existing ORMs and application architectures.
Flow: SQL Query → RLS Policy → Cedarling Extension → Cedar Policy Engine → Allow/Deny Row
# Install dependencies (Rust, pgrx, PostgreSQL dev libraries)
cargo pgrx new cedarling_pg
cd cedarling_pg
# Add dependency to Cargo.toml
[dependencies]
cedarling = { path = "../path/to/cedarling" }
# Build and install
cargo pgrx install
# Enable in PostgreSQL
CREATE EXTENSION cedarling_pg;
Use typed helpers to avoid manual JSON construction:
-- 1. Enable RLS on table
ALTER TABLE students ENABLE ROW LEVEL SECURITY;
-- 2. Create policy using typed helper (no JSON needed)
CREATE POLICY student_access ON students
USING (cedarling_authorized_row(students, 'Read'));
-- 3. ORM-friendly token management
SELECT cedarling_set_tokens('{"access_token": "...", "id_token": "..."}');
SELECT * FROM students; -- Automatically filtered
SELECT cedarling_clear_tokens();
# Middleware approach - works with any ORM
class CedarlingMiddleware:
def __call__(self, request):
with connection.cursor() as cursor:
cursor.execute("SELECT cedarling_set_tokens(%s)", [request.user.jwt_tokens])
response = self.get_response(request)
with connection.cursor() as cursor:
cursor.execute("SELECT cedarling_clear_tokens()")
return response
# Now ORM queries work automatically
students = Student.objects.all() # Filtered by RLS
@Component
public class CedarlingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, ...) {
String tokens = extractTokens(request);
jdbcTemplate.execute("SELECT cedarling_set_tokens('" + tokens + "')");
return true;
}
@Override
public void afterCompletion(...) {
jdbcTemplate.execute("SELECT cedarling_clear_tokens()");
}
}
// Repository methods work automatically
@Repository
public class StudentRepository {
public List<Student> findAll() {
return studentRepo.findAll(); // Filtered by RLS
}
}
For high-performance scenarios, use predicate pushdown:
-- Generate optimized WHERE clause from policies
SELECT cedarling_where('students', 'Read', current_tokens());
-- Returns: "department = 'engineering' AND grad_year < 2026"
-- Use in queries for better performance
SELECT * FROM students
WHERE (cedarling_where('students', 'Read', current_tokens()))::boolean;
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
grad_year INTEGER,
department VARCHAR(50)
);
INSERT INTO students (name, grad_year, department) VALUES
('Jim', 2022, 'engineering'),
('Joe', 2023, 'marketing'),
('Jude', 2026, 'engineering');
@id("allow_student_access")
permit(
principal,
action == Jans::Action::"Read",
resource is Jans::Student
) when {
principal.role == "AdmissionsCounselor" ||
(resource.department == principal.department && resource.grad_year < 2026)
};
-- Typed helper (recommended) - auto-maps row to Cedar entity
cedarling_authorized_row(record, action TEXT DEFAULT 'Read') RETURNS BOOLEAN
-- Manual resource construction (for complex cases)
cedarling_authorized(resource_data TEXT, token_bundle TEXT) RETURNS BOOLEAN
-- Build Cedar resource from row
cedarling_build_resource(record) RETURNS TEXT
-- Performance optimization
cedarling_where(table_name TEXT, action TEXT, tokens TEXT) RETURNS TEXT
-- Set tokens for current session/transaction
cedarling_set_tokens(tokens JSONB) RETURNS VOID
-- Clear tokens
cedarling_clear_tokens() RETURNS VOID
-- Get current tokens
cedarling_current_tokens() RETURNS JSONB
-- Explain authorization decision
cedarling_explain(resource_data TEXT, action TEXT) RETURNS JSONB
-- Get last authorization trace
cedarling_last_trace() RETURNS JSONB
-- Health check
cedarling_status() RETURNS JSONB
-- Token management (ORM-friendly)
SELECT cedarling_set_tokens('{"access_token": "...", "id_token": "..."}');
-- Operational modes
SET cedarling.mode = 'enforcement'; -- 'enforcement' | 'instrumentation' | 'shadow'
SET cedarling.fail_mode = 'closed'; -- 'closed' | 'open' (for non-critical reads)
SET cedarling.cache_ttl = 300; -- Cache TTL in seconds
SET cedarling.log_level = 'info'; -- Logging level
-- Check extension health
SELECT * FROM cedarling_status();
-- Returns: {"status": "healthy", "policy_version": "v1.0", "cache_hit_rate": 0.85}
-- Policy version management
SELECT cedarling_use_policy('v2025-08-19.1');
SELECT cedarling_rollback_policy();
-- Fail-safe (default): deny on errors
SET cedarling.fail_mode = 'closed';
-- Fail-open for read-only, low-risk operations (with audit logging)
SET cedarling.fail_mode = 'open';
SET cedarling.audit_fail_open = true;
-- Debug a specific authorization decision
SELECT cedarling_explain(
cedarling_build_resource(students),
'Read'
) FROM students WHERE id = 1;
-- Returns detailed trace:
{
"decision": "Deny",
"policy_id": "allow_student_access",
"reason": "principal.department != resource.department",
"principal_claims": {"department": "marketing"},
"resource_attrs": {"department": "engineering"}
}
# CLI tool to keep schemas in sync
cedarling-pg-codegen --table students --output student.cedarschema
cedarling-pg-codegen --all-tables --output entities/
-- Validate schema compatibility
SELECT cedarling_validate_schema('students', 'path/to/student.cedarschema');
-- Policy diff for safe rollouts
SELECT cedarling_diff_policies('v1.0', 'v1.1');
-
Use Typed Helpers: Avoid manual JSON construction with
cedarling_authorized_row()
-
Transaction Scope: Use
cedarling_set_tokens()
/cedarling_clear_tokens()
pairs - Connection Pooling: Token functions work safely with pgBouncer and connection pools
- Fail-Safe Default: Extension denies access by default on errors
- Audit Logging: All decisions are logged with correlation IDs for debugging
- Instance Caching: Cedarling instance cached across function calls
- Result Caching: Authorization decisions cached with configurable TTL
-
Predicate Pushdown:
cedarling_where()
generates optimized SQL predicates - Batch Evaluation: Processes multiple rows efficiently