Remove deprecated Authentication#getLookedUpBy#91126
Conversation
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
|
Pinging @elastic/es-security (Team:Security) |
| 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()) | ||
| ); |
There was a problem hiding this comment.
There are existing bugs in this code block. Some assertions were checking same auth object against itself.
| 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)); |
There was a problem hiding this comment.
This can be simplified to just ensure the authentication object does not change at all.
| 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; | ||
| } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
LGTM. Sanity check on one of the changes (not blocking).
| final Authentication.RealmRef authenticatedBy = authentication.getAuthenticatingSubject().getRealm(); | ||
| if (authentication.isRunAs()) { | ||
| final Authentication.RealmRef lookedUpBy = authentication.getLookedUpBy(); | ||
| final Authentication.RealmRef lookedUpBy = authentication.getEffectiveSubject().getRealm(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Glad I jotted it down! Thanks for following up and figuring out the NPE. Separate PR makes sense 👍
| final Authentication authentication = requestInfo.getAuthentication(); | ||
| if (authentication.getLookedUpBy() == null) { | ||
| assert authentication.isRunAs() : "authentication does not have a run-as"; | ||
| if (authentication.getEffectiveSubject().getRealm() == null) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
| 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()) | ||
| ); |
| } | ||
| final User user = authentication.getEffectiveSubject().getUser(); | ||
| // API key or service account have no named roles | ||
| assertThat(user.roles(), emptyArray()); |
…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)
#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)
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)
#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)
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