Skip to content
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

[ISSUE-1206] improvement: Use the real user for audit information #1258

Merged
merged 20 commits into from
Dec 28, 2023

Conversation

qqqttt123
Copy link
Contributor

@qqqttt123 qqqttt123 commented Dec 26, 2023

What changes were proposed in this pull request?

Use the real user for audit information.
Use a PrincipalContext to pass the value of user without interface modification referring to the design of Spark's TaskContext

Why are the changes needed?

Fix: #1206

Does this PR introduce any user-facing change?

No.

How was this patch tested?

Add UT.

@qqqttt123 qqqttt123 marked this pull request as draft December 26, 2023 08:40
@qqqttt123
Copy link
Contributor Author

Blocked by #1257

@qqqttt123 qqqttt123 marked this pull request as ready for review December 26, 2023 12:30
@qqqttt123 qqqttt123 requested review from yuqi1129, jerryshao, xunliu, FANNG1, diqiu50, mchades and Clearvive and removed request for yuqi1129 December 26, 2023 12:30
@qqqttt123
Copy link
Contributor Author

Blocked by #1257

#1257 merged.

@qqqttt123 qqqttt123 self-assigned this Dec 26, 2023
@@ -13,4 +13,6 @@ public interface AuthConstants {
String AUTHORIZATION_BASIC_HEADER = "Basic ";

String ANONYMOUS_USER = "anonymous";

String AuthenticatedPrincipalAttributeName = AuthConstants.class.getName() + "-principal";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you capitalize the first letter, also why don't you use the upper-case for this static variable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.


private String currentUser;

private static final ThreadLocal<PrincipalContext> context = new ThreadLocal<>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it safe to use thread local variable? I'm afraid if we change some code, we will not get the correct variable?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also if we don't remove the thread local variable in time, we will get the wrong user if this thread is reused.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the meantime, is it better to use "InheritableThreadLocal"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use a thread pool, InheritableThreadLocal can't also guarantee that we get the correct user.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also if we don't remove the thread local variable in time, we will get the wrong user if this thread is reused.

Now, we use try finally to guarantee the correctness. It's a regular way to solve the similar case.

@@ -52,7 +53,8 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
authData = headerData.nextElement().getBytes(StandardCharsets.UTF_8);
}
if (authenticator.isDataFromToken()) {
authenticator.authenticateToken(authData);
Principal principal = authenticator.authenticateToken(authData);
request.setAttribute(AuthConstants.AuthenticatedPrincipalAttributeName, principal);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a common way to get user name from attribute?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulsar uses this way. I don't find other implements. Some systems will put the user formation into the session. But we don't have session manager in our Jetty server. It may bring some cost to add a session manager in our Jetty server. So I choose to use to put the user to the attributes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My original purpose is to use this:

image

If you use another way, maybe you can remove that variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My original purpose is to use this:

image If you use another way, maybe you can remove that variable.

I have used this variable. I need to set the attribute and get the attribute with this variable.

@@ -80,7 +81,7 @@ public Response createTable(
@PathParam("catalog") String catalog,
@PathParam("schema") String schema,
TableCreateRequest request) {
try {
try (PrincipalContext context = Utils.createUserPrincipalContext(httpRequest)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you would wrap this for all the operations later on, not just create and alter operations. For example, if you want to support proxy user for hive catalog operations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I will do.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

import javax.security.auth.Subject;

@SuppressWarnings("removal")
public class PrincipalUtils {
Copy link
Contributor Author

@qqqttt123 qqqttt123 Dec 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refer to the doAs and getCurrentUser from Hadoop UserInformation.

Copy link
Contributor Author

@qqqttt123 qqqttt123 Dec 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This api will be removed in the future. But JDK doesn't provide alternative solution. Hadoop will encounter the similar situation. I add the anotation to avoid the failure of compile.

Heng Qin added 2 commits December 28, 2023 14:49
@jerryshao
Copy link
Contributor

Please let me know when it is ready to review again.

@qqqttt123
Copy link
Contributor Author

qqqttt123 commented Dec 28, 2023

Please let me know when it is ready to review again.

I have addressed all the comments. It's ready to review again.

@@ -13,4 +13,6 @@ public interface AuthConstants {
String AUTHORIZATION_BASIC_HEADER = "Basic ";

String ANONYMOUS_USER = "anonymous";

String AUTHENTICATED_PRINCIPAL_ATTRIBUTE_NAME = AuthConstants.class.getName() + "-principal";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of using AuthConstants.class.getName() as an attribute name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refer to the style of Pulsar. I have added the comment.

Throwable cause = pae.getCause();
if (!(cause instanceof Exception)) {
throw new RuntimeException(
"PrivilegedActionException with no "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which exception will be thrown here, the exception message is not easy to understand.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can use Guava's Throwables to simplify the code here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

executorService.submit(
() ->
Assertions.assertEquals(
"testThreadPool", PrincipalUtils.getCurrentPrincipal().getName()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this may not be correct if the thread is cached and reused? You can only make sure to get correct principal when you doAs in the lambda function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doAs doesn't use InherittedThreadLocal. The implement is referring to the UserGroupInformation. It get the principal from stack.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My fault. Removed.

Heng Qin added 2 commits December 28, 2023 19:21
@qqqttt123
Copy link
Contributor Author

qqqttt123 commented Dec 28, 2023

All comments are addressed.

  1. Removed the bad case
  2. Use Throwables to simplify the code
  3. Add the comment.

@jerryshao jerryshao merged commit 1315755 into apache:main Dec 28, 2023
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Subtask] Use real user for audit information
2 participants