Overview
For cloud DataLens on Yandex Cloud (datalens.ru). RLSv2 (the rls2 field) stores subjects by
id, whereas the legacy RLS (the rls text config) let you use names. This skill
resolves names → ids via the yc CLI (Cloud Organization Manager) and can emit a complete
rls2 config.
Users → cloud subject id (subject_claims.sub); groups → org group id.
subject_name is display-only; subject_id is what enforcement uses. Unresolved subjects
in convert output become notfound with a !FAILED_ name prefix. In resolve output,
resolved subject objects stay under resolved and unresolved input names are listed separately
under unresolved.
⚠️ Access required. To resolve Yandex Cloud subjects you must hold at least the
organization-manager.viewerrole on the target organization. Without it, theyc organization-manager user list/group listcalls fail with aPermissionDeniederror and nothing resolves. If you hit that, ask an organization admin to grant youorganization-manager.viewer(or a higher Organization Manager role) on that org — do not work around it.
When to use
- Filling
rls2for a dataset and you only have logins / emails / group names. - Migrating an existing dataset from the legacy
rlstext config torls2.
Two modes
| Mode | Input | Output |
|---|---|---|
resolve |
subject names (logins, emails, group names) | rls2 subject objects + unresolved list |
convert |
the legacy rls config — JSON {field_guid: "<text>"} (one or many fields), or a single field's raw RLS text |
the full exact rls2 config {field_guid: [rules]} |
Subject input format
| Subject | Input form |
|---|---|
| user | login or email as-is |
| group | @group:<group name> |
| service account | @sa:<service-account id> |
| all rows | * |
| source-level user id | userid |
Normalize explicit group names and service-account IDs before invoking the tool. If the user says
"group Analysts", pass @group:Analysts. If the user supplies service-account ID aje123, pass
@sa:aje123; do not look it up as a user or ask for the same ID again.
How users are matched. The tool lists Cloud Organization members and matches your input,
case-insensitively, against each member's preferred_username (login) and email; the id it
returns is subject_claims.sub — exactly what the backend stores. A full email is the most
reliable key. A bare login (ivan) is treated as ivan@yandex.ru (a legacy compatibility
mapping), so it resolves only for yandex.ru accounts — in a federated organization pass the
full email or the name comes back unresolved. A name that matches more than one subject is left
unresolved rather than guessed. Large orgs are handled: the tool raises yc's default 1000-row
--limit so members past the first page are not silently dropped.
Convert input — the legacy rls config
convert takes the dataset's legacy rls field: a JSON object mapping each field guid to
that field's RLS text — the text you edit in a field's Row-level security settings. It
accepts the whole config (every field) or a single field; {"<one_guid>": "<text>"} is
equally valid, and the output is keyed by the same guids.
Each field's text uses the legacy grammar (one rule per line):
'Moscow': alice@example.com, @group:Analysts
'Tver': alice@example.com
*: bob@example.com
userid: userid
'value': subjects— one quoted value per line; those subjects see rows where the field equalsvalue(double''inside to escape a literal quote).*: subjects— those subjects see all rows.userid: userid— filter rows by the field equalling the viewer's own id.
When the user pastes just one field's text (the common case — copied from the field's RLS
settings), convert it directly: pass the raw text (stdin or --input) and convert emits the
rules under a <field_guid> placeholder to substitute later. The guid is optional — it never
affects the resolved subjects, only the output key — so if you do have it, wrap the text yourself
as {"<field_guid>": "<text>"} to key the output correctly.
Prerequisites
- Python 3.9+ (standard library only — no
pip install). - The
ycCLI installed and authenticated (IAM-token session — OAuth Yandex ID is deprecated for YC). Auth is handled below, by you. - At least the
organization-manager.viewerrole on the target organization (see the ⚠️ warning above) — required to list users/groups.
Authenticate yc — YOU run these checks; never expose the token
Check the session with the bundled wrapper — it runs the token call for you with output discarded and a timeout, and prints only a status word (never the token):
python3 scripts/rls_tool.py auth-check # -> authenticated | not_authenticated | timeout | not_installed
authenticated→ continue.not_authenticated/not_installed→ re-authenticate or install (below).timeout→ the check is blocked — often an interactive hardware-key touch, or a sandbox withholding network. Re-authenticate interactively withyc init(below), where you can respond to the prompt.
Prefer the wrapper so a live token is never exposed. Backstop — never run these yourself:
yc iam create-token without redirecting output, or yc config list / yc config get token —
they print the OAuth token / SA key into the transcript. Never print, echo, log, or store any
token or key. (yc config profile list, which shows only profile names, is fine.)
Re-authenticate — drive it, don't hand the user a to-do list
If step 2 failed, re-auth yourself. Do not use a plain OAuth token: OAuth Yandex ID
tokens are not supported for IAM exchange (error OAuth token … is not supported for IAM token exchange). Use one of these instead:
- Federated SSO (default).
yc init --federation-id <FEDERATION_ID>authenticates via the org federation in the browser — not subject to the OAuth deprecation. If you don't already know the federation id, ask the user for it (YC console → Organization → Federations, or their cloud admin). Runyc init --federation-id <FEDERATION_ID>in an interactive terminal. If the current agent cannot complete the browser interaction, ask the user to run that one command locally, then continue automatically. - Service-account key. If the user has an SA key file:
yc config set service-account-key <path>(a path, not a secret).
Then re-check with python3 scripts/rls_tool.py auth-check and proceed. Do not stop and dump
a list of manual steps — drive the flow, and only hand off the single interactive browser step.
Organization id — YOU fetch it
Run yc organization-manager organization list --format json.
- Exactly one org → use its
id. - Several → ask the user which one.
Run the resolver — YOU run it, then present the result
Do not tell the user to "then run the resolver". You invoke the tool via Bash yourself and show them the output.
# resolve names
python3 scripts/rls_tool.py resolve --org-id <ORG_ID> alice@example.com "@group:Analysts" bob
# convert the legacy rls -> full rls2 (rls JSON via --input FILE or stdin)
python3 scripts/rls_tool.py convert --org-id <ORG_ID> --input old_rls.json
# or one field's raw RLS text straight from stdin (guid optional)
printf "%s\n" "'Moscow': alice@example.com" "*: bob@example.com" | python3 scripts/rls_tool.py convert --org-id <ORG_ID>
Names may be comma / newline / semicolon separated (not space — cloud group names can
contain spaces). Prefix groups with @group: and service accounts with @sa:. If the tool
prints an auth error (the yc session lapsed mid-run),
re-authenticate as above and retry — automatically, without asking the user to intervene.
Output shape
convert prints exactly what the backend stores:
{
"<field_guid>": [
{"subject": {"subject_id": "aje1234...", "subject_name": "alice@example.com", "subject_type": "user"},
"allowed_value": "Philadelphia", "pattern_type": "value"}
]
}
Subject id details and the yc calls are in references/id-formats.md.
Common mistakes
- Leaking the token. Never run
yc config list/yc config get tokenor an unredirectedyc iam create-token. Check auth only viapython3 scripts/rls_tool.py auth-check. - Handing work to the user. Drive
yc initand run the resolver yourself; don't tell the user to do it. - Sending both
rlsandrls2. The backend rejects a dataset with both set — send onlyrls2. - Cross-installation ids. Yandex Cloud ids (cloud subject id / org group id) are not valid on other DataLens installations.
- Unresolved subjects. In
convert, they are emitted asnotfound(never dropped) and summarized; fix the name and re-run rather than shipping a!FAILED_entry. Inresolve, they are listed underunresolved.
Verify the transform
python3 tests/test_rls_tool.py runs offline logic tests (parser, normalization,
resolution assembly) whose expected rls2 values mirror the backend.