Skip to content

Commit 8255352

Browse files
authored
Respect runas realm for ApiKey security operations (#52178) (#52932)
When user A runs as user B and performs any API key related operations, user B's realm should always be used to associate with the API key. Currently user A's realm is used when getting or invalidating API keys and owner=true. The PR is to fix this bug. resolves: #51975
1 parent 866b087 commit 8255352

File tree

7 files changed

+226
-27
lines changed

7 files changed

+226
-27
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ManageOwnApiKeyClusterPrivilege.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,22 +78,22 @@ private boolean checkIfUserIsOwnerOfApiKeys(Authentication authentication, Strin
7878
* TODO bizybot we need to think on how we can propagate appropriate error message to the end user when username, realm name
7979
* is missing. This is similar to the problem of propagating right error messages in case of access denied.
8080
*/
81-
if (authentication.getAuthenticatedBy().getType().equals(API_KEY_REALM_TYPE)) {
81+
if (authentication.getSourceRealm().getType().equals(API_KEY_REALM_TYPE)) {
8282
// API key cannot own any other API key so deny access
8383
return false;
8484
} else if (ownedByAuthenticatedUser) {
8585
return true;
8686
} else if (Strings.hasText(username) && Strings.hasText(realmName)) {
87-
final String authenticatedUserPrincipal = authentication.getUser().principal();
88-
final String authenticatedUserRealm = authentication.getAuthenticatedBy().getName();
89-
return username.equals(authenticatedUserPrincipal) && realmName.equals(authenticatedUserRealm);
87+
final String sourceUserPrincipal = authentication.getUser().principal();
88+
final String sourceRealmName = authentication.getSourceRealm().getName();
89+
return username.equals(sourceUserPrincipal) && realmName.equals(sourceRealmName);
9090
}
9191
}
9292
return false;
9393
}
9494

9595
private boolean isCurrentAuthenticationUsingSameApiKeyIdFromRequest(Authentication authentication, String apiKeyId) {
96-
if (authentication.getAuthenticatedBy().getType().equals(API_KEY_REALM_TYPE)) {
96+
if (authentication.getSourceRealm().getType().equals(API_KEY_REALM_TYPE)) {
9797
// API key id from authentication must match the id from request
9898
final String authenticatedApiKeyId = (String) authentication.getMetadata().get(API_KEY_ID_KEY);
9999
if (Strings.hasText(apiKeyId)) {

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,46 @@ public void testAuthenticationWithUserDeniesAccessToApiKeyActionsWhenItIsNotOwne
9797
assertFalse(clusterPermission.check("cluster:admin/xpack/security/api_key/invalidate", invalidateApiKeyRequest, authentication));
9898
}
9999

100+
public void testGetAndInvalidateApiKeyWillRespectRunAsUser() {
101+
final ClusterPermission clusterPermission =
102+
ManageOwnApiKeyClusterPrivilege.INSTANCE.buildPermission(ClusterPermission.builder()).build();
103+
104+
final Authentication authentication = createMockRunAsAuthentication(
105+
"user_a", "realm_a", "realm_a_type",
106+
"user_b", "realm_b", "realm_b_type");
107+
108+
assertTrue(clusterPermission.check("cluster:admin/xpack/security/api_key/get",
109+
GetApiKeyRequest.usingRealmAndUserName("realm_b", "user_b"), authentication));
110+
assertTrue(clusterPermission.check("cluster:admin/xpack/security/api_key/invalidate",
111+
InvalidateApiKeyRequest.usingRealmAndUserName("realm_b", "user_b"), authentication));
112+
}
113+
100114
private Authentication createMockAuthentication(String username, String realmName, String realmType, Map<String, Object> metadata) {
101115
final User user = new User(username);
102116
final Authentication authentication = mock(Authentication.class);
103117
final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class);
104118
when(authentication.getUser()).thenReturn(user);
105-
when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
119+
when(authentication.getSourceRealm()).thenReturn(authenticatedBy);
106120
when(authenticatedBy.getName()).thenReturn(realmName);
107121
when(authenticatedBy.getType()).thenReturn(realmType);
108122
when(authentication.getMetadata()).thenReturn(metadata);
109123
return authentication;
110124
}
125+
126+
private Authentication createMockRunAsAuthentication(String username, String realmName, String realmType,
127+
String runAsUsername, String runAsRealmName, String runAsRealmType) {
128+
final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class);
129+
when(authenticatedBy.getName()).thenReturn(realmName);
130+
when(authenticatedBy.getType()).thenReturn(realmType);
131+
final Authentication.RealmRef lookedUpBy = mock(Authentication.RealmRef.class);
132+
when(lookedUpBy.getName()).thenReturn(runAsRealmName);
133+
when(lookedUpBy.getType()).thenReturn(runAsRealmType);
134+
final User user = new User(runAsUsername, new String[0], new User(username));
135+
final Authentication authentication = mock(Authentication.class);
136+
when(authentication.getUser()).thenReturn(user);
137+
when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
138+
when(authentication.getSourceRealm()).thenReturn(lookedUpBy);
139+
when(authentication.getMetadata()).thenReturn(Collections.emptyMap());
140+
return authentication;
141+
}
111142
}

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -892,7 +892,22 @@ public static String getCreatorRealmName(final Authentication authentication) {
892892
if (authentication.getAuthenticatedBy().getType().equals(API_KEY_REALM_TYPE)) {
893893
return (String) authentication.getMetadata().get(API_KEY_CREATOR_REALM_NAME);
894894
} else {
895-
return authentication.getAuthenticatedBy().getName();
895+
return authentication.getSourceRealm().getName();
896+
}
897+
}
898+
899+
/**
900+
* Returns realm type for the authenticated user.
901+
* If the user is authenticated by realm type {@value API_KEY_REALM_TYPE}
902+
* then it will return the realm name of user who created this API key.
903+
* @param authentication {@link Authentication}
904+
* @return realm type
905+
*/
906+
public static String getCreatorRealmType(final Authentication authentication) {
907+
if (authentication.getAuthenticatedBy().getType().equals(API_KEY_REALM_TYPE)) {
908+
return (String) authentication.getMetadata().get(API_KEY_CREATOR_REALM_TYPE);
909+
} else {
910+
return authentication.getSourceRealm().getType();
896911
}
897912
}
898913

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessor.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,17 +114,11 @@ public IngestDocument execute(IngestDocument ingestDocument) throws Exception {
114114
final Map<String, Object> realmField =
115115
existingRealmField instanceof Map ? (Map<String, Object>) existingRealmField : new HashMap<>();
116116

117-
final Object realmName, realmType;
118-
if (Authentication.AuthenticationType.API_KEY == authentication.getAuthenticationType()) {
119-
realmName = authentication.getMetadata().get(ApiKeyService.API_KEY_CREATOR_REALM_NAME);
120-
realmType = authentication.getMetadata().get(ApiKeyService.API_KEY_CREATOR_REALM_TYPE);
121-
} else {
122-
realmName = authentication.getSourceRealm().getName();
123-
realmType = authentication.getSourceRealm().getType();
124-
}
117+
final Object realmName = ApiKeyService.getCreatorRealmName(authentication);
125118
if (realmName != null) {
126119
realmField.put("name", realmName);
127120
}
121+
final Object realmType = ApiKeyService.getCreatorRealmType(authentication);
128122
if (realmType != null) {
129123
realmField.put("type", realmType);
130124
}

0 commit comments

Comments
 (0)