You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: drop built-in tenant column, make tenant a custom user attribute
Remove the `tenant` column from `proxy_user` so that tenant is managed
entirely through the ABAC attribute definition system. This eliminates
special-casing for tenant across the proxy, admin API, and UI.
- Add migration 055 to drop the tenant column
- Remove tenant from entity model, CLI args, DTOs, and auth flow
- Update policy hook to resolve tenant from user attributes
- Remove hardcoded tenant dummy from validate_expression
- Update integration tests to create tenant as a custom attribute
- Update admin-ui types, forms, list pages, and test factories
- Update docs and security vectors to reflect tenant as custom attribute
Copy file name to clipboardExpand all lines: CONTRIBUTING.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -126,7 +126,7 @@ The shared pool is safe for all authorized users of a datasource: Pool = "how to
126
126
127
127
`PolicyHook` injects row filters and column transforms at the DataFusion logical plan level via `transform_up`. The filter is applied below the `TableScan` node — it cannot be bypassed by table aliases, CTEs, or subqueries, since DataFusion inlines those into the plan before transformation.
128
128
129
-
Template variable substitution (`{user.tenant}`, etc.) uses parse-then-substitute: the filter expression is parsed into a `DataFusion Expr` tree first, then placeholder identifiers are replaced with typed `Expr::Literal` values. The user's tenant/username never passes through the SQL parser, preventing injection even if the value contains SQL syntax.
129
+
Template variable substitution (`{user.username}`, `{user.id}`, custom attributes like `{user.tenant}`, etc.) uses parse-then-substitute: the filter expression is parsed into a `DataFusion Expr` tree first, then placeholder identifiers are replaced with typed `Expr::Literal` values. The user's values never pass through the SQL parser, preventing injection even if the value contains SQL syntax.
130
130
131
131
### Permissions Model
132
132
@@ -136,7 +136,7 @@ BetweenRows enforces a two-layer access control model:
136
136
137
137
**Data plane** — controlled by two independent mechanisms:
138
138
1.*Connection access* — `data_source_access` entries. A user can connect to a datasource via direct assignment, role membership (including inherited roles), or all-user scope. Being an admin does **not** automatically grant data plane access.
139
-
2.*Query policy* — `PolicyHook` applies row filters, column masks, and column access controls per-query based on assigned policies (direct, role-based, or all-scoped). If the datasource `access_mode` is `"policy_required"`, tables with no matching permit policy return empty results. Policies can reference built-in identity fields (`{user.tenant}`, `{user.username}`, `{user.id}`) and custom user attributes (`{user.KEY}`) for attribute-based access control (ABAC). Optional decision functions (JavaScript/WASM) provide programmable policy gates.
139
+
2.*Query policy* — `PolicyHook` applies row filters, column masks, and column access controls per-query based on assigned policies (direct, role-based, or all-scoped). If the datasource `access_mode` is `"policy_required"`, tables with no matching permit policy return empty results. Policies can reference built-in identity fields (`{user.username}`, `{user.id}`) and custom user attributes (`{user.KEY}`, e.g., `{user.tenant}`) for attribute-based access control (ABAC). Optional decision functions (JavaScript/WASM) provide programmable policy gates.
140
140
141
141
See `docs/permission-system.md` for the full policy system user guide.
142
142
@@ -189,7 +189,7 @@ There is currently no automated performance regression suite. Meaningful regress
189
189
All primary keys are UUIDs. The admin store uses SQLite by default (configurable via `DATABASE_URL`).
190
190
191
191
```
192
-
proxy_user (id UUID, username, password_hash, tenant, is_admin, is_active, …)
192
+
proxy_user (id UUID, username, password_hash, is_admin, is_active, attributes JSON, …)
193
193
data_source (id UUID, name, ds_type, config JSON, secure_config encrypted,
Copy file name to clipboardExpand all lines: README.md
+7-7Lines changed: 7 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,7 +9,7 @@ BetweenRows sits between your users and your data sources, applying security pol
9
9
## Why
10
10
11
11
-**No application changes** — policies are enforced at the proxy layer, not in your app code
12
-
-**Row-level filtering** — automatically filter rows based on user identity (tenant, role, department)
12
+
-**Row-level filtering** — automatically filter rows based on user identity (role, department, tenant)
13
13
-**Column masking** — mask sensitive columns (SSN, email, salary) with expressions, not views
14
14
-**Column & table deny** — hide columns or entire tables from specific users or roles
15
15
-**Full audit trail** — every query is logged with the original SQL, rewritten SQL, and policies applied
@@ -71,9 +71,9 @@ Open **http://localhost:5435** and log in with your admin credentials.
71
71
72
72
1.**Add a data source** — Go to Data Sources → Create. Enter your data source connection details and test the connection.
73
73
2.**Discover the schema** — Click "Discover Catalog" on your new data source. Select which schemas, tables, and columns to expose through the proxy.
74
-
3.**Create a user** — Go to Users → Create. Set a username, password, and tenant.
74
+
3.**Create a user** — Go to Users → Create. Set a usernameand password.
75
75
4.**Grant access** — On the data source page, assign the user (or a role) access to the data source.
76
-
5.**Create a policy** — Go to Policies → Create. For example, a `row_filter` policy with expression `tenant = {user.tenant}` to isolate rows by tenant.
76
+
5.**Create a policy** — Go to Policies → Create. For example, a `row_filter` policy with expression `tenant = {user.tenant}` to isolate rows by tenant. (The `tenant` attribute must be defined as a custom attribute definition first.)
77
77
6.**Assign the policy** — On the data source page, assign the policy to a user, role, or all users.
78
78
7.**Connect through the proxy** — The user can now query through BetweenRows:
79
79
```bash
@@ -127,7 +127,7 @@ Key concepts:
127
127
-**ABAC** — define custom user attributes (e.g., department, region, clearance level) and use them in policy expressions via `{user.<key>}` template variables
128
128
-**Decision functions** — optional JavaScript functions compiled to WASM that gate policy evaluation based on arbitrary logic (time windows, multi-attribute conditions, external state)
129
129
-**Assignment scopes** — policies can be assigned to individual users, roles, or all users on a data source
130
-
-**Template variables** — `{user.tenant}`, `{user.username}`, `{user.id}`, and custom attributes are substituted at query time, making policies dynamic per user
130
+
-**Template variables** — `{user.username}`, `{user.id}` (built-in), and custom attributes like `{user.tenant}`, `{user.region}` are substituted at query time, making policies dynamic per user
131
131
-**Access modes** — data sources can be set to `open` (all tables accessible by default) or `policy_required` (tables are hidden unless a `column_allow` policy grants access)
132
132
-**Deny wins** — deny policies are evaluated before permit policies and cannot be overridden
133
133
-**Visibility follows access** — denied columns and tables are removed from the user's schema at connection time, so they don't appear in client tools like TablePlus or DBeaver
@@ -157,11 +157,11 @@ Create users without the UI — useful for scripting and automation. If you're l
157
157
158
158
```bash
159
159
# Docker
160
-
docker exec -it <container> proxy user create --username alice --password secret --tenant acme
0 commit comments