RLS Persona Tester
Automated row-level security (RLS) testing for Power BI & Fabric semantic models.
Point it at a published model, declare what each role is supposed to see, and it answers — automatically, in CI, before you ship — the question BI teams still answer by hand:
Does every persona see exactly what it should — no more, no less?
An RLS leak means the wrong users see the wrong data: a security, compliance and audit failure waiting to surface in front of an auditor or a customer. Today teams catch it with spare "test accounts" and eyeballing View as role — manual, partial, and easy to skip under deadline. This tool makes it a one-command regression test that blocks the release pipeline when a role leaks.
Install
pip install rls-persona-tester # core engine (stdlib only)
pip install 'rls-persona-tester[rest]' # + live testing against a real model (msal, requests)
Try it in 30 seconds — free, no login
The offline demo runs against a built-in model seeded with two deliberately buggy roles, so you can watch the checks fire:
rls-test -c examples/demo_config.json
EMEA_leaky scope_leak FAIL Sees Region outside allowed scope: ['North', 'South']
EMEA_leaky value_scope FAIL Total Sales=800 but allowed scope should total 300
Empty_role empty_view FAIL Role sees no data on any key measure (over-restrictive / broken RLS)
...
[FAIL] 47 checks — 12 failed, 35 passed # exit 1 → a CI gate would block the deploy
Machine-readable and CI outputs:
rls-test -c examples/demo_config.json -f junit -o results.xml # JUnit → CI gate
rls-test -c examples/demo_config.json -f json # JSON
The offline demo and --preflight diagnostics are free forever.
Test your real model (licensed)
Run the same checks against a published Power BI / Fabric semantic model over the
executeQueries REST API — cross-platform (macOS/Linux/Windows/CI), no Power BI
Desktop, no .NET, no Fabric notebook required:
rls-test --preflight -c your_model.json # connectivity + permission diagnostics (free)
rls-test -c your_model.json --license <KEY> # live RLS test (licensed)
# or set it once: export RLS_TESTER_LICENSE_KEY=...
A valid license unlocks live runs against your own models. Get a license → · $99/year.
What it checks
Five checks, from zero-config to declared-intent:
| Check | Needs | Catches |
|---|---|---|
empty_view |
nothing | broken / over-restrictive RLS (a role sees nothing) |
exceeds_unrestricted |
nothing | hard leak (a role totals more than the whole model) |
scope_leak |
role → allowed members | a role sees dimension members outside its lane |
value_scope |
role → allowed members | a role's measure ≠ the measure over its allowed scope |
reconciliation |
a partition of roles | the roles don't tile the unrestricted total exactly once |
It passes a correctly-restricted role and fails a leaky one — the complete regression signal, with a non-zero exit code so release pipelines stop on a leak.
Config
{
"connector": "rest",
"security_table": "Geography",
"security_column": "Region",
"measures": ["Total Sales", "Order Count"],
"connection": { "dataset_id": "<guid>", "group_id": "<workspace-guid>", "client_id": "<app-guid>" },
"roles": [
{ "name": "North", "as_user": "north-tester@yourco.com", "allowed": ["North"],
"expected": { "measures": { "Total Sales": 250 }, "visible": ["North"] } }
],
"partition_roles": ["North", "South", "EMEA"]
}
allowed— the members this role should see onsecurity_column(its intent).as_user— a UPN that is a member of this role in the model's Security settings (executeQueriesimpersonates a user; it can't activate a role by name — that needs XMLA).partition_roles— roles that together should tile the whole model exactly once.connector: "simulated"runs the free offline demo with no connection block.
See examples/ for ready-to-edit configs and docs/SETUP_FABRIC.md for the one-time
Fabric/Power BI setup (test users, tenant setting, permissions).
Knows the Direct Lake + SSO trap
A Direct Lake model whose source connection uses SSO is incompatible with
executeQueries impersonation — every impersonation fails with PowerBIEntityNotFound,
even impersonating the owner. It's a very common Fabric setup and an opaque failure.
This tool auto-detects that signature and tells you the fix (bind the Direct Lake
source to a dedicated cloud connection with fixed credentials, SSO off) instead of
leaving you guessing.
Requirements (live testing)
- Model on Premium / PPU / Fabric capacity (the Fabric trial covers it).
- Tenant setting "Dataset Execute Queries REST API" = ON.
- Build permission on the model; a test user per role, assigned in Security.
- User auth (MSAL device-code) — service principals aren't allowed on RLS datasets.
Pricing & license
- Free: the offline demo +
--preflightdiagnostics, forever. - $99/year: live RLS testing against your own semantic models, plus updates.
- One license covers a developer's machines and CI. Buy →
A commercial license governs use — see LICENSE.
Support
Questions, a model shape that doesn't fit, or a false positive? Reach us through your Polar customer portal (linked on your receipt). Built by Green Analytics Ltd.
Roadmap
- XMLA/ADOMD connector (activate roles by name; unattended CI at scale)
-
sempy_labsnotebook connector (Direct Lake-native impersonation) - HTML report; GitHub Action / Azure DevOps task wrapper
- Excel "source of truth" reconciliation
Metadata
Release files for rls-persona-tester 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rls_persona_tester-0.1.1.tar.gz | 21.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rls_persona_tester-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 45.3 kB
Release files / rls_persona_tester-0.1.1.tar.gz
| Download URL | rls_persona_tester-0.1.1.tar.gz |
|---|---|
| Size | 21.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ab5e126af92e5000cdf61579fdac897b7661389a38a488b0f8f5cb3aeee8633a
|
|
BLAKE2b-256 checksum How to use checksums |
9d0d30becdfa9be152e34c2ad28cb2b5a4a609886da7e6195a2b28f42b540f69
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.0
|
Release files / rls_persona_tester-0.1.1-py3-none-any.whl
| Download URL | rls_persona_tester-0.1.1-py3-none-any.whl |
|---|---|
| Size | 24.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9504dd5230ac8326258fa7db1661a03fd0ff121d00b53ce06f3ed3228e67fa41
|
|
BLAKE2b-256 checksum How to use checksums |
3a6b30932d42941e8eae55e6079c896c130239f91bc2eecce743d666ba105649
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.0
|