YouTrackDB provides predicate-based security — YQL conditions that are evaluated per-record to determine whether an operation is allowed. This enables row-level access control enforced entirely by the database, with no application-side filtering required.
All examples use the public API and run as embedded in-memory databases.
The complete source code is in
SecurityExample.java,
with tests in
ExamplesTest.java.
- Users have credentials and are assigned one or more roles.
- Roles define coarse-grained permissions (admin, writer, reader).
- Security policies are sets of YQL predicates bound to specific operations (READ, CREATE, DELETE, BEFORE UPDATE, AFTER UPDATE, EXECUTE).
- Policies are granted to a role on a resource (e.g., a class) and automatically enforced for every record access.
| Role | Permissions |
|---|---|
admin |
Full access — all operations on all resources |
writer |
Read and modify data, but cannot create or drop classes |
reader |
Read-only access to data |
Create a database with multiple users by passing credential triples (username, password, role):
ytdb.create("my-db", DatabaseType.MEMORY,
"admin", "adminpwd", "admin",
"analyst", "analystpwd", "reader");Users can also be created after database creation with YQL:
CREATE USER analyst IDENTIFIED BY analystpwd ROLE readerFor more, see CREATE USER and DROP USER.
A READ policy controls which records a role can see. The database applies the predicate automatically to every query.
try (var admin = ytdb.openTraversal("sec-read", "admin", "adminpwd")) {
// Schema and data setup.
admin.executeInTx(tx -> {
tx.command("CREATE CLASS Document EXTENDS V");
tx.command("CREATE PROPERTY Document.title STRING");
tx.command("CREATE PROPERTY Document.classification STRING");
});
admin.executeInTx(tx -> {
tx.yql("CREATE VERTEX Document SET title = 'Q1 Report',"
+ " classification = 'public'").iterate();
tx.yql("CREATE VERTEX Document SET title = 'Salary Data',"
+ " classification = 'confidential'").iterate();
tx.yql("CREATE VERTEX Document SET title = 'Press Release',"
+ " classification = 'public'").iterate();
tx.yql("CREATE VERTEX Document SET title = 'Board Minutes',"
+ " classification = 'internal'").iterate();
});
// Create a policy and bind it to the reader role.
admin.executeInTx(tx -> {
tx.command("CREATE SECURITY POLICY readPublicOnly"
+ " SET READ = (classification = 'public')");
tx.command(
"GRANT POLICY readPublicOnly ON database.class.Document TO reader");
});
// Admin sees all 4 documents.
}
// Analyst (reader role) — the policy filters automatically.
try (var reader = ytdb.openTraversal("sec-read", "analyst", "analystpwd")) {
reader.executeInTx(tx -> {
var docs = tx.yql("SELECT title FROM Document ORDER BY title").toList();
// Returns only: Press Release, Q1 Report
// Salary Data and Board Minutes are invisible to this role.
});
}A DELETE policy controls which records a role is allowed to remove. Records that do not match the predicate cannot be deleted.
// Writers can only delete debug-level log entries.
admin.executeInTx(tx -> {
tx.command("CREATE SECURITY POLICY deleteDebugOnly"
+ " SET DELETE = (level = 'debug')");
tx.command(
"GRANT POLICY deleteDebugOnly ON database.class.LogEntry TO writer");
});
// As editor (writer role):
editor.executeInTx(tx -> {
tx.yql("DELETE VERTEX LogEntry WHERE level = 'debug'").iterate(); // succeeds
});
try {
editor.executeInTx(tx -> {
tx.yql("DELETE VERTEX LogEntry WHERE level = 'error'").iterate(); // blocked
});
} catch (Exception e) {
// SecurityException — the policy prevents deletion of error entries
}Different roles can have different policies on the same class. Each user sees data according to their role's policy.
admin.executeInTx(tx -> {
// Interns (reader) can only see engineering projects.
tx.command("CREATE SECURITY POLICY engineeringOnly"
+ " SET READ = (department = 'engineering')");
tx.command(
"GRANT POLICY engineeringOnly ON database.class.Project TO reader");
});
// Intern sees: Alpha, Gamma (engineering only)
// Manager (writer, no READ policy) sees: Alpha, Beta, Gamma (all projects)Policies can be changed at runtime without recreating them. The change takes effect immediately for all subsequent queries.
// Initially: only high-priority memos visible to readers.
admin.executeInTx(tx -> {
tx.command("CREATE SECURITY POLICY memoPolicy"
+ " SET READ = (priority = 'high')");
tx.command("GRANT POLICY memoPolicy ON database.class.Memo TO reader");
});
// Viewer sees: 1 memo (high only)
// Widen the policy to include medium-priority memos.
admin.executeInTx(tx -> {
tx.command("ALTER SECURITY POLICY memoPolicy"
+ " SET READ = (priority IN ['high', 'medium'])");
});
// Viewer sees: 2 memos (high + medium)Revoking a policy restores the default role permissions for that resource.
admin.executeInTx(tx -> {
tx.command("REVOKE POLICY ON database.class.Memo FROM reader");
});
// Viewer now sees all 3 memos — no policy filtering applied.A security policy can include predicates for any combination of these operations:
| Operation | When evaluated |
|---|---|
READ |
On every record returned by a query |
CREATE |
When inserting a new record |
BEFORE UPDATE |
Before a record is modified (checks the old state) |
AFTER UPDATE |
After a record is modified (checks the new state) |
DELETE |
When removing a record |
EXECUTE |
When executing a function |
Multiple operations can be set in a single policy:
CREATE SECURITY POLICY fullControl
SET READ = (classification = 'public'),
CREATE = (status = 'draft'),
DELETE = (status = 'archived')In addition to policies (predicate-based, per-record), YouTrackDB supports coarse-grained permission grants on resources:
-- Grant read access on a specific class
GRANT READ ON database.class.Report TO analyst
-- Grant all permissions on the database
GRANT ALL ON database TO admin
-- Revoke delete permission
REVOKE DELETE ON database.class.AuditLog FROM writer