Skip to content

Commit 23af171

Browse files
authored
Disallow Password Change when authenticated by Token (#49694) (#53614)
Password changes are only allowed when the user is currently authenticated by a realm (that permits the password to be changed) and not when authenticated by a bearer token or an API key.
1 parent 7f21ade commit 23af171

File tree

3 files changed

+123
-6
lines changed

3 files changed

+123
-6
lines changed

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/RBACEngine.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -539,9 +539,12 @@ private static boolean checkChangePasswordAction(Authentication authentication)
539539
}
540540

541541
assert realmType != null;
542-
// ensure the user was authenticated by a realm that we can change a password for. The native realm is an internal realm and
543-
// right now only one can exist in the realm configuration - if this changes we should update this check
544-
return ReservedRealm.TYPE.equals(realmType) || NativeRealmSettings.TYPE.equals(realmType);
542+
// Ensure that the user is not authenticated with an access token or an API key.
543+
// Also ensure that the user was authenticated by a realm that we can change a password for. The native realm is an internal realm
544+
// and right now only one can exist in the realm configuration - if this changes we should update this check
545+
final Authentication.AuthenticationType authType = authentication.getAuthenticationType();
546+
return (authType.equals(Authentication.AuthenticationType.REALM)
547+
&& (ReservedRealm.TYPE.equals(realmType) || NativeRealmSettings.TYPE.equals(realmType)));
545548
}
546549

547550
static class RBACAuthorizationInfo implements AuthorizationInfo {

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

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public void testSameUserPermission() {
102102
final String action = changePasswordRequest ? ChangePasswordAction.NAME : AuthenticateAction.NAME;
103103
final Authentication authentication = mock(Authentication.class);
104104
final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class);
105+
when(authentication.getAuthenticationType()).thenReturn(Authentication.AuthenticationType.REALM);
105106
when(authentication.getUser()).thenReturn(user);
106107
when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
107108
when(authenticatedBy.getType())
@@ -125,9 +126,10 @@ public void testSameUserPermissionDoesNotAllowNonMatchingUsername() {
125126
final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class);
126127
when(authentication.getUser()).thenReturn(user);
127128
when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
128-
when(authenticatedBy.getType())
129-
.thenReturn(changePasswordRequest ? randomFrom(ReservedRealm.TYPE, NativeRealmSettings.TYPE) :
130-
randomAlphaOfLengthBetween(4, 12));
129+
final String authenticationType = changePasswordRequest ? randomFrom(ReservedRealm.TYPE, NativeRealmSettings.TYPE) :
130+
randomAlphaOfLengthBetween(4, 12);
131+
when(authenticatedBy.getType()).thenReturn(authenticationType);
132+
when(authentication.getAuthenticationType()).thenReturn(Authentication.AuthenticationType.REALM);
131133

132134
assertThat(request, instanceOf(UserRequest.class));
133135
assertFalse(engine.checkSameUserPermissions(action, request, authentication));
@@ -180,6 +182,7 @@ public void testSameUserPermissionRunAsChecksAuthenticatedBy() {
180182
final Authentication authentication = mock(Authentication.class);
181183
final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class);
182184
final Authentication.RealmRef lookedUpBy = mock(Authentication.RealmRef.class);
185+
when(authentication.getAuthenticationType()).thenReturn(Authentication.AuthenticationType.REALM);
183186
when(authentication.getUser()).thenReturn(user);
184187
when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
185188
when(authentication.getLookedUpBy()).thenReturn(lookedUpBy);
@@ -198,6 +201,7 @@ public void testSameUserPermissionDoesNotAllowChangePasswordForOtherRealms() {
198201
final String action = ChangePasswordAction.NAME;
199202
final Authentication authentication = mock(Authentication.class);
200203
final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class);
204+
when(authentication.getAuthenticationType()).thenReturn(Authentication.AuthenticationType.REALM);
201205
when(authentication.getUser()).thenReturn(user);
202206
when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
203207
when(authenticatedBy.getType()).thenReturn(randomFrom(LdapRealmSettings.LDAP_TYPE, FileRealmSettings.TYPE,
@@ -209,6 +213,47 @@ public void testSameUserPermissionDoesNotAllowChangePasswordForOtherRealms() {
209213
verify(authenticatedBy).getType();
210214
verify(authentication).getAuthenticatedBy();
211215
verify(authentication, times(2)).getUser();
216+
verify(authentication).getAuthenticationType();
217+
verifyNoMoreInteractions(authenticatedBy, authentication);
218+
}
219+
220+
public void testSameUserPermissionDoesNotAllowChangePasswordForApiKey() {
221+
final User user = new User("joe");
222+
final ChangePasswordRequest request = new ChangePasswordRequestBuilder(mock(Client.class)).username(user.principal()).request();
223+
final String action = ChangePasswordAction.NAME;
224+
final Authentication authentication = mock(Authentication.class);
225+
final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class);
226+
when(authentication.getUser()).thenReturn(user);
227+
when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
228+
when(authentication.getAuthenticationType()).thenReturn(Authentication.AuthenticationType.API_KEY);
229+
when(authenticatedBy.getType()).thenReturn(ApiKeyService.API_KEY_REALM_TYPE);
230+
231+
assertThat(request, instanceOf(UserRequest.class));
232+
assertFalse(engine.checkSameUserPermissions(action, request, authentication));
233+
verify(authenticatedBy).getType();
234+
verify(authentication).getAuthenticatedBy();
235+
verify(authentication, times(2)).getUser();
236+
verify(authentication).getAuthenticationType();
237+
verifyNoMoreInteractions(authenticatedBy, authentication);
238+
}
239+
240+
public void testSameUserPermissionDoesNotAllowChangePasswordForAccessToken() {
241+
final User user = new User("joe");
242+
final ChangePasswordRequest request = new ChangePasswordRequestBuilder(mock(Client.class)).username(user.principal()).request();
243+
final String action = ChangePasswordAction.NAME;
244+
final Authentication authentication = mock(Authentication.class);
245+
final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class);
246+
when(authentication.getUser()).thenReturn(user);
247+
when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
248+
when(authentication.getAuthenticationType()).thenReturn(Authentication.AuthenticationType.TOKEN);
249+
when(authenticatedBy.getType()).thenReturn(NativeRealmSettings.TYPE);
250+
251+
assertThat(request, instanceOf(UserRequest.class));
252+
assertFalse(engine.checkSameUserPermissions(action, request, authentication));
253+
verify(authenticatedBy).getType();
254+
verify(authentication).getAuthenticatedBy();
255+
verify(authentication, times(2)).getUser();
256+
verify(authentication).getAuthenticationType();
212257
verifyNoMoreInteractions(authenticatedBy, authentication);
213258
}
214259

@@ -220,6 +265,7 @@ public void testSameUserPermissionDoesNotAllowChangePasswordForLookedUpByOtherRe
220265
final Authentication authentication = mock(Authentication.class);
221266
final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class);
222267
final Authentication.RealmRef lookedUpBy = mock(Authentication.RealmRef.class);
268+
when(authentication.getAuthenticationType()).thenReturn(Authentication.AuthenticationType.REALM);
223269
when(authentication.getUser()).thenReturn(user);
224270
when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
225271
when(authentication.getLookedUpBy()).thenReturn(lookedUpBy);
@@ -232,6 +278,7 @@ public void testSameUserPermissionDoesNotAllowChangePasswordForLookedUpByOtherRe
232278
verify(authentication).getLookedUpBy();
233279
verify(authentication, times(2)).getUser();
234280
verify(lookedUpBy).getType();
281+
verify(authentication).getAuthenticationType();
235282
verifyNoMoreInteractions(authentication, lookedUpBy, authenticatedBy);
236283
}
237284

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
setup:
3+
- skip:
4+
features: headers
5+
- do:
6+
cluster.health:
7+
wait_for_status: yellow
8+
- do:
9+
security.put_user:
10+
username: "token_joe"
11+
body: >
12+
{
13+
"password": "s3krit",
14+
"roles" : [ "token_admin" ]
15+
}
16+
- do:
17+
security.put_role:
18+
name: "token_admin"
19+
body: >
20+
{
21+
"cluster": ["manage_token"],
22+
"indices": [
23+
{
24+
"names": "*",
25+
"privileges": ["all"]
26+
}
27+
]
28+
}
29+
---
30+
teardown:
31+
- do:
32+
security.delete_user:
33+
username: "token_joe"
34+
ignore: 404
35+
- do:
36+
security.delete_role:
37+
name: "token_admin"
38+
ignore: 404
39+
40+
---
41+
"Test user changing their password authenticating with token not allowed":
42+
43+
- do:
44+
headers:
45+
Authorization: "Basic dG9rZW5fam9lOnMza3JpdA=="
46+
security.get_token:
47+
body:
48+
grant_type: "password"
49+
username: "token_joe"
50+
password: "s3krit"
51+
52+
- match: { type: "Bearer" }
53+
- is_true: access_token
54+
- set: { access_token: token }
55+
- match: { expires_in: 1200 }
56+
- is_false: scope
57+
58+
- do:
59+
headers:
60+
Authorization: Bearer ${token}
61+
catch: forbidden
62+
security.change_password:
63+
username: "joe"
64+
body: >
65+
{
66+
"password" : "s3krit2"
67+
}

0 commit comments

Comments
 (0)