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

Remove deprecated Authentication#getLookedUpBy #91126

Merged
merged 2 commits into from
Oct 26, 2022

Conversation

ywangd
Copy link
Member

@ywangd ywangd commented Oct 26, 2022

This PR removes the deprecated Authentication#getLookedUpBy method. Its usages are replaced by either isRunAs or
getEffectiveSubject#getRealm or their combinations depending on the actual context.

Relates: #88494

This PR removes the deprecated Authentication#getLookedUpBy method.
Its usages are replaced by either isRunAs or
getEffectiveSubject#getRealm or their combinations depending on the
actual context.

Relates: elastic#88494
@ywangd ywangd added >refactoring :Security/Security Security issues without another label v8.6.0 labels Oct 26, 2022
@ywangd ywangd requested a review from n1v0lg October 26, 2022 03:19
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-security (Team:Security)

@elasticsearchmachine elasticsearchmachine added the Team:Security Meta label for security team label Oct 26, 2022
Comment on lines -155 to +163
assertThat(auth.getAuthenticatingSubject().getRealm(), sameInstance(auth.getAuthenticatingSubject().getRealm()));
assertThat(auth.getLookedUpBy(), sameInstance(auth.getLookedUpBy()));
assertThat(auth.getEffectiveSubject().getVersion(), sameInstance(auth.getEffectiveSubject().getVersion()));
assertThat(auth.getAuthenticationType(), sameInstance(auth.getAuthenticationType()));
assertThat(auth.getAuthenticatingSubject().getMetadata(), sameInstance(auth.getAuthenticatingSubject().getMetadata()));
assertThat(auth.getAuthenticatingSubject().getRealm(), sameInstance(authentication.getAuthenticatingSubject().getRealm()));
assertThat(auth.getEffectiveSubject().getRealm(), sameInstance(authentication.getEffectiveSubject().getRealm()));
assertThat(auth.getEffectiveSubject().getVersion(), sameInstance(authentication.getEffectiveSubject().getVersion()));
assertThat(auth.getAuthenticationType(), sameInstance(authentication.getAuthenticationType()));
assertThat(
auth.getAuthenticatingSubject().getMetadata(),
sameInstance(authentication.getAuthenticatingSubject().getMetadata())
);
Copy link
Member Author

Choose a reason for hiding this comment

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

There are existing bugs in this code block. Some assertions were checking same auth object against itself.

Copy link
Contributor

Choose a reason for hiding this comment

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

Very strong assertions 😅

Comment on lines -195 to +202
if (anonymousUser.enabled()) {
final Authentication auth = responseRef.get().authentication();
final User authUser = auth.getEffectiveSubject().getUser();
assertThat(authUser.roles(), emptyArray());
assertThat(auth.getAuthenticatingSubject().getRealm(), sameInstance(auth.getAuthenticatingSubject().getRealm()));
assertThat(auth.getLookedUpBy(), sameInstance(auth.getLookedUpBy()));
assertThat(auth.getEffectiveSubject().getVersion(), sameInstance(auth.getEffectiveSubject().getVersion()));
assertThat(auth.getAuthenticationType(), sameInstance(auth.getAuthenticationType()));
assertThat(auth.getAuthenticatingSubject().getMetadata(), sameInstance(auth.getAuthenticatingSubject().getMetadata()));
} else {
assertThat(responseRef.get().authentication(), sameInstance(authentication));
}
// Anonymous roles should not be added which means there is no change to the authentication at all
assertThat(responseRef.get().authentication(), sameInstance(authentication));
Copy link
Member Author

Choose a reason for hiding this comment

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

This can be simplified to just ensure the authentication object does not change at all.

Comment on lines -175 to -181
public RealmRef getLookedUpBy() {
if (isRunAs()) {
return effectiveSubject.getRealm();
} else {
// retain the behaviour of returning null for lookup realm for if the authentication is not run-as
return null;
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Because we use null to indicate lookup failure, it means the method can return null even when isRunAs is true. I think this behaviour is itself a tech debt. But I am leaving it for another day.

Copy link
Contributor

@n1v0lg n1v0lg left a comment

Choose a reason for hiding this comment

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

LGTM. Sanity check on one of the changes (not blocking).

@@ -1627,7 +1628,7 @@ LogEntryBuilder withAuthentication(Authentication authentication) {
} else {
final Authentication.RealmRef authenticatedBy = authentication.getAuthenticatingSubject().getRealm();
if (authentication.isRunAs()) {
final Authentication.RealmRef lookedUpBy = authentication.getLookedUpBy();
final Authentication.RealmRef lookedUpBy = authentication.getEffectiveSubject().getRealm();
Copy link
Contributor

Choose a reason for hiding this comment

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

I know we're not introducing this behavior in this PR, but it's a little suspect that we handle nullability of "lookedUpBy" in some places but not in others. Just jotting this down as an observation, I don't think we should change existing behavior in this refactor.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is a great observation! This turned out to be a bug! When run-as fails (because user does not exist) and authentication_success event is enabled for audit logging, the server throws a NPE

Cannot invoke "org.elasticsearch.xpack.core.security.authc.Authentication$RealmRef.getName()" because "lookedUpBy" is null

Since this is an existing bug, I'll have a separate PR to address this. Thanks for raising it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Glad I jotted it down! Thanks for following up and figuring out the NPE. Separate PR makes sense 👍

@@ -664,7 +664,8 @@ private void authorizeRunAs(
final ActionListener<AuthorizationResult> listener
) {
final Authentication authentication = requestInfo.getAuthentication();
if (authentication.getLookedUpBy() == null) {
assert authentication.isRunAs() : "authentication does not have a run-as";
if (authentication.getEffectiveSubject().getRealm() == null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a slight change in business logic: if runAs is false, the condition here would be true regardless of the value of authentication.getEffectiveSubject().getRealm() before the change, whereas now this is not the case anymore (in production, where assertions are disabled). Just double-checking that this is intentional.

Copy link
Member Author

Choose a reason for hiding this comment

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

The enclosing method is authorizeRunAs which is called inside maybeAuthorizeRunAs when the authentication is indeed a run-as. So the new assertion is basically making the existing invariant explicit and does not change existing logic.

That said, I will change the assertion message to something like

authentication must have run-as for run-as to be authorized

Hope it makes the intention clearer.

Alternatively, we can inline this method. But since it is private. I don't feel strong for it. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

I missed the fact that isRunAs is necessarily true here due to maybeAuthorizeRunAs. I had only looked at it in isolation. I'm good to leave as is -- it's a private method and to anyone looking at the code as a whole (as opposed to just the diff in the PR), things will make sense. I don't think we should inline.

Comment on lines -155 to +163
assertThat(auth.getAuthenticatingSubject().getRealm(), sameInstance(auth.getAuthenticatingSubject().getRealm()));
assertThat(auth.getLookedUpBy(), sameInstance(auth.getLookedUpBy()));
assertThat(auth.getEffectiveSubject().getVersion(), sameInstance(auth.getEffectiveSubject().getVersion()));
assertThat(auth.getAuthenticationType(), sameInstance(auth.getAuthenticationType()));
assertThat(auth.getAuthenticatingSubject().getMetadata(), sameInstance(auth.getAuthenticatingSubject().getMetadata()));
assertThat(auth.getAuthenticatingSubject().getRealm(), sameInstance(authentication.getAuthenticatingSubject().getRealm()));
assertThat(auth.getEffectiveSubject().getRealm(), sameInstance(authentication.getEffectiveSubject().getRealm()));
assertThat(auth.getEffectiveSubject().getVersion(), sameInstance(authentication.getEffectiveSubject().getVersion()));
assertThat(auth.getAuthenticationType(), sameInstance(authentication.getAuthenticationType()));
assertThat(
auth.getAuthenticatingSubject().getMetadata(),
sameInstance(authentication.getAuthenticatingSubject().getMetadata())
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Very strong assertions 😅

@@ -174,6 +178,8 @@ public void testShouldNotAddAnonymousRolesForApiKeyOrServiceAccount() {
authentication = AuthenticationTestHelper.builder().serviceAccount().build();
}
final User user = authentication.getEffectiveSubject().getUser();
// API key or service account have no named roles
assertThat(user.roles(), emptyArray());
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@ywangd ywangd merged commit bb44f7f into elastic:main Oct 26, 2022
ywangd added a commit to ywangd/elasticsearch that referenced this pull request Oct 28, 2022
…xist

When run-as fails because the target user does not exist, the
authentication is created with a null lookup realm. It is then rejected
at authorization time. But for authentication, it is treated as success.
This can lead to NPE when auditing the authenticationSuccess event.

This PR fixes the NPE by checking whether lookup realm is null before
using it.

Relates: elastic#91126 (comment)
ywangd added a commit that referenced this pull request Nov 1, 2022
#91171)

When run-as fails because the target user does not exist, the
authentication is created with a null lookup realm. It is then rejected
at authorization time. But for authentication, it is treated as success.
This can lead to NPE when auditing the authenticationSuccess event.

This PR fixes the NPE by checking whether lookup realm is null before
using it.

Relates: #91126 (comment)
ywangd added a commit to ywangd/elasticsearch that referenced this pull request Nov 2, 2022
elastic#91171)

When run-as fails because the target user does not exist, the
authentication is created with a null lookup realm. It is then rejected
at authorization time. But for authentication, it is treated as success.
This can lead to NPE when auditing the authenticationSuccess event.

This PR fixes the NPE by checking whether lookup realm is null before
using it.

Relates: elastic#91126 (comment)
elasticsearchmachine pushed a commit that referenced this pull request Nov 2, 2022
#91171) (#91240)

When run-as fails because the target user does not exist, the
authentication is created with a null lookup realm. It is then rejected
at authorization time. But for authentication, it is treated as success.
This can lead to NPE when auditing the authenticationSuccess event.

This PR fixes the NPE by checking whether lookup realm is null before
using it.

Relates: #91126 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
>refactoring :Security/Security Security issues without another label Team:Security Meta label for security team v8.6.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants