Fine-grained data access control for jOOQ
Protect sensitive data at the row and column level, without changing a line of your application code.
Fine-grained data access control is essential. Most implementations get it wrong.
-
Scattered & Hardcoded
Access checks are buried across your codebase. Every new feature, every refactor is another place to forget a permission check.
-
Blacklist Thinking
Most apps block specific access rather than requiring explicit grants. One forgotten check = one security hole.
-
Invisible to Auditors
Only developers know what access rules exist. Compliance reviews require code archaeology.
How Regelwerk Works
Regelwerk sits between your jOOQ queries and the database. It rewrites SQL at runtime based on declarative rules, so your application code never changes.
SELECT *
FROM customers SELECT name, '***' AS balance, advisor_id, branch
FROM customers
Added by Regelwerk: WHERE advisor_id = 'john.doe'
AND branch = 'zurich'
As a jOOQ ExecuteListener, Regelwerk intercepts every query. Rules add WHERE clauses, mask columns, and validate writes before the query
reaches the database.
Key Capabilities
Row-Level Security
Control exactly which rows each user can read, update, or delete via automatic SQL WHERE clause injection.
Column-Level Masking
Hide or anonymize sensitive columns per user or role with expression-based value replacement.
Full CRUD Protection
INSERT and UPDATE statements are validated within the transaction boundary and rolled back on violation. Users can't escalate privileges by writing rows they shouldn't own.
Context-Aware Rules
Rules adapt based on user identity, role, use-case, and dynamic runtime variables like department or location.
Why Regelwerk
Deny by Default
No rule means no access. Period. Unlike blacklist approaches, you can't accidentally leave a door open. Every data access requires an explicit grant, so a forgotten rule blocks access instead of quietly allowing it.
Zero Code Intrusion
Regelwerk integrates at the jOOQ configuration level. Your business logic has zero dependency on Regelwerk, not even an import statement. You can add, remove, or reconfigure it without touching application code.
Rules, Not Code
Access policies are declarative configuration, not scattered if-statements. Change permissions in hours, not sprints. Security officers, auditors, and data owners can read and verify the rules without opening an IDE.
Battle-Tested
Born from 15+ years of real-world data access control in security-critical domains like e-banking and e-CRM. Now rebuilt as a modern jOOQ extension with support for PostgreSQL, Oracle, MySQL, and more.
Who Benefits
Developers
Stop writing permission checks. Focus on business logic. Regelwerk handles data access control transparently, with no new dependencies in your application code.
Security & Compliance
All access rules are visible in one place, readable without reading code. Every data access is logged with the user and context behind it.
Data Owners
Take back control over who sees your data. Access rules are configuration, not code, so you can change them without waiting for the next release.
Real-World Rule Examples
Regelwerk handles complex, real-world access scenarios with simple, declarative rules.
<rule xsi:type="v3:rule">
<name>Advisor sees own team's customers</name>
<accessTypeRef>read</accessTypeRef>
<roleRef>ADVISOR</roleRef>
<tableRef>PUBLIC.CUSTOMER</tableRef>
<selectionAccessRef>own-team-customers</selectionAccessRef>
</rule>
<selectionAccess xsi:type="v3:selectionAccess">
<name>own-team-customers</name>
<clause>
IN (SELECT customer_id FROM contract
WHERE team_id = ${regelwerkTeam})
</clause>
<selector>${$}.id</selector>
</selectionAccess> <rule xsi:type="v3:rule">
<name>Mask salary for EMPLOYEE role</name>
<accessTypeRef>read</accessTypeRef>
<roleRef>EMPLOYEE</roleRef>
<tableRef>PUBLIC.EMPLOYEE_CONTRACT</tableRef>
<projectionAccessRef>mask-salary</projectionAccessRef>
</rule>
<projectionAccess xsi:type="v3:projectionAccess">
<name>mask-salary</name>
<columnAccess xsi:type="v3:columnAccess">
<columnName>SALARY</columnName>
<readExpression>'***'</readExpression>
</columnAccess>
</projectionAccess> <rule xsi:type="v3:rule">
<name>Claims specialists can create claims</name>
<accessTypeRef>create</accessTypeRef>
<roleRef>CLAIMS_SPECIALIST</roleRef>
<tableRef>PUBLIC.INSURANCE_CLAIM</tableRef>
<selectionAccessRef>all</selectionAccessRef>
</rule>
<!-- No create rule exists for other roles.
Deny by default: without an explicit grant,
any INSERT on INSURANCE_CLAIM is blocked. --> Regelwerk Studio
Build and review access logic without writing code.
Integration in 3 Steps
Set it up once. The execution context resolves the current user on each query, so your queries and business logic stay the same.
- Define your rules in XML or programmatically
- Provide an execution context that reads the current user per request (e.g. from a JWT)
- Attach it to your jOOQ configuration
// 1. Define your rules
RuleProvider rules = new RegelwerkXmlRuleProvider(
() -> getClass().getResourceAsStream("/regelwerk.xml"));
// 2. Resolve the current user per request (e.g. from a JWT claim)
ExecutionContext ctx = new JwtAwareExecutionContext();
// 3. Attach to your jOOQ Configuration once at startup.
DSLContext dsl = DSL.using(connection, SQLDialect.POSTGRES)
.configuration()
.set(RegelwerkListenerFactory.create(rawDsl, rules, ctx))
.dsl();
// From here on, Regelwerk rewrites queries using the request's user and roles
dsl.select().from(CUSTOMERS).fetch(); // ← rules applied transparently Ready to protect your data?
Add Regelwerk to your jOOQ project. Existing queries stay untouched.
<dependency>
<groupId>com.inventage.regelwerk.jooq</groupId>
<artifactId>regelwerk.jooq</artifactId>
</dependency>