Skip to content

Commit 7a3dd5a

Browse files
authored
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.
1 parent 5bab086 commit 7a3dd5a

File tree

11 files changed

+144
-275
lines changed

11 files changed

+144
-275
lines changed

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

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -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,

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

Lines changed: 0 additions & 105 deletions
This file was deleted.

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,10 @@ public static RealmRef randomRealmRef(boolean underDomain, boolean includeIntern
493493
* The authentication can have any version from 7.0.0 to current and random metadata.
494494
*/
495495
public static Authentication randomAuthentication(User user, RealmRef realmRef) {
496+
return randomAuthentication(user, realmRef, randomBoolean());
497+
}
498+
499+
public static Authentication randomAuthentication(User user, RealmRef realmRef, boolean isRunAs) {
496500
if (user == null) {
497501
user = randomUser();
498502
}
@@ -508,7 +512,7 @@ public static Authentication randomAuthentication(User user, RealmRef realmRef)
508512
.distinct()
509513
.collect(Collectors.toMap(s -> s, s -> randomAlphaOfLengthBetween(3, 8)));
510514
}
511-
return AuthenticationTestHelper.builder().user(user).realmRef(realmRef).version(version).metadata(metadata).build();
515+
return AuthenticationTestHelper.builder().user(user).realmRef(realmRef).version(version).metadata(metadata).build(isRunAs);
512516
}
513517

514518
public static Authentication randomApiKeyAuthentication(User user, String apiKeyId) {

0 commit comments

Comments
 (0)