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
I had searched in the issues and found no similar issues.
Description
The problem: a multi-tenant middle tier cannot make Doris enforce the end user's entitlements, nor attribute the query to that person.
A common deployment shape is a service that queries Doris on behalf of many people — an AI/BI gateway, an MCP server, an embedded-analytics backend. Today it has two options, both bad:
Option
What Doris sees
Consequence
One shared service account
svc_x for every query
fe.audit.log and ROW POLICY … TO <user> cannot tell people apart; the service must re-implement access control on top of Doris
One connection per end user with that user's own credentials
the person
Doris sees the person's full role union — including grants the service must never exercise (write privileges, other tenants' tables). The middle tier ends up more privileged than the tenant it serves, and it must hold every user's password
What the middle tier actually needs is what MySQL's PROXY privilege and Oracle's ALTER USER … GRANT CONNECT THROUGH provide: authenticate as the service, then run this session as a named end user, restricted to an explicit subset of that user's roles. Doris has no equivalent — RBAC activates all of a user's roles on every connection, and there is no way to narrow a session (#49915 asks for session attribution in the audit log; this is the identity half of the same need).
Proposed feature
-- session-scoped; no metadata change; only the authenticated session is affected
SU 'alice'@'%' WITH ROLES ('tenant_42', 'tenant_42_scoped') [WORKLOAD GROUP 'wg_tenant_42'];
Semantics:
Authorization to switch is a grantable privilege using MySQL's own name and syntax: GRANT PROXY_PRIV ON 'alice'@'%' TO 'svc_x'@'%' (MySQL: GRANT PROXY ON 'alice'@'%' TO 'svc_x'@'%'), with ''@'' as the wildcard exactly as MySQL defines it, and ADMIN_PRIV implying it. Without it the statement is denied. Doris's privilege vocabulary already mirrors MySQL (SELECT_PRIV, GRANT_PRIV, USAGE_PRIV), so PROXY_PRIV is the natural name; SHOW GRANTS lists it like any other privilege. The one deliberate difference from MySQL: in MySQL a proxy user acquires the proxied user's full privileges, whereas here the WITH ROLES list is mandatory — proxying always narrows (point 2). We think that is the safer default for a middle tier, but are open to allowing a no-list form that behaves exactly like MySQL if maintainers prefer parity.
The role list REPLACES the target's role union for this session — never widens it. Every requested role must be granted to the target user (the "ceiling"); a role the target does not hold is refused. The target's default/personal grants are not active under the switch unless named.
current_user() returns the target, so ROW POLICY … TO <user> / TO ROLE …, column masking and any current_user()-keyed predicate evaluate as the person; fe.audit.log records the person as User with the switcher kept alongside (a new audit column or the existing Client/comment field) so the trail shows both "who ran it" and "through what".
Session-only and one-shot: a second SU in a switched session is refused; resetConnection() / COM_CHANGE_USER revert to the authenticated identity (never to the target's full roles); nothing is persisted.
Dormant roles (the piece that makes the service strictly less privileged than the person): a role property such as 'default_active' = 'false' marks a role as inert in ordinary sessions — SHOW GRANTS lists it, but its privileges only apply when explicitly activated through SU … WITH ROLES. Tenant-scoped roles are created dormant, so a person logging in directly with their own account does not get the gateway's tenant view, and the gateway cannot get anything the person was not granted.
A builtin session_is_narrowed() (BOOLEAN, FE-constant-folded like current_user()) lets policies and diagnostics distinguish a switched session.
Implementation sketch (one choke point): Auth.getRolesByUserWithLdap returns the session override when one is set on the ConnectContext, and filters dormant roles when none is. Everything downstream (privilege checks, row policies, SHOW GRANTS for the session) follows from that, with no changes to the privilege tables. current_user() already reads the context's identity.
We have this running as a fork patch: SU … WITH ROLES and session_is_narrowed() as described; the grantable privilege is currently a config allowlist and dormant roles a config regex, to keep the patch small. The upstream-native shape above (PROXY_PRIV + the default_active role property) is what we would contribute, and we are happy to adjust naming and syntax to whatever the maintainers prefer — the ask here is agreement on the model before we open the PR.
Use case
An MCP/AI gateway serves ~N tenants. Each tenant is a Doris role (tenant_<id>) granting SELECT on exactly that tenant's tables plus a RESTRICTIVE row policy keyed on current_user() through a membership table. The gateway authenticates once as svc_gateway, then per user session runs SU '<person>' WITH ROLES ('tenant_<id>', 'tenant_<id>_scoped') WORKLOAD GROUP 'wg_tenant_<id>'. Result:
Doris — not the gateway — enforces the table allowlist, the row policy and the resource lane, because the session is the person with exactly those roles.
The audit log names the person; the gateway holds only its own credential.
The same person connecting directly (BI tool, mysql client) does not see the tenant view, because the tenant roles are dormant — the gateway's grants are not a back door for the person's own account.
Without this, the gateway must either run everything as one account (no per-person audit, app-level authz) or hold every person's password and accept that the session carries their full role union.
MySQL PROXY privilege (GRANT PROXY ON 'alice'@'%' TO 'svc'@'%', https://dev.mysql.com/doc/refman/8.4/en/proxy-users.html) and Oracle proxy authentication (ALTER USER alice GRANT CONNECT THROUGH svc WITH ROLE tenant_42) are the precedents for the shape proposed here — note MySQL's SET ROLE (activate a subset of your own roles) is deliberately not the model: the requirement is a service acting as another user with a bounded role set, which is proxy authentication, not role toggling.
Search before asking
Description
The problem: a multi-tenant middle tier cannot make Doris enforce the end user's entitlements, nor attribute the query to that person.
A common deployment shape is a service that queries Doris on behalf of many people — an AI/BI gateway, an MCP server, an embedded-analytics backend. Today it has two options, both bad:
svc_xfor every queryfe.audit.logandROW POLICY … TO <user>cannot tell people apart; the service must re-implement access control on top of DorisWhat the middle tier actually needs is what MySQL's
PROXYprivilege and Oracle'sALTER USER … GRANT CONNECT THROUGHprovide: authenticate as the service, then run this session as a named end user, restricted to an explicit subset of that user's roles. Doris has no equivalent — RBAC activates all of a user's roles on every connection, and there is no way to narrow a session (#49915 asks for session attribution in the audit log; this is the identity half of the same need).Proposed feature
Semantics:
GRANT PROXY_PRIV ON 'alice'@'%' TO 'svc_x'@'%'(MySQL:GRANT PROXY ON 'alice'@'%' TO 'svc_x'@'%'), with''@''as the wildcard exactly as MySQL defines it, andADMIN_PRIVimplying it. Without it the statement is denied. Doris's privilege vocabulary already mirrors MySQL (SELECT_PRIV,GRANT_PRIV,USAGE_PRIV), soPROXY_PRIVis the natural name;SHOW GRANTSlists it like any other privilege. The one deliberate difference from MySQL: in MySQL a proxy user acquires the proxied user's full privileges, whereas here theWITH ROLESlist is mandatory — proxying always narrows (point 2). We think that is the safer default for a middle tier, but are open to allowing a no-list form that behaves exactly like MySQL if maintainers prefer parity.current_user()returns the target, soROW POLICY … TO <user>/TO ROLE …, column masking and anycurrent_user()-keyed predicate evaluate as the person;fe.audit.logrecords the person asUserwith the switcher kept alongside (a new audit column or the existingClient/comment field) so the trail shows both "who ran it" and "through what".SUin a switched session is refused;resetConnection()/COM_CHANGE_USERrevert to the authenticated identity (never to the target's full roles); nothing is persisted.'default_active' = 'false'marks a role as inert in ordinary sessions —SHOW GRANTSlists it, but its privileges only apply when explicitly activated throughSU … WITH ROLES. Tenant-scoped roles are created dormant, so a person logging in directly with their own account does not get the gateway's tenant view, and the gateway cannot get anything the person was not granted.session_is_narrowed()(BOOLEAN, FE-constant-folded likecurrent_user()) lets policies and diagnostics distinguish a switched session.Implementation sketch (one choke point):
Auth.getRolesByUserWithLdapreturns the session override when one is set on theConnectContext, and filters dormant roles when none is. Everything downstream (privilege checks, row policies,SHOW GRANTSfor the session) follows from that, with no changes to the privilege tables.current_user()already reads the context's identity.We have this running as a fork patch:
SU … WITH ROLESandsession_is_narrowed()as described; the grantable privilege is currently a config allowlist and dormant roles a config regex, to keep the patch small. The upstream-native shape above (PROXY_PRIV+ thedefault_activerole property) is what we would contribute, and we are happy to adjust naming and syntax to whatever the maintainers prefer — the ask here is agreement on the model before we open the PR.Use case
An MCP/AI gateway serves ~N tenants. Each tenant is a Doris role (
tenant_<id>) granting SELECT on exactly that tenant's tables plus aRESTRICTIVErow policy keyed oncurrent_user()through a membership table. The gateway authenticates once assvc_gateway, then per user session runsSU '<person>' WITH ROLES ('tenant_<id>', 'tenant_<id>_scoped') WORKLOAD GROUP 'wg_tenant_<id>'. Result:mysqlclient) does not see the tenant view, because the tenant roles are dormant — the gateway's grants are not a back door for the person's own account.Without this, the gateway must either run everything as one account (no per-person audit, app-level authz) or hold every person's password and accept that the session carries their full role union.
Related issues
PROXYprivilege (GRANT PROXY ON 'alice'@'%' TO 'svc'@'%', https://dev.mysql.com/doc/refman/8.4/en/proxy-users.html) and Oracle proxy authentication (ALTER USER alice GRANT CONNECT THROUGH svc WITH ROLE tenant_42) are the precedents for the shape proposed here — note MySQL'sSET ROLE(activate a subset of your own roles) is deliberately not the model: the requirement is a service acting as another user with a bounded role set, which is proxy authentication, not role toggling.Are you willing to submit PR?
Code of Conduct