Skip to content

Commit 6e4a695

Browse files
committed
Include role names in access denied errors
This commit adds a User's list of current role names to access denied error messages to aid in diagnostics. This allows an administrator to know whether the correct course of action is to add another role to the user (e.g. by fixing incorrect role mappings) or by modifying a role to add more privileges. Resolves: elastic#42166
1 parent 1256558 commit 6e4a695

File tree

4 files changed

+29
-11
lines changed

4 files changed

+29
-11
lines changed

x-pack/plugin/ilm/qa/with-security/src/javaRestTest/java/org/elasticsearch/xpack/security/PermissionsIT.java

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ public void testCanManageIndexWithNoPermissions() throws Exception {
146146
assertThat(stepInfo.get("type"), equalTo("security_exception"));
147147
assertThat(stepInfo.get("reason"), equalTo("action [indices:monitor/stats] is unauthorized" +
148148
" for user [test_ilm]" +
149+
" with roles [ilm]" +
149150
" on indices [not-ilm]," +
150151
" this action is granted by the index privileges [monitor,manage,all]"));
151152
}

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

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.elasticsearch.cluster.metadata.Metadata;
2929
import org.elasticsearch.cluster.service.ClusterService;
3030
import org.elasticsearch.common.Nullable;
31+
import org.elasticsearch.common.Strings;
3132
import org.elasticsearch.common.collect.Tuple;
3233
import org.elasticsearch.common.settings.Setting;
3334
import org.elasticsearch.common.settings.Setting.Property;
@@ -630,6 +631,9 @@ private ElasticsearchSecurityException denialException(Authentication authentica
630631
final String apiKeyId = (String) authentication.getMetadata().get(ApiKeyService.API_KEY_ID_KEY);
631632
assert apiKeyId != null : "api key id must be present in the metadata";
632633
userText = "API key id [" + apiKeyId + "] of " + userText;
634+
} else {
635+
// Don't print roles for API keys because they're not meaningful
636+
userText = userText + " with roles [" + Strings.arrayToCommaDelimitedString(authentication.getUser().roles()) + "]";
633637
}
634638

635639
String message = "action [" + action + "] is unauthorized for " + userText;

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

+20-9
Original file line numberDiff line numberDiff line change
@@ -677,8 +677,11 @@ public void testUnknownRoleCausesDenial() throws IOException {
677677

678678
ElasticsearchSecurityException securityException = expectThrows(ElasticsearchSecurityException.class,
679679
() -> authorize(authentication, action, request));
680-
assertThat(securityException,
681-
throwableWithMessage(containsString("[" + action + "] is unauthorized for user [test user] on indices [")));
680+
assertThat(securityException, throwableWithMessage(containsString(
681+
"[" + action + "] is unauthorized" +
682+
" for user [test user]" +
683+
" with roles [non-existent-role]" +
684+
" on indices [")));
682685
assertThat(securityException, throwableWithMessage(containsString("this action is granted by the index privileges [read,all]")));
683686

684687
verify(auditTrail).accessDenied(eq(requestId), eq(authentication), eq(action), eq(request), authzInfoRoles(Role.EMPTY.names()));
@@ -716,8 +719,11 @@ public void testThatRoleWithNoIndicesIsDenied() throws IOException {
716719

717720
ElasticsearchSecurityException securityException = expectThrows(ElasticsearchSecurityException.class,
718721
() -> authorize(authentication, action, request));
719-
assertThat(securityException,
720-
throwableWithMessage(containsString("[" + action + "] is unauthorized for user [test user] on indices [")));
722+
assertThat(securityException, throwableWithMessage(containsString(
723+
"[" + action + "] is unauthorized" +
724+
" for user [test user]" +
725+
" with roles [no_indices]" +
726+
" on indices [")));
721727
assertThat(securityException, throwableWithMessage(containsString("this action is granted by the index privileges [read,all]")));
722728

723729
verify(auditTrail).accessDenied(eq(requestId), eq(authentication), eq(action), eq(request),
@@ -888,23 +894,28 @@ public void testCreateIndexWithAlias() throws IOException {
888894
}
889895

890896
public void testDenialErrorMessagesForSearchAction() throws IOException {
891-
RoleDescriptor role = new RoleDescriptor("some_indices_" + randomAlphaOfLengthBetween(3, 6), null, new IndicesPrivileges[]{
897+
RoleDescriptor indexRole = new RoleDescriptor("some_indices_" + randomAlphaOfLengthBetween(3, 6), null, new IndicesPrivileges[]{
892898
IndicesPrivileges.builder().indices("all*").privileges("all").build(),
893899
IndicesPrivileges.builder().indices("read*").privileges("read").build(),
894900
IndicesPrivileges.builder().indices("write*").privileges("write").build()
895901
}, null);
896-
User user = new User(randomAlphaOfLengthBetween(6, 8), role.getName());
902+
RoleDescriptor emptyRole = new RoleDescriptor("empty_role_" + randomAlphaOfLengthBetween(1,4), null, null, null);
903+
User user = new User(randomAlphaOfLengthBetween(6, 8), indexRole.getName(), emptyRole.getName());
897904
final Authentication authentication = createAuthentication(user);
898-
roleMap.put(role.getName(), role);
905+
roleMap.put(indexRole.getName(), indexRole);
906+
roleMap.put(emptyRole.getName(), emptyRole);
899907

900908
AuditUtil.getOrGenerateRequestId(threadContext);
901909

902910
TransportRequest request = new SearchRequest("all-1", "read-2", "write-3", "other-4");
903911

904912
ElasticsearchSecurityException securityException = expectThrows(ElasticsearchSecurityException.class,
905913
() -> authorize(authentication, SearchAction.NAME, request));
906-
assertThat(securityException, throwableWithMessage(
907-
containsString("[" + SearchAction.NAME + "] is unauthorized for user [" + user.principal() + "] on indices [")));
914+
assertThat(securityException, throwableWithMessage(containsString(
915+
"[" + SearchAction.NAME + "] is unauthorized" +
916+
" for user [" + user.principal() + "]" +
917+
" with roles [" + indexRole.getName() + "," + emptyRole.getName() + "]" +
918+
" on indices [")));
908919
assertThat(securityException, throwableWithMessage(containsString("write-3")));
909920
assertThat(securityException, throwableWithMessage(containsString("other-4")));
910921
assertThat(securityException, throwableWithMessage(not(containsString("all-1"))));

x-pack/plugin/src/test/resources/rest-api-spec/test/api_key/11_invalidation.yml

+4-2
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ teardown:
126126
"username": "api_key_manager"
127127
}
128128
- match: { "error.type": "security_exception" }
129-
- match: { "error.reason": "action [cluster:admin/xpack/security/api_key/invalidate] is unauthorized for user [api_key_user_1], this action is granted by the cluster privileges [manage_api_key,manage_security,all]" }
129+
- match:
130+
"error.reason": "action [cluster:admin/xpack/security/api_key/invalidate] is unauthorized for user [api_key_user_1] with roles [user_role], this action is granted by the cluster privileges [manage_api_key,manage_security,all]"
130131

131132
- do:
132133
headers:
@@ -189,7 +190,8 @@ teardown:
189190
"realm_name": "default_native"
190191
}
191192
- match: { "error.type": "security_exception" }
192-
- match: { "error.reason": "action [cluster:admin/xpack/security/api_key/invalidate] is unauthorized for user [api_key_user_1], this action is granted by the cluster privileges [manage_api_key,manage_security,all]" }
193+
- match:
194+
"error.reason": "action [cluster:admin/xpack/security/api_key/invalidate] is unauthorized for user [api_key_user_1] with roles [user_role], this action is granted by the cluster privileges [manage_api_key,manage_security,all]"
193195

194196
- do:
195197
headers:

0 commit comments

Comments
 (0)