Explorar el Código

Merge AuthenticationContex into Authentication (#85255)

This PR removes the AuthenticationContext class introduced in #80926 and
merges its functions into Authentication.

It becomes more apparent that the most useful refactoring in #80926 is
the new Subject class, which is also what AuthenticationContext provides
most of its value. The AuthenticationContext is essentially just a thin
wrapper of two subjects which represents the existing Authentication
object in a more structured format. The original plan was to replace
Authentication with AuthenticationContext. However, it has practical
challenges that the usage of Authentication is too wide spread. It's
hard to have a series of scoped changes to replace it. Therefore the new
plan is to stick with Authentication, agumenting it with subjects
similar to what AuthenticationContext has and remove
AuthenticationContext. This PR also deprecates methods that should be
replaced by methods of Subjects. In future, the plan is to remove the
deprecated methods, also rework the User class so it does not need
nest another User to represent run-as (which is another main reason for
the original refactor #80926). Overall, the new plan makes it easier to
spread the work in a few more tightly scoped PRs while achieving the
same original goal.
Yang Wang hace 3 años
padre
commit
7a3dd5ae46

+ 71 - 17
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/Authentication.java

@@ -73,6 +73,9 @@ public class Authentication implements ToXContentObject {
     private final AuthenticationType type;
     private final Map<String, Object> metadata; // authentication contains metadata, includes api_key details (including api_key metadata)
 
+    private final Subject authenticatingSubject;
+    private final Subject effectiveSubject;
+
     public Authentication(User user, RealmRef authenticatedBy, RealmRef lookedUpBy) {
         this(user, authenticatedBy, lookedUpBy, Version.CURRENT, AuthenticationType.REALM, Collections.emptyMap());
     }
@@ -91,6 +94,16 @@ public class Authentication implements ToXContentObject {
         this.version = version;
         this.type = type;
         this.metadata = metadata;
+        if (user.isRunAs()) {
+            authenticatingSubject = new Subject(user.authenticatedUser(), authenticatedBy, version, metadata);
+            // The lookup user for run-as currently doesn't have authentication metadata associated with them because
+            // lookupUser only returns the User object. The lookup user for authorization delegation does have
+            // authentication metadata, but the realm does not expose this difference between authenticatingUser and
+            // delegateUser so effectively this is handled together with the authenticatingSubject not effectiveSubject.
+            effectiveSubject = new Subject(user, lookedUpBy, version, Map.of());
+        } else {
+            authenticatingSubject = effectiveSubject = new Subject(user, authenticatedBy, version, metadata);
+        }
         this.assertApiKeyMetadata();
         this.assertDomainAssignment();
     }
@@ -106,18 +119,64 @@ public class Authentication implements ToXContentObject {
         this.version = in.getVersion();
         type = AuthenticationType.values()[in.readVInt()];
         metadata = in.readMap();
+        if (user.isRunAs()) {
+            authenticatingSubject = new Subject(user.authenticatedUser(), authenticatedBy, version, metadata);
+            // The lookup user for run-as currently doesn't have authentication metadata associated with them because
+            // lookupUser only returns the User object. The lookup user for authorization delegation does have
+            // authentication metadata, but the realm does not expose this difference between authenticatingUser and
+            // delegateUser so effectively this is handled together with the authenticatingSubject not effectiveSubject.
+            effectiveSubject = new Subject(user, lookedUpBy, version, Map.of());
+        } else {
+            authenticatingSubject = effectiveSubject = new Subject(user, authenticatedBy, version, metadata);
+        }
         this.assertApiKeyMetadata();
         this.assertDomainAssignment();
     }
 
+    /**
+     * Get the {@link Subject} that performs the actual authentication. This normally means it provides a credentials.
+     */
+    public Subject getAuthenticatingSubject() {
+        return authenticatingSubject;
+    }
+
+    /**
+     * Get the {@link Subject} that the authentication effectively represents. It may not be the authenticating subject
+     * because the authentication subject can run-as another subject.
+     */
+    public Subject getEffectiveSubject() {
+        return effectiveSubject;
+    }
+
+    /**
+     * Whether the authentication contains a subject run-as another subject. That is, the authentication subject
+     * is different from the effective subject.
+     */
+    public boolean isRunAs() {
+        return authenticatingSubject != effectiveSubject;
+    }
+
+    /**
+     * Use {@code getEffectiveSubject().getUser()} instead.
+     */
+    @Deprecated
     public User getUser() {
         return user;
     }
 
+    /**
+     * Use {@code getAuthenticatingSubject().getRealm()} instead.
+     */
+    @Deprecated
     public RealmRef getAuthenticatedBy() {
         return authenticatedBy;
     }
 
+    /**
+     * The use case for this method is largely trying to tell whether there is a run-as user
+     * and can be replaced by {@code isRunAs}
+     */
+    @Deprecated
     public RealmRef getLookedUpBy() {
         return lookedUpBy;
     }
@@ -125,7 +184,10 @@ public class Authentication implements ToXContentObject {
     /**
      * Get the realm where the effective user comes from.
      * The effective user is the es-security-runas-user if present or the authenticated user.
+     *
+     * Use {@code getEffectiveSubject().getRealm()} instead.
      */
+    @Deprecated
     public RealmRef getSourceRealm() {
         return lookedUpBy == null ? authenticatedBy : lookedUpBy;
     }
@@ -284,19 +346,17 @@ public class Authentication implements ToXContentObject {
 
     /**
      * Whether the authenticating user is an API key, including a simple API key or a token created by an API key.
-     * @return
      */
     public boolean isAuthenticatedAsApiKey() {
-        final boolean result = AuthenticationField.API_KEY_REALM_TYPE.equals(getAuthenticatedBy().getType());
-        assert false == result || AuthenticationField.API_KEY_REALM_NAME.equals(getAuthenticatedBy().getName());
-        return result;
+        return authenticatingSubject.getType() == Subject.Type.API_KEY;
     }
 
-    public boolean isAuthenticatedAnonymously() {
+    // TODO: this is not entirely accurate if anonymous user can create a token
+    private boolean isAuthenticatedAnonymously() {
         return AuthenticationType.ANONYMOUS.equals(getAuthenticationType());
     }
 
-    public boolean isAuthenticatedInternally() {
+    private boolean isAuthenticatedInternally() {
         return AuthenticationType.INTERNAL.equals(getAuthenticationType());
     }
 
@@ -304,10 +364,7 @@ public class Authentication implements ToXContentObject {
      * Authenticate with a service account and no run-as
      */
     public boolean isServiceAccount() {
-        final boolean result = ServiceAccountSettings.REALM_TYPE.equals(getSourceRealm().getType());
-        assert false == result || ServiceAccountSettings.REALM_NAME.equals(getSourceRealm().getName())
-            : "service account realm name mismatch";
-        return result;
+        return effectiveSubject.getType() == Subject.Type.SERVICE_ACCOUNT;
     }
 
     /**
@@ -315,9 +372,7 @@ public class Authentication implements ToXContentObject {
      * or a token created by the API key.
      */
     public boolean isApiKey() {
-        final boolean result = AuthenticationField.API_KEY_REALM_TYPE.equals(getSourceRealm().getType());
-        assert false == result || AuthenticationField.API_KEY_REALM_NAME.equals(getSourceRealm().getName()) : "api key realm name mismatch";
-        return result;
+        return effectiveSubject.getType() == Subject.Type.API_KEY;
     }
 
     /**
@@ -374,10 +429,8 @@ public class Authentication implements ToXContentObject {
         ).containsAll(EnumSet.of(getAuthenticationType(), resourceCreatorAuthentication.getAuthenticationType()))
             : "cross AuthenticationType comparison for canAccessResourcesOf is not applicable for: "
                 + EnumSet.of(getAuthenticationType(), resourceCreatorAuthentication.getAuthenticationType());
-        final AuthenticationContext myAuthContext = AuthenticationContext.fromAuthentication(this);
-        final AuthenticationContext creatorAuthContext = AuthenticationContext.fromAuthentication(resourceCreatorAuthentication);
-        final Subject mySubject = myAuthContext.getEffectiveSubject();
-        final Subject creatorSubject = creatorAuthContext.getEffectiveSubject();
+        final Subject mySubject = getEffectiveSubject();
+        final Subject creatorSubject = resourceCreatorAuthentication.getEffectiveSubject();
         return mySubject.canAccessResourcesOf(creatorSubject);
     }
 
@@ -810,6 +863,7 @@ public class Authentication implements ToXContentObject {
         }
     }
 
+    // TODO: Rename to AuthenticationMethod
     public enum AuthenticationType {
         REALM,
         API_KEY,

+ 0 - 105
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/AuthenticationContext.java

@@ -1,105 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-package org.elasticsearch.xpack.core.security.authc;
-
-import org.elasticsearch.Version;
-import org.elasticsearch.xpack.core.security.user.User;
-
-import java.util.Map;
-
-import static org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType;
-import static org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef;
-
-public class AuthenticationContext {
-
-    private final Version version;
-    private final Subject authenticatingSubject;
-    private final Subject effectiveSubject;
-    // TODO: Rename to AuthenticationMethod
-    private final AuthenticationType type;
-
-    private AuthenticationContext(
-        Version version,
-        Subject authenticatingSubject,
-        Subject effectiveSubject,
-        AuthenticationType authenticationType
-    ) {
-        this.version = version;
-        this.authenticatingSubject = authenticatingSubject;
-        this.effectiveSubject = effectiveSubject;
-        this.type = authenticationType;
-    }
-
-    public boolean isRunAs() {
-        assert authenticatingSubject != null && effectiveSubject != null;
-        return authenticatingSubject != effectiveSubject;
-    }
-
-    public Subject getAuthenticatingSubject() {
-        return authenticatingSubject;
-    }
-
-    public Subject getEffectiveSubject() {
-        return effectiveSubject;
-    }
-
-    public static AuthenticationContext fromAuthentication(Authentication authentication) {
-        final Builder builder = new Builder(authentication.getVersion());
-        builder.authenticationType(authentication.getAuthenticationType());
-        final User user = authentication.getUser();
-        if (user.isRunAs()) {
-            builder.authenticatingSubject(user.authenticatedUser(), authentication.getAuthenticatedBy(), authentication.getMetadata());
-            // The lookup user for run-as currently don't have authentication metadata associated with them because
-            // lookupUser only returns the User object. The lookup user for authorization delegation does have
-            // authentication metadata, but the realm does not expose this difference between authenticatingUser and
-            // delegateUser so effectively this is handled together with the authenticatingSubject not effectiveSubject.
-            builder.effectiveSubject(user, authentication.getLookedUpBy(), Map.of());
-        } else {
-            builder.authenticatingSubject(user, authentication.getAuthenticatedBy(), authentication.getMetadata());
-        }
-        return builder.build();
-    }
-
-    public static class Builder {
-        private final Version version;
-        private AuthenticationType authenticationType;
-        private Subject authenticatingSubject;
-        private Subject effectiveSubject;
-
-        public Builder() {
-            this(Version.CURRENT);
-        }
-
-        public Builder(Version version) {
-            this.version = version;
-        }
-
-        public Builder authenticationType(AuthenticationType authenticationType) {
-            this.authenticationType = authenticationType;
-            return this;
-        }
-
-        public Builder authenticatingSubject(User authenticatingUser, RealmRef authenticatingRealmRef, Map<String, Object> metadata) {
-            this.authenticatingSubject = new Subject(authenticatingUser, authenticatingRealmRef, version, metadata);
-            return this;
-        }
-
-        public Builder effectiveSubject(User effectiveUser, RealmRef lookupRealmRef, Map<String, Object> metadata) {
-            this.effectiveSubject = new Subject(effectiveUser, lookupRealmRef, version, metadata);
-            return this;
-        }
-
-        public AuthenticationContext build() {
-            if (effectiveSubject == null) {
-                effectiveSubject = authenticatingSubject;
-            }
-            return new AuthenticationContext(version, authenticatingSubject, effectiveSubject, authenticationType);
-        }
-    }
-
-}

+ 5 - 1
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/AuthenticationTests.java

@@ -493,6 +493,10 @@ public class AuthenticationTests extends ESTestCase {
      * The authentication can have any version from 7.0.0 to current and random metadata.
      */
     public static Authentication randomAuthentication(User user, RealmRef realmRef) {
+        return randomAuthentication(user, realmRef, randomBoolean());
+    }
+
+    public static Authentication randomAuthentication(User user, RealmRef realmRef, boolean isRunAs) {
         if (user == null) {
             user = randomUser();
         }
@@ -508,7 +512,7 @@ public class AuthenticationTests extends ESTestCase {
                 .distinct()
                 .collect(Collectors.toMap(s -> s, s -> randomAlphaOfLengthBetween(3, 8)));
         }
-        return AuthenticationTestHelper.builder().user(user).realmRef(realmRef).version(version).metadata(metadata).build();
+        return AuthenticationTestHelper.builder().user(user).realmRef(realmRef).version(version).metadata(metadata).build(isRunAs);
     }
 
     public static Authentication randomApiKeyAuthentication(User user, String apiKeyId) {

+ 34 - 77
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ManageOwnApiKeyClusterPrivilegeTests.java

@@ -16,20 +16,14 @@ import org.elasticsearch.xpack.core.security.action.apikey.InvalidateApiKeyReque
 import org.elasticsearch.xpack.core.security.action.apikey.QueryApiKeyAction;
 import org.elasticsearch.xpack.core.security.action.apikey.QueryApiKeyRequest;
 import org.elasticsearch.xpack.core.security.authc.Authentication;
-import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType;
-import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
 import org.elasticsearch.xpack.core.security.authc.AuthenticationTests;
 import org.elasticsearch.xpack.core.security.authc.RealmConfig;
 import org.elasticsearch.xpack.core.security.authc.RealmDomain;
 import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission;
 import org.elasticsearch.xpack.core.security.user.User;
 
-import java.util.HashMap;
-import java.util.Map;
-
 import static org.hamcrest.Matchers.is;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 public class ManageOwnApiKeyClusterPrivilegeTests extends ESTestCase {
 
@@ -38,11 +32,8 @@ public class ManageOwnApiKeyClusterPrivilegeTests extends ESTestCase {
             .build();
 
         final String apiKeyId = randomAlphaOfLengthBetween(4, 7);
-        final HashMap<String, Object> metadata = new HashMap<>();
-        metadata.put(AuthenticationField.API_KEY_ID_KEY, apiKeyId);
-        metadata.put(AuthenticationField.API_KEY_NAME_KEY, randomAlphaOfLengthBetween(1, 16));
         final User userJoe = new User("joe");
-        final Authentication authentication = createMockAuthentication(userJoe, "_es_api_key", AuthenticationType.API_KEY, metadata);
+        final Authentication authentication = AuthenticationTests.randomApiKeyAuthentication(userJoe, apiKeyId);
         final TransportRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyId(apiKeyId, randomBoolean());
         final TransportRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyId(apiKeyId, randomBoolean());
 
@@ -56,11 +47,8 @@ public class ManageOwnApiKeyClusterPrivilegeTests extends ESTestCase {
             .build();
 
         final String apiKeyId = randomAlphaOfLengthBetween(4, 7);
-        final HashMap<String, Object> metadata = new HashMap<>();
-        metadata.put(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLength(7));
-        metadata.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : randomAlphaOfLengthBetween(1, 16));
         final User userJoe = new User("joe");
-        final Authentication authentication = createMockAuthentication(userJoe, "_es_api_key", AuthenticationType.API_KEY, metadata);
+        final Authentication authentication = AuthenticationTests.randomApiKeyAuthentication(userJoe, randomAlphaOfLength(20));
         final TransportRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyId(apiKeyId, randomBoolean());
         final TransportRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyId(apiKeyId, randomBoolean());
 
@@ -117,12 +105,20 @@ public class ManageOwnApiKeyClusterPrivilegeTests extends ESTestCase {
 
         final boolean isRunAs = randomBoolean();
         final User userJoe = new User("joe");
-        final Authentication authentication = createMockAuthentication(
-            isRunAs ? new User(userJoe, new User("not-joe")) : userJoe,
-            "realm1",
-            isRunAs ? randomFrom(AuthenticationType.REALM, AuthenticationType.API_KEY) : AuthenticationType.REALM,
-            Map.of()
-        );
+        final Authentication.RealmRef realmRef = new Authentication.RealmRef("realm1", "realm1", randomAlphaOfLengthBetween(3, 8));
+        final Authentication authentication;
+        if (isRunAs) {
+            final User runByUser = new User("not-joe");
+            if (randomBoolean()) {
+                authentication = Authentication.newRealmAuthentication(runByUser, realmRef).runAs(userJoe, realmRef);
+            } else {
+                authentication = AuthenticationTests.randomApiKeyAuthentication(runByUser, randomAlphaOfLength(20))
+                    .runAs(userJoe, realmRef);
+            }
+        } else {
+            authentication = AuthenticationTests.randomAuthentication(userJoe, realmRef);
+        }
+
         final TransportRequest getApiKeyRequest = GetApiKeyRequest.forOwnedApiKeys();
         final TransportRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.forOwnedApiKeys();
 
@@ -137,12 +133,20 @@ public class ManageOwnApiKeyClusterPrivilegeTests extends ESTestCase {
 
         final boolean isRunAs = randomBoolean();
         final User userJoe = new User("joe");
-        final Authentication authentication = createMockAuthentication(
-            isRunAs ? new User(userJoe, new User("not-joe")) : userJoe,
-            "realm1",
-            isRunAs ? randomFrom(AuthenticationType.REALM, AuthenticationType.API_KEY) : AuthenticationType.REALM,
-            Map.of()
-        );
+        final Authentication.RealmRef realmRef = new Authentication.RealmRef("realm1", "realm1", randomAlphaOfLengthBetween(3, 8));
+        final Authentication authentication;
+        if (isRunAs) {
+            final User runByUser = new User("not-joe");
+            if (randomBoolean()) {
+                authentication = Authentication.newRealmAuthentication(runByUser, realmRef).runAs(userJoe, realmRef);
+            } else {
+                authentication = AuthenticationTests.randomApiKeyAuthentication(runByUser, randomAlphaOfLength(20))
+                    .runAs(userJoe, realmRef);
+            }
+        } else {
+            authentication = AuthenticationTests.randomAuthentication(userJoe, realmRef);
+        }
+
         final TransportRequest getApiKeyRequest = randomFrom(
             GetApiKeyRequest.usingRealmAndUserName("realm1", randomAlphaOfLength(7)),
             GetApiKeyRequest.usingRealmAndUserName(randomAlphaOfLength(5), "joe"),
@@ -162,14 +166,10 @@ public class ManageOwnApiKeyClusterPrivilegeTests extends ESTestCase {
         final ClusterPermission clusterPermission = ManageOwnApiKeyClusterPrivilege.INSTANCE.buildPermission(ClusterPermission.builder())
             .build();
 
-        final Authentication authentication = createMockRunAsAuthentication(
-            "user_a",
-            "realm_a",
-            "realm_a_type",
-            "user_b",
-            "realm_b",
-            "realm_b_type"
-        );
+        final Authentication authentication = Authentication.newRealmAuthentication(
+            new User("user_a"),
+            new Authentication.RealmRef("realm_a", "realm_a_type", randomAlphaOfLengthBetween(3, 8))
+        ).runAs(new User("user_b"), new Authentication.RealmRef("realm_b", "realm_b_type", randomAlphaOfLengthBetween(3, 8)));
 
         assertTrue(
             clusterPermission.check(
@@ -200,47 +200,4 @@ public class ManageOwnApiKeyClusterPrivilegeTests extends ESTestCase {
             is(queryApiKeyRequest.isFilterForCurrentUser())
         );
     }
-
-    private Authentication createMockAuthentication(
-        User user,
-        String realmName,
-        AuthenticationType authenticationType,
-        Map<String, Object> metadata
-    ) {
-        final Authentication authentication = mock(Authentication.class);
-        final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class);
-        when(authentication.getUser()).thenReturn(user);
-        when(authentication.getSourceRealm()).thenReturn(authenticatedBy);
-        when(authentication.getAuthenticationType()).thenReturn(authenticationType);
-        when(authenticatedBy.getName()).thenReturn(realmName);
-        when(authenticatedBy.getType()).thenReturn(realmName);
-        when(authentication.getMetadata()).thenReturn(metadata);
-        when(authentication.isAuthenticatedAsApiKey()).thenCallRealMethod();
-        when(authentication.isApiKey()).thenCallRealMethod();
-        return authentication;
-    }
-
-    private Authentication createMockRunAsAuthentication(
-        String username,
-        String realmName,
-        String realmType,
-        String runAsUsername,
-        String runAsRealmName,
-        String runAsRealmType
-    ) {
-        final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class);
-        when(authenticatedBy.getName()).thenReturn(realmName);
-        when(authenticatedBy.getType()).thenReturn(realmType);
-        final Authentication.RealmRef lookedUpBy = mock(Authentication.RealmRef.class);
-        when(lookedUpBy.getName()).thenReturn(runAsRealmName);
-        when(lookedUpBy.getType()).thenReturn(runAsRealmType);
-        final User user = new User(runAsUsername, new String[0], new User(username));
-        final Authentication authentication = mock(Authentication.class);
-        when(authentication.getUser()).thenReturn(user);
-        when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
-        when(authentication.getSourceRealm()).thenReturn(lookedUpBy);
-        when(authentication.getMetadata()).thenReturn(Map.of());
-        when(authentication.getAuthenticationType()).thenReturn(AuthenticationType.REALM);
-        return authentication;
-    }
 }

+ 5 - 6
x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/profile/ProfileDomainIntegTests.java

@@ -21,7 +21,6 @@ import org.elasticsearch.xpack.core.security.action.profile.Profile;
 import org.elasticsearch.xpack.core.security.action.user.PutUserAction;
 import org.elasticsearch.xpack.core.security.action.user.PutUserRequest;
 import org.elasticsearch.xpack.core.security.authc.Authentication;
-import org.elasticsearch.xpack.core.security.authc.AuthenticationContext;
 import org.elasticsearch.xpack.core.security.authc.AuthenticationTestHelper;
 import org.elasticsearch.xpack.core.security.authc.AuthenticationTests;
 import org.elasticsearch.xpack.core.security.authc.RealmConfig;
@@ -145,7 +144,7 @@ public class ProfileDomainIntegTests extends AbstractProfileIntegTestCase {
                 )
             )
             .build(false);
-        final Subject subject = AuthenticationContext.fromAuthentication(authentication).getEffectiveSubject();
+        final Subject subject = authentication.getEffectiveSubject();
 
         // Profile does not exist yet
         final PlainActionFuture<ProfileService.VersionedDocument> future1 = new PlainActionFuture<>();
@@ -190,7 +189,7 @@ public class ProfileDomainIntegTests extends AbstractProfileIntegTestCase {
             .user(new User("Foo"))
             .realmRef(new Authentication.RealmRef(realmIdentifier1.getName(), realmIdentifier1.getType(), nodeName))
             .build(false);
-        final Subject subject1 = AuthenticationContext.fromAuthentication(authentication1).getEffectiveSubject();
+        final Subject subject1 = authentication1.getEffectiveSubject();
 
         final PlainActionFuture<ProfileService.VersionedDocument> future1 = new PlainActionFuture<>();
         profileService.searchVersionedDocumentForSubject(subject1, future1);
@@ -206,7 +205,7 @@ public class ProfileDomainIntegTests extends AbstractProfileIntegTestCase {
             .user(new User("Foo"))
             .realmRef(new Authentication.RealmRef(realmIdentifier2.getName(), realmIdentifier2.getType(), nodeName, realmDomain1))
             .build(false);
-        final Subject subject2 = AuthenticationContext.fromAuthentication(authentication2).getEffectiveSubject();
+        final Subject subject2 = authentication2.getEffectiveSubject();
 
         final PlainActionFuture<ProfileService.VersionedDocument> future2 = new PlainActionFuture<>();
         profileService.searchVersionedDocumentForSubject(subject2, future2);
@@ -218,7 +217,7 @@ public class ProfileDomainIntegTests extends AbstractProfileIntegTestCase {
             .user(new User("Foo"))
             .realmRef(new Authentication.RealmRef(realmIdentifier2.getName(), realmIdentifier2.getType(), nodeName))
             .build(false);
-        final Subject subject3 = AuthenticationContext.fromAuthentication(authentication3).getEffectiveSubject();
+        final Subject subject3 = authentication3.getEffectiveSubject();
 
         final PlainActionFuture<ProfileService.VersionedDocument> future3 = new PlainActionFuture<>();
         profileService.searchVersionedDocumentForSubject(subject3, future3);
@@ -406,7 +405,7 @@ public class ProfileDomainIntegTests extends AbstractProfileIntegTestCase {
                 authentication = authentication.token();
             }
         }
-        final Subject subject = AuthenticationContext.fromAuthentication(authentication).getEffectiveSubject();
+        final Subject subject = authentication.getEffectiveSubject();
         final ProfileService profileService = getInstanceFromRandomNode(ProfileService.class);
         final PlainActionFuture<Profile> future1 = new PlainActionFuture<>();
         profileService.activateProfile(authentication, future1);

+ 1 - 2
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/support/ApiKeyGenerator.java

@@ -14,7 +14,6 @@ import org.elasticsearch.xcontent.NamedXContentRegistry;
 import org.elasticsearch.xpack.core.security.action.apikey.CreateApiKeyRequest;
 import org.elasticsearch.xpack.core.security.action.apikey.CreateApiKeyResponse;
 import org.elasticsearch.xpack.core.security.authc.Authentication;
-import org.elasticsearch.xpack.core.security.authc.AuthenticationContext;
 import org.elasticsearch.xpack.core.security.authc.Subject;
 import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
 import org.elasticsearch.xpack.core.security.authz.support.DLSRoleQueryValidator;
@@ -54,7 +53,7 @@ public class ApiKeyGenerator {
             apiKeyService.createApiKey(authentication, request, roleDescriptors, listener);
         }, listener::onFailure);
 
-        final Subject effectiveSubject = AuthenticationContext.fromAuthentication(authentication).getEffectiveSubject();
+        final Subject effectiveSubject = authentication.getEffectiveSubject();
 
         // Retain current behaviour that User of an API key authentication has no roles
         if (effectiveSubject.getType() == Subject.Type.API_KEY) {

+ 3 - 5
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java

@@ -26,7 +26,6 @@ import org.elasticsearch.core.Nullable;
 import org.elasticsearch.core.Tuple;
 import org.elasticsearch.license.XPackLicenseState;
 import org.elasticsearch.xpack.core.security.authc.Authentication;
-import org.elasticsearch.xpack.core.security.authc.AuthenticationContext;
 import org.elasticsearch.xpack.core.security.authc.Subject;
 import org.elasticsearch.xpack.core.security.authz.RestrictedIndices;
 import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
@@ -173,11 +172,10 @@ public class CompositeRolesStore {
     }
 
     public void getRoles(Authentication authentication, ActionListener<Tuple<Role, Role>> roleActionListener) {
-        final AuthenticationContext authenticationContext = AuthenticationContext.fromAuthentication(authentication);
-        getRole(authenticationContext.getEffectiveSubject(), ActionListener.wrap(role -> {
-            if (authenticationContext.isRunAs()) {
+        getRole(authentication.getEffectiveSubject(), ActionListener.wrap(role -> {
+            if (authentication.isRunAs()) {
                 getRole(
-                    authenticationContext.getAuthenticatingSubject(),
+                    authentication.getAuthenticatingSubject(),
                     ActionListener.wrap(
                         authenticatingRole -> roleActionListener.onResponse(new Tuple<>(role, authenticatingRole)),
                         roleActionListener::onFailure

+ 1 - 2
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/profile/ProfileService.java

@@ -58,7 +58,6 @@ import org.elasticsearch.xpack.core.security.action.profile.SuggestProfilesReque
 import org.elasticsearch.xpack.core.security.action.profile.SuggestProfilesResponse;
 import org.elasticsearch.xpack.core.security.action.profile.UpdateProfileDataRequest;
 import org.elasticsearch.xpack.core.security.authc.Authentication;
-import org.elasticsearch.xpack.core.security.authc.AuthenticationContext;
 import org.elasticsearch.xpack.core.security.authc.Subject;
 import org.elasticsearch.xpack.core.security.user.User;
 import org.elasticsearch.xpack.security.support.SecurityIndexManager;
@@ -119,7 +118,7 @@ public class ProfileService {
      *                       to submit the request.
      */
     public void activateProfile(Authentication authentication, ActionListener<Profile> listener) {
-        final Subject subject = AuthenticationContext.fromAuthentication(authentication).getEffectiveSubject();
+        final Subject subject = authentication.getEffectiveSubject();
         if (Subject.Type.USER != subject.getType()) {
             listener.onFailure(
                 new IllegalArgumentException(

+ 12 - 11
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyServiceTests.java

@@ -120,6 +120,7 @@ import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
 import static org.elasticsearch.test.ActionListenerUtils.anyActionListener;
 import static org.elasticsearch.test.SecurityIntegTestCase.getFastStoredHashAlgoForTests;
 import static org.elasticsearch.test.TestMatchers.throwableWithMessage;
+import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_ID_KEY;
 import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_METADATA_KEY;
 import static org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR;
 import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.INTERNAL_SECURITY_MAIN_INDEX_7;
@@ -1683,20 +1684,20 @@ public class ApiKeyServiceTests extends ESTestCase {
     }
 
     public void testGetApiKeyMetadata() throws IOException {
-        final Authentication apiKeyAuthentication = mock(Authentication.class);
-        when(apiKeyAuthentication.getAuthenticationType()).thenReturn(AuthenticationType.API_KEY);
-        when(apiKeyAuthentication.getAuthenticatedBy()).thenReturn(
-            new RealmRef(AuthenticationField.API_KEY_REALM_NAME, AuthenticationField.API_KEY_REALM_TYPE, randomAlphaOfLengthBetween(3, 8))
-        );
-        when(apiKeyAuthentication.isAuthenticatedAsApiKey()).thenCallRealMethod();
+        final Map<String, Object> metadata;
         final Map<String, Object> apiKeyMetadata = ApiKeyTests.randomMetadata();
         if (apiKeyMetadata == null) {
-            when(apiKeyAuthentication.getMetadata()).thenReturn(Map.of());
+            metadata = Map.of(API_KEY_ID_KEY, randomAlphaOfLength(20));
         } else {
             final BytesReference metadataBytes = XContentTestUtils.convertToXContent(apiKeyMetadata, XContentType.JSON);
-            when(apiKeyAuthentication.getMetadata()).thenReturn(Map.of(API_KEY_METADATA_KEY, metadataBytes));
+            metadata = Map.of(API_KEY_ID_KEY, randomAlphaOfLength(20), API_KEY_METADATA_KEY, metadataBytes);
         }
 
+        final Authentication apiKeyAuthentication = Authentication.newApiKeyAuthentication(
+            AuthenticationResult.success(AuthenticationTests.randomUser(), metadata),
+            randomAlphaOfLengthBetween(3, 8)
+        );
+
         final Map<String, Object> restoredApiKeyMetadata = ApiKeyService.getApiKeyMetadata(apiKeyAuthentication);
         if (apiKeyMetadata == null) {
             assertThat(restoredApiKeyMetadata, anEmptyMap());
@@ -1704,9 +1705,9 @@ public class ApiKeyServiceTests extends ESTestCase {
             assertThat(restoredApiKeyMetadata, equalTo(apiKeyMetadata));
         }
 
-        final Authentication authentication = mock(Authentication.class);
-        when(authentication.getAuthenticatedBy()).thenReturn(
-            new RealmRef(randomAlphaOfLengthBetween(5, 10), randomAlphaOfLengthBetween(5, 10), randomAlphaOfLengthBetween(3, 8))
+        final Authentication authentication = AuthenticationTests.randomAuthentication(
+            AuthenticationTests.randomUser(),
+            AuthenticationTests.randomRealmRef(randomBoolean())
         );
         final IllegalArgumentException e = expectThrows(
             IllegalArgumentException.class,

+ 5 - 41
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java

@@ -47,8 +47,8 @@ import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesResponse;
 import org.elasticsearch.xpack.core.security.action.user.PutUserAction;
 import org.elasticsearch.xpack.core.security.action.user.UserRequest;
 import org.elasticsearch.xpack.core.security.authc.Authentication;
-import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType;
 import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
+import org.elasticsearch.xpack.core.security.authc.AuthenticationTests;
 import org.elasticsearch.xpack.core.security.authc.esnative.NativeRealmSettings;
 import org.elasticsearch.xpack.core.security.authc.file.FileRealmSettings;
 import org.elasticsearch.xpack.core.security.authc.ldap.LdapRealmSettings;
@@ -80,7 +80,6 @@ import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.function.Predicate;
@@ -333,19 +332,8 @@ public class RBACEngineTests extends ESTestCase {
     public void testSameUserPermissionAllowsSelfApiKeyInfoRetrievalWhenAuthenticatedByApiKey() {
         final User user = new User("joe");
         final String apiKeyId = randomAlphaOfLengthBetween(4, 7);
+        final Authentication authentication = AuthenticationTests.randomApiKeyAuthentication(user, apiKeyId);
         final TransportRequest request = GetApiKeyRequest.usingApiKeyId(apiKeyId, false);
-        final Authentication authentication = mock(Authentication.class);
-        final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class);
-        when(authenticatedBy.getName()).thenReturn(AuthenticationField.API_KEY_REALM_NAME);
-        when(authenticatedBy.getType()).thenReturn(AuthenticationField.API_KEY_REALM_TYPE);
-        when(authentication.getUser()).thenReturn(user);
-        when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
-        when(authentication.getAuthenticationType()).thenReturn(AuthenticationType.API_KEY);
-        when(authentication.getSourceRealm()).thenReturn(authenticatedBy);
-        when(authentication.getMetadata()).thenReturn(Map.of(AuthenticationField.API_KEY_ID_KEY, apiKeyId));
-        when(authentication.isAuthenticatedAsApiKey()).thenCallRealMethod();
-        when(authentication.isApiKey()).thenCallRealMethod();
-
         assertTrue(RBACEngine.checkSameUserPermissions(GetApiKeyAction.NAME, request, authentication));
     }
 
@@ -353,17 +341,7 @@ public class RBACEngineTests extends ESTestCase {
         final User user = new User("joe");
         final String apiKeyId = randomAlphaOfLengthBetween(4, 7);
         final TransportRequest request = GetApiKeyRequest.usingApiKeyId(apiKeyId, false);
-        final Authentication authentication = mock(Authentication.class);
-        final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class);
-        when(authentication.getUser()).thenReturn(user);
-        when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
-        when(authenticatedBy.getName()).thenReturn(AuthenticationField.API_KEY_REALM_NAME);
-        when(authenticatedBy.getType()).thenReturn(AuthenticationField.API_KEY_REALM_TYPE);
-        when(authentication.getSourceRealm()).thenReturn(authenticatedBy);
-        when(authentication.getMetadata()).thenReturn(Map.of(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLengthBetween(4, 7)));
-        when(authentication.isAuthenticatedAsApiKey()).thenCallRealMethod();
-        when(authentication.isApiKey()).thenCallRealMethod();
-
+        final Authentication authentication = AuthenticationTests.randomApiKeyAuthentication(user, randomAlphaOfLength(8));
         assertFalse(RBACEngine.checkSameUserPermissions(GetApiKeyAction.NAME, request, authentication));
     }
 
@@ -371,22 +349,8 @@ public class RBACEngineTests extends ESTestCase {
         final User user = new User("joe");
         final String apiKeyId = randomAlphaOfLengthBetween(4, 7);
         final TransportRequest request = GetApiKeyRequest.usingApiKeyId(apiKeyId, false);
-        final Authentication authentication = mock(Authentication.class);
-        final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class);
-        final Authentication.RealmRef lookedupBy = mock(Authentication.RealmRef.class);
-        when(authentication.getUser()).thenReturn(user);
-        when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
-        when(authenticatedBy.getName()).thenReturn(AuthenticationField.API_KEY_REALM_NAME);
-        when(authenticatedBy.getType()).thenReturn(AuthenticationField.API_KEY_REALM_TYPE);
-        when(authentication.getLookedUpBy()).thenReturn(lookedupBy);
-        when(lookedupBy.getName()).thenReturn("name");
-        when(lookedupBy.getType()).thenReturn("type");
-        when(authentication.getSourceRealm()).thenReturn(lookedupBy);
-        when(authentication.getAuthenticationType()).thenReturn(AuthenticationType.API_KEY);
-        when(authentication.getMetadata()).thenReturn(Map.of(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLengthBetween(4, 7)));
-        when(authentication.isAuthenticatedAsApiKey()).thenCallRealMethod();
-        when(authentication.isApiKey()).thenCallRealMethod();
-
+        final Authentication authentication = AuthenticationTests.randomApiKeyAuthentication(new User("not-joe"), apiKeyId)
+            .runAs(user, new Authentication.RealmRef("name", "type", randomAlphaOfLengthBetween(3, 8)));
         assertFalse(RBACEngine.checkSameUserPermissions(GetApiKeyAction.NAME, request, authentication));
     }
 

+ 7 - 8
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java

@@ -55,7 +55,6 @@ import org.elasticsearch.xpack.core.security.action.user.PutUserAction;
 import org.elasticsearch.xpack.core.security.authc.Authentication;
 import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType;
 import org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef;
-import org.elasticsearch.xpack.core.security.authc.AuthenticationContext;
 import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
 import org.elasticsearch.xpack.core.security.authc.Subject;
 import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
@@ -1488,7 +1487,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
         );
 
         PlainActionFuture<Role> roleFuture = new PlainActionFuture<>();
-        compositeRolesStore.getRole(AuthenticationContext.fromAuthentication(authentication).getEffectiveSubject(), roleFuture);
+        compositeRolesStore.getRole(authentication.getEffectiveSubject(), roleFuture);
         Role role = roleFuture.actionGet();
         assertThat(effectiveRoleDescriptors.get(), is(nullValue()));
 
@@ -1562,7 +1561,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
         final String apiKeyId = (String) authentication.getMetadata().get(API_KEY_ID_KEY);
 
         PlainActionFuture<Role> roleFuture = new PlainActionFuture<>();
-        compositeRolesStore.getRole(AuthenticationContext.fromAuthentication(authentication).getEffectiveSubject(), roleFuture);
+        compositeRolesStore.getRole(authentication.getEffectiveSubject(), roleFuture);
         Role role = roleFuture.actionGet();
         assertThat(role.checkClusterAction("cluster:admin/foo", Empty.INSTANCE, mock(Authentication.class)), is(false));
         assertThat(effectiveRoleDescriptors.get(), is(nullValue()));
@@ -1631,7 +1630,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
             )
         );
         final PlainActionFuture<Role> future1 = new PlainActionFuture<>();
-        compositeRolesStore.getRole(AuthenticationContext.fromAuthentication(authentication1).getAuthenticatingSubject(), future1);
+        compositeRolesStore.getRole(authentication1.getAuthenticatingSubject(), future1);
         future1.actionGet();
         verify(apiKeyService).parseRoleDescriptorsBytes(apiKeyId, limitedByRoleDescriptorBytes, RoleReference.ApiKeyRoleType.LIMITED_BY);
 
@@ -1652,7 +1651,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
             listener.onResponse(new RoleDescriptor(authenticatedUser2.principal(), null, null, null));
             return null;
         }).when(serviceAccountService).getRoleDescriptorForPrincipal(eq(authenticatedUser2.principal()), anyActionListener());
-        compositeRolesStore.getRole(AuthenticationContext.fromAuthentication(authentication2).getAuthenticatingSubject(), future2);
+        compositeRolesStore.getRole(authentication2.getAuthenticatingSubject(), future2);
         future2.actionGet();
         verify(serviceAccountService).getRoleDescriptorForPrincipal(eq(authenticatedUser2.principal()), anyActionListener());
     }
@@ -1822,7 +1821,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
         );
 
         PlainActionFuture<Role> roleFuture = new PlainActionFuture<>();
-        compositeRolesStore.getRole(AuthenticationContext.fromAuthentication(authentication).getEffectiveSubject(), roleFuture);
+        compositeRolesStore.getRole(authentication.getEffectiveSubject(), roleFuture);
         roleFuture.actionGet();
         assertThat(effectiveRoleDescriptors.get(), is(nullValue()));
         verify(apiKeyService).parseRoleDescriptorsBytes("key-id-1", roleBytes, RoleReference.ApiKeyRoleType.ASSIGNED);
@@ -1843,7 +1842,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
             metadata2
         );
         roleFuture = new PlainActionFuture<>();
-        compositeRolesStore.getRole(AuthenticationContext.fromAuthentication(authentication).getEffectiveSubject(), roleFuture);
+        compositeRolesStore.getRole(authentication.getEffectiveSubject(), roleFuture);
         roleFuture.actionGet();
         assertThat(effectiveRoleDescriptors.get(), is(nullValue()));
         verify(apiKeyService, never()).parseRoleDescriptorsBytes(eq("key-id-2"), any(BytesReference.class), any());
@@ -1864,7 +1863,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
             metadata3
         );
         roleFuture = new PlainActionFuture<>();
-        compositeRolesStore.getRole(AuthenticationContext.fromAuthentication(authentication).getEffectiveSubject(), roleFuture);
+        compositeRolesStore.getRole(authentication.getEffectiveSubject(), roleFuture);
         roleFuture.actionGet();
         assertThat(effectiveRoleDescriptors.get(), is(nullValue()));
         verify(apiKeyService).parseRoleDescriptorsBytes("key-id-3", anotherRoleBytes, RoleReference.ApiKeyRoleType.ASSIGNED);