-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support SET SESSION AUTHORIZATION and RESET SESSION AUTHORIZATION #16067
Support SET SESSION AUTHORIZATION and RESET SESSION AUTHORIZATION #16067
Conversation
Please look at test failures - seem related |
core/trino-main/src/main/java/io/trino/server/HttpRequestSessionContextFactory.java
Outdated
Show resolved
Hide resolved
@@ -209,22 +218,36 @@ public Identity extractAuthorizedIdentity( | |||
throw badRequest(e.getMessage()); | |||
} | |||
|
|||
Identity identity = buildSessionIdentity(optionalAuthenticatedIdentity, protocolHeaders, headers); | |||
Identity originalIdentity = buildSessionOriginalIdentity(optionalAuthenticatedIdentity, protocolHeaders, headers); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we add tests in TestResourceSecurity and TestUserImpersonationAccessControl to validate the impersonation ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can add unit tests in TestUserImpersonationAccessControl after we have added client side support. And I think the unit test there should be enough to validate the impersonation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The unit tests are added in testing/trino-tests/src/test/java/io/trino/execution/TestSetSessionAuthorization.java
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding to TestResourceSecurity still seems valuable as UI access (e.g. QueryResource
access goes through extractAuthorizedIdentity
without going through the query flow - and would be good to also test it out in this codepath - in case someone sends a X-Trino-Authorization-User
header that way)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. Will take a look and try to add some unit tests for UI access impersonation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a unit test in TestResourceSecurity to verify the code path of UI impersonation.
e15af08
to
91ae181
Compare
5ed9e0f
to
5c77e88
Compare
a9a6af1
to
0f26e2c
Compare
@@ -308,6 +326,22 @@ public Identity toIdentity(Map<String, String> extraCredentials) | |||
.build(); | |||
} | |||
|
|||
public Identity toOriginalIdentity() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In HttpRequestSessionContextFactory
we are also using authenticatedIdentity
. I think the flow is already complex, it would be nice to document them and differences between them somewhere. One thing is to document it https://trino.io/docs/current/develop/client-protocol.html, but I think also it would be nice to document authentication flow. Maybe https://github.com/trinodb/trino/wiki/HTTP-Protocol
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added docs for new client headers.
@@ -115,6 +117,7 @@ public Session( | |||
this.transactionId = requireNonNull(transactionId, "transactionId is null"); | |||
this.clientTransactionSupport = clientTransactionSupport; | |||
this.identity = requireNonNull(identity, "identity is null"); | |||
this.originalIdentity = requireNonNull(originalIdentity, "originalIdentity is null"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean principalIdentity
or authenticationIdentity
. The identity that is used for authentication, while the other one should be called sessionIdentity
or authorizationIdentity
. Am I right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right - but since the "executing" identity is referred to as "identity" in the code currently - we can refer the "authorization" identity as "identity" with this change ... and rename the original (or authentication) identity, wdyt ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some initial comments
public void setSetAuthorizationUser(String authorizationUser) | ||
{ | ||
requireNonNull(authorizationUser, "Authorization user cannot be null or empty"); | ||
this.setAuthorizationUser.set(authorizationUser); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: redundant this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
@@ -825,6 +828,12 @@ public String getSetPath() | |||
return setPath.get(); | |||
} | |||
|
|||
public void setSetAuthorizationUser(String authorizationUser) | |||
{ | |||
requireNonNull(authorizationUser, "Authorization user cannot be null or empty"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: should have isNullOrEmpty check based on the error message
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
core/trino-main/src/main/java/io/trino/execution/SetSessionAuthorizationTask.java
Show resolved
Hide resolved
|
||
String user; | ||
Expression userExpression = statement.getUser(); | ||
if (userExpression instanceof Identifier) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: use Java 17 syntax where you don't need to cast again
e.g.
if (userExpression instanceof Identifier identifier) {
user = identifier.getValue();
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
else { | ||
throw new IllegalArgumentException("Unsupported user expression: " + userExpression.getClass().getName()); | ||
} | ||
requireNonNull(user, "Authorization user cannot be null or empty"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment about nullOrEmpty
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
Description | ||
----------- | ||
|
||
Resets the current user back to the original user. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: we need to add more details in terms of what the "original" user means here (i.e. sessionUser vs principal etc)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added more details.
@@ -419,6 +426,11 @@ private void processResponse(Headers headers, QueryResults results) | |||
this.setAuthorizationUser.set(setAuthorizationUser); | |||
} | |||
|
|||
String resetAuthorizationUserStr = headers.get(TRINO_HEADERS.responseResetAuthorizationUser()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resetAuthorizationUser should be fine - generally we avoid abbreviations in codebase
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
core/trino-parser/src/main/antlr4/io/trino/sql/parser/SqlBase.g4
Outdated
Show resolved
Hide resolved
@@ -209,22 +218,36 @@ public Identity extractAuthorizedIdentity( | |||
throw badRequest(e.getMessage()); | |||
} | |||
|
|||
Identity identity = buildSessionIdentity(optionalAuthenticatedIdentity, protocolHeaders, headers); | |||
Identity originalIdentity = buildSessionOriginalIdentity(optionalAuthenticatedIdentity, protocolHeaders, headers); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding to TestResourceSecurity still seems valuable as UI access (e.g. QueryResource
access goes through extractAuthorizedIdentity
without going through the query flow - and would be good to also test it out in this codepath - in case someone sends a X-Trino-Authorization-User
header that way)
@@ -115,6 +117,7 @@ public Session( | |||
this.transactionId = requireNonNull(transactionId, "transactionId is null"); | |||
this.clientTransactionSupport = clientTransactionSupport; | |||
this.identity = requireNonNull(identity, "identity is null"); | |||
this.originalIdentity = requireNonNull(originalIdentity, "originalIdentity is null"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right - but since the "executing" identity is referred to as "identity" in the code currently - we can refer the "authorization" identity as "identity" with this change ... and rename the original (or authentication) identity, wdyt ?
a56a25e
to
10d6bfc
Compare
Adding some discussion here that we had over slack with @kokosing :
|
testing/trino-tests/src/test/java/io/trino/execution/TestSetSessionAuthorization.java
Show resolved
Hide resolved
10d6bfc
to
fbe6f3c
Compare
@phd3
to get the authorizationUser's groups. I wonder do we need to change |
fbe6f3c
to
9d01526
Compare
9d01526
to
46d49e7
Compare
46d49e7
to
8c8e2b9
Compare
8c8e2b9
to
087e2ec
Compare
00ef8b5
to
c27b7db
Compare
Thank you for your pull request and welcome to our community. We could not parse the GitHub identity of the following contributors: Baohe Zhang.
|
c27b7db
to
eac060d
Compare
Thank you for your pull request and welcome to our community. We could not parse the GitHub identity of the following contributors: Baohe Zhang.
|
eac060d
to
47e8055
Compare
Thank you for your pull request and welcome to our community. We could not parse the GitHub identity of the following contributors: Baohe Zhang.
|
47e8055
to
2a97e95
Compare
e71d0b0
to
0c73513
Compare
d1b4b76
to
9011784
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Minor comments.
client/trino-client/src/main/java/io/trino/client/ClientSession.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/SessionRepresentation.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/server/HttpRequestSessionContextFactory.java
Outdated
Show resolved
Hide resolved
userHeader = detectProtocol(alternateHeaderName, headers.keySet()).requestOriginalUser(); | ||
if (headers.getFirst(userHeader) == null || headers.getFirst(userHeader).isEmpty()) { | ||
userHeader = detectProtocol(alternateHeaderName, headers.keySet()).requestUser(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd extract this out in a method so that the logic of preferring originalUser and fallback on user remains in one place. Also let's add a comment on why we do this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
testing/trino-tests/src/test/java/io/trino/execution/TestSetSessionAuthorization.java
Show resolved
Hide resolved
testing/trino-tests/src/test/java/io/trino/execution/TestSetSessionAuthorization.java
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/server/HttpRequestSessionContextFactory.java
Outdated
Show resolved
Hide resolved
2748bf4
to
ce689dc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. @baohe-zhang Could you please squash commits? Or open access for me to be able to push to your branch, which I'm currently not.
ce689dc
to
4a2a489
Compare
Merged, thanks @baohe-zhang @martint ! |
The new statements should fail when client does not support the new headers. Please update ClientCapabilities. |
@baohe-zhang @phd3 can you please follow-up on this? |
Description
This PR adds support for impersonating as another user by running
SET SESSION AUTHIORIZATION user
SQL command. And it's related to the issue #2512. After this PR gets merged, user u1 can runSET SESSION AUTHORIZATION u2
to impersonate u2. And the user can runRESET SESSION AUTHORIZATION
to switch back to the original user u1. This SQL command can allow a superuser to temporarily become an unprivileged user and later switch back to being a superuser.New request headers:
SET SESSION AUTHORIZATION user
command.New response headers:
The workflow of the set session authorization:
<X-Trino-User: u1>
and<X-Trino-Original-User: u1>
.SET SESSION AUTHORIZATION u2
.<X-Trino-Set-Authorization-User:u2>
in the response header.X-Trino-Set-Authorization-User
exist in the header, thus updating the ClientSession.authorizationUser field to u2.<X-Trino-User: u2>
and<X-Trino-Original-User: u1>
in the request header.Since the Session's identity will be overridden by authorizationIdentity in step 5, we would need a new session field originalIdentity to preserve the information of the original user. This originalIdentity will be used for the impersonation checks during query session creation and It will also be used as the source of audit information.
Additional context and related issues
workflow:
trino-python-client support is added in trinodb/trino-python-client#349
Release notes
( ) This is not user-visible or docs only and no release notes are required.
() Release notes are required, please propose a release note for me.
(x) Release notes are required, with the following suggested text: