@@ -73,6 +73,9 @@ public class Authentication implements ToXContentObject {
7373 private final AuthenticationType type ;
7474 private final Map <String , Object > metadata ; // authentication contains metadata, includes api_key details (including api_key metadata)
7575
76+ private final Subject authenticatingSubject ;
77+ private final Subject effectiveSubject ;
78+
7679 public Authentication (User user , RealmRef authenticatedBy , RealmRef lookedUpBy ) {
7780 this (user , authenticatedBy , lookedUpBy , Version .CURRENT , AuthenticationType .REALM , Collections .emptyMap ());
7881 }
@@ -91,6 +94,16 @@ public Authentication(
9194 this .version = version ;
9295 this .type = type ;
9396 this .metadata = metadata ;
97+ if (user .isRunAs ()) {
98+ authenticatingSubject = new Subject (user .authenticatedUser (), authenticatedBy , version , metadata );
99+ // The lookup user for run-as currently doesn't have authentication metadata associated with them because
100+ // lookupUser only returns the User object. The lookup user for authorization delegation does have
101+ // authentication metadata, but the realm does not expose this difference between authenticatingUser and
102+ // delegateUser so effectively this is handled together with the authenticatingSubject not effectiveSubject.
103+ effectiveSubject = new Subject (user , lookedUpBy , version , Map .of ());
104+ } else {
105+ authenticatingSubject = effectiveSubject = new Subject (user , authenticatedBy , version , metadata );
106+ }
94107 this .assertApiKeyMetadata ();
95108 this .assertDomainAssignment ();
96109 }
@@ -106,26 +119,75 @@ public Authentication(StreamInput in) throws IOException {
106119 this .version = in .getVersion ();
107120 type = AuthenticationType .values ()[in .readVInt ()];
108121 metadata = in .readMap ();
122+ if (user .isRunAs ()) {
123+ authenticatingSubject = new Subject (user .authenticatedUser (), authenticatedBy , version , metadata );
124+ // The lookup user for run-as currently doesn't have authentication metadata associated with them because
125+ // lookupUser only returns the User object. The lookup user for authorization delegation does have
126+ // authentication metadata, but the realm does not expose this difference between authenticatingUser and
127+ // delegateUser so effectively this is handled together with the authenticatingSubject not effectiveSubject.
128+ effectiveSubject = new Subject (user , lookedUpBy , version , Map .of ());
129+ } else {
130+ authenticatingSubject = effectiveSubject = new Subject (user , authenticatedBy , version , metadata );
131+ }
109132 this .assertApiKeyMetadata ();
110133 this .assertDomainAssignment ();
111134 }
112135
136+ /**
137+ * Get the {@link Subject} that performs the actual authentication. This normally means it provides a credentials.
138+ */
139+ public Subject getAuthenticatingSubject () {
140+ return authenticatingSubject ;
141+ }
142+
143+ /**
144+ * Get the {@link Subject} that the authentication effectively represents. It may not be the authenticating subject
145+ * because the authentication subject can run-as another subject.
146+ */
147+ public Subject getEffectiveSubject () {
148+ return effectiveSubject ;
149+ }
150+
151+ /**
152+ * Whether the authentication contains a subject run-as another subject. That is, the authentication subject
153+ * is different from the effective subject.
154+ */
155+ public boolean isRunAs () {
156+ return authenticatingSubject != effectiveSubject ;
157+ }
158+
159+ /**
160+ * Use {@code getEffectiveSubject().getUser()} instead.
161+ */
162+ @ Deprecated
113163 public User getUser () {
114164 return user ;
115165 }
116166
167+ /**
168+ * Use {@code getAuthenticatingSubject().getRealm()} instead.
169+ */
170+ @ Deprecated
117171 public RealmRef getAuthenticatedBy () {
118172 return authenticatedBy ;
119173 }
120174
175+ /**
176+ * The use case for this method is largely trying to tell whether there is a run-as user
177+ * and can be replaced by {@code isRunAs}
178+ */
179+ @ Deprecated
121180 public RealmRef getLookedUpBy () {
122181 return lookedUpBy ;
123182 }
124183
125184 /**
126185 * Get the realm where the effective user comes from.
127186 * The effective user is the es-security-runas-user if present or the authenticated user.
187+ *
188+ * Use {@code getEffectiveSubject().getRealm()} instead.
128189 */
190+ @ Deprecated
129191 public RealmRef getSourceRealm () {
130192 return lookedUpBy == null ? authenticatedBy : lookedUpBy ;
131193 }
@@ -284,40 +346,33 @@ public boolean isAuthenticatedWithServiceAccount() {
284346
285347 /**
286348 * Whether the authenticating user is an API key, including a simple API key or a token created by an API key.
287- * @return
288349 */
289350 public boolean isAuthenticatedAsApiKey () {
290- final boolean result = AuthenticationField .API_KEY_REALM_TYPE .equals (getAuthenticatedBy ().getType ());
291- assert false == result || AuthenticationField .API_KEY_REALM_NAME .equals (getAuthenticatedBy ().getName ());
292- return result ;
351+ return authenticatingSubject .getType () == Subject .Type .API_KEY ;
293352 }
294353
295- public boolean isAuthenticatedAnonymously () {
354+ // TODO: this is not entirely accurate if anonymous user can create a token
355+ private boolean isAuthenticatedAnonymously () {
296356 return AuthenticationType .ANONYMOUS .equals (getAuthenticationType ());
297357 }
298358
299- public boolean isAuthenticatedInternally () {
359+ private boolean isAuthenticatedInternally () {
300360 return AuthenticationType .INTERNAL .equals (getAuthenticationType ());
301361 }
302362
303363 /**
304364 * Authenticate with a service account and no run-as
305365 */
306366 public boolean isServiceAccount () {
307- final boolean result = ServiceAccountSettings .REALM_TYPE .equals (getSourceRealm ().getType ());
308- assert false == result || ServiceAccountSettings .REALM_NAME .equals (getSourceRealm ().getName ())
309- : "service account realm name mismatch" ;
310- return result ;
367+ return effectiveSubject .getType () == Subject .Type .SERVICE_ACCOUNT ;
311368 }
312369
313370 /**
314371 * Whether the effective user is an API key, this including a simple API key authentication
315372 * or a token created by the API key.
316373 */
317374 public boolean isApiKey () {
318- final boolean result = AuthenticationField .API_KEY_REALM_TYPE .equals (getSourceRealm ().getType ());
319- assert false == result || AuthenticationField .API_KEY_REALM_NAME .equals (getSourceRealm ().getName ()) : "api key realm name mismatch" ;
320- return result ;
375+ return effectiveSubject .getType () == Subject .Type .API_KEY ;
321376 }
322377
323378 /**
@@ -374,10 +429,8 @@ public boolean canAccessResourcesOf(Authentication resourceCreatorAuthentication
374429 ).containsAll (EnumSet .of (getAuthenticationType (), resourceCreatorAuthentication .getAuthenticationType ()))
375430 : "cross AuthenticationType comparison for canAccessResourcesOf is not applicable for: "
376431 + EnumSet .of (getAuthenticationType (), resourceCreatorAuthentication .getAuthenticationType ());
377- final AuthenticationContext myAuthContext = AuthenticationContext .fromAuthentication (this );
378- final AuthenticationContext creatorAuthContext = AuthenticationContext .fromAuthentication (resourceCreatorAuthentication );
379- final Subject mySubject = myAuthContext .getEffectiveSubject ();
380- final Subject creatorSubject = creatorAuthContext .getEffectiveSubject ();
432+ final Subject mySubject = getEffectiveSubject ();
433+ final Subject creatorSubject = resourceCreatorAuthentication .getEffectiveSubject ();
381434 return mySubject .canAccessResourcesOf (creatorSubject );
382435 }
383436
@@ -810,6 +863,7 @@ static boolean equivalentRealms(String name1, String type1, String name2, String
810863 }
811864 }
812865
866+ // TODO: Rename to AuthenticationMethod
813867 public enum AuthenticationType {
814868 REALM ,
815869 API_KEY ,
0 commit comments