瀏覽代碼

Inject authz denial messages (#106358)

This PR moves authorization denied message creation from static methods
to an injectable class.

Relates: ES-7984
Nikolaj Volgushev 1 年之前
父節點
當前提交
4e2f56b9b6

+ 1 - 0
x-pack/plugin/security/src/main/java/module-info.java

@@ -65,6 +65,7 @@ module org.elasticsearch.security {
     exports org.elasticsearch.xpack.security.action.user to org.elasticsearch.server;
     exports org.elasticsearch.xpack.security.action.settings to org.elasticsearch.server;
     exports org.elasticsearch.xpack.security.operator to org.elasticsearch.internal.operator, org.elasticsearch.internal.security;
+    exports org.elasticsearch.xpack.security.authz to org.elasticsearch.internal.security;
     exports org.elasticsearch.xpack.security.authc to org.elasticsearch.xcontent;
     exports org.elasticsearch.xpack.security.slowlog to org.elasticsearch.server;
 

+ 8 - 1
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java

@@ -290,6 +290,7 @@ import org.elasticsearch.xpack.security.authc.service.IndexServiceAccountTokenSt
 import org.elasticsearch.xpack.security.authc.service.ServiceAccountService;
 import org.elasticsearch.xpack.security.authc.support.SecondaryAuthenticator;
 import org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore;
+import org.elasticsearch.xpack.security.authz.AuthorizationDenialMessages;
 import org.elasticsearch.xpack.security.authz.AuthorizationService;
 import org.elasticsearch.xpack.security.authz.DlsFlsRequestCacheDifferentiator;
 import org.elasticsearch.xpack.security.authz.SecuritySearchOperationListener;
@@ -581,6 +582,7 @@ public class Security extends Plugin
     private final SetOnce<Realms> realms = new SetOnce<>();
     private final SetOnce<Client> client = new SetOnce<>();
     private final SetOnce<List<ReloadableSecurityComponent>> reloadableComponents = new SetOnce<>();
+    private final SetOnce<AuthorizationDenialMessages> authorizationDenialMessages = new SetOnce<>();
 
     public Security(Settings settings) {
         this(settings, Collections.emptyList());
@@ -1007,6 +1009,9 @@ public class Security extends Plugin
         }
         requestInterceptors = Collections.unmodifiableSet(requestInterceptors);
 
+        if (authorizationDenialMessages.get() == null) {
+            authorizationDenialMessages.set(new AuthorizationDenialMessages.Default());
+        }
         final AuthorizationService authzService = new AuthorizationService(
             settings,
             allRolesStore,
@@ -1021,7 +1026,8 @@ public class Security extends Plugin
             getLicenseState(),
             expressionResolver,
             operatorPrivilegesService.get(),
-            restrictedIndices
+            restrictedIndices,
+            authorizationDenialMessages.get()
         );
 
         components.add(nativeRolesStore); // used by roles actions
@@ -2098,6 +2104,7 @@ public class Security extends Plugin
         loadSingletonExtensionAndSetOnce(loader, bulkUpdateApiKeyRequestTranslator, BulkUpdateApiKeyRequestTranslator.class);
         loadSingletonExtensionAndSetOnce(loader, createApiKeyRequestBuilderFactory, CreateApiKeyRequestBuilderFactory.class);
         loadSingletonExtensionAndSetOnce(loader, hasPrivilegesRequestBuilderFactory, HasPrivilegesRequestBuilderFactory.class);
+        loadSingletonExtensionAndSetOnce(loader, authorizationDenialMessages, AuthorizationDenialMessages.class);
     }
 
     private <T> void loadSingletonExtensionAndSetOnce(ExtensionLoader loader, SetOnce<T> setOnce, Class<T> clazz) {

+ 169 - 125
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationDenialMessages.java

@@ -14,7 +14,7 @@ import org.elasticsearch.transport.TransportRequest;
 import org.elasticsearch.xpack.core.security.authc.Authentication;
 import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
 import org.elasticsearch.xpack.core.security.authc.Subject;
-import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.AuthorizationInfo;
+import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine;
 import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
 import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
 
@@ -29,158 +29,202 @@ import static org.elasticsearch.common.Strings.collectionToCommaDelimitedString;
 import static org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrail.PRINCIPAL_ROLES_FIELD_NAME;
 import static org.elasticsearch.xpack.security.authz.AuthorizationService.isIndexAction;
 
-class AuthorizationDenialMessages {
-
-    private AuthorizationDenialMessages() {}
-
-    static String runAsDenied(Authentication authentication, @Nullable AuthorizationInfo authorizationInfo, String action) {
-        assert authentication.isRunAs() : "constructing run as denied message but authentication for action was not run as";
-
-        String userText = authenticatedUserDescription(authentication);
-        String actionIsUnauthorizedMessage = actionIsUnauthorizedMessage(action, userText);
-
-        String unauthorizedToRunAsMessage = "because "
-            + userText
-            + " is unauthorized to run as ["
-            + authentication.getEffectiveSubject().getUser().principal()
-            + "]";
-
-        return actionIsUnauthorizedMessage
-            + rolesDescription(authentication.getAuthenticatingSubject(), authorizationInfo.getAuthenticatedUserAuthorizationInfo())
-            + ", "
-            + unauthorizedToRunAsMessage;
-    }
-
-    static String actionDenied(
+public interface AuthorizationDenialMessages {
+    String actionDenied(
         Authentication authentication,
-        @Nullable AuthorizationInfo authorizationInfo,
+        @Nullable AuthorizationEngine.AuthorizationInfo authorizationInfo,
         String action,
         TransportRequest request,
         @Nullable String context
-    ) {
-        String userText = successfulAuthenticationDescription(authentication, authorizationInfo);
-        String remoteClusterText = authentication.isCrossClusterAccess() ? remoteClusterText(null) : "";
-        String message = actionIsUnauthorizedMessage(action, remoteClusterText, userText);
-        if (context != null) {
-            message = message + " " + context;
+    );
+
+    String runAsDenied(Authentication authentication, @Nullable AuthorizationEngine.AuthorizationInfo authorizationInfo, String action);
+
+    String remoteActionDenied(
+        Authentication authentication,
+        @Nullable AuthorizationEngine.AuthorizationInfo authorizationInfo,
+        String action,
+        String clusterAlias
+    );
+
+    class Default implements AuthorizationDenialMessages {
+        public Default() {}
+
+        @Override
+        public String runAsDenied(
+            Authentication authentication,
+            @Nullable AuthorizationEngine.AuthorizationInfo authorizationInfo,
+            String action
+        ) {
+            assert authentication.isRunAs() : "constructing run as denied message but authentication for action was not run as";
+
+            String userText = authenticatedUserDescription(authentication);
+            String actionIsUnauthorizedMessage = actionIsUnauthorizedMessage(action, userText);
+
+            String unauthorizedToRunAsMessage = "because "
+                + userText
+                + " is unauthorized to run as ["
+                + authentication.getEffectiveSubject().getUser().principal()
+                + "]";
+
+            return actionIsUnauthorizedMessage
+                + rolesDescription(authentication.getAuthenticatingSubject(), authorizationInfo.getAuthenticatedUserAuthorizationInfo())
+                + ", "
+                + unauthorizedToRunAsMessage;
         }
 
-        if (ClusterPrivilegeResolver.isClusterAction(action)) {
-            final Collection<String> privileges = ClusterPrivilegeResolver.findPrivilegesThatGrant(action, request, authentication);
-            if (privileges != null && privileges.size() > 0) {
-                message = message
-                    + ", this action is granted by the cluster privileges ["
-                    + collectionToCommaDelimitedString(privileges)
-                    + "]";
+        @Override
+        public String actionDenied(
+            Authentication authentication,
+            @Nullable AuthorizationEngine.AuthorizationInfo authorizationInfo,
+            String action,
+            TransportRequest request,
+            @Nullable String context
+        ) {
+            String userText = successfulAuthenticationDescription(authentication, authorizationInfo);
+            String remoteClusterText = authentication.isCrossClusterAccess() ? remoteClusterText(null) : "";
+            String message = actionIsUnauthorizedMessage(action, remoteClusterText, userText);
+            if (context != null) {
+                message = message + " " + context;
             }
-        } else if (isIndexAction(action)) {
-            final Collection<String> privileges = IndexPrivilege.findPrivilegesThatGrant(action);
-            if (privileges != null && privileges.size() > 0) {
-                message = message
-                    + ", this action is granted by the index privileges ["
-                    + collectionToCommaDelimitedString(privileges)
-                    + "]";
+
+            if (ClusterPrivilegeResolver.isClusterAction(action)) {
+                final Collection<String> privileges = findClusterPrivilegesThatGrant(authentication, action, request);
+                if (privileges != null && privileges.size() > 0) {
+                    message = message
+                        + ", this action is granted by the cluster privileges ["
+                        + collectionToCommaDelimitedString(privileges)
+                        + "]";
+                }
+            } else if (isIndexAction(action)) {
+                final Collection<String> privileges = findIndexPrivilegesThatGrant(action);
+                if (privileges != null && privileges.size() > 0) {
+                    message = message
+                        + ", this action is granted by the index privileges ["
+                        + collectionToCommaDelimitedString(privileges)
+                        + "]";
+                }
             }
+
+            return message;
         }
 
-        return message;
-    }
+        @Override
+        public String remoteActionDenied(
+            Authentication authentication,
+            @Nullable AuthorizationEngine.AuthorizationInfo authorizationInfo,
+            String action,
+            String clusterAlias
+        ) {
+            assert isIndexAction(action);
+            String userText = successfulAuthenticationDescription(authentication, authorizationInfo);
+            String remoteClusterText = remoteClusterText(clusterAlias);
+            return actionIsUnauthorizedMessage(action, remoteClusterText, userText)
+                + " because no remote indices privileges apply for the target cluster";
+        }
 
-    static String remoteActionDenied(
-        Authentication authentication,
-        @Nullable AuthorizationInfo authorizationInfo,
-        String action,
-        String clusterAlias
-    ) {
-        assert isIndexAction(action);
-        String userText = successfulAuthenticationDescription(authentication, authorizationInfo);
-        String remoteClusterText = remoteClusterText(clusterAlias);
-        return actionIsUnauthorizedMessage(action, remoteClusterText, userText)
-            + " because no remote indices privileges apply for the target cluster";
-    }
+        protected Collection<String> findClusterPrivilegesThatGrant(
+            Authentication authentication,
+            String action,
+            TransportRequest request
+        ) {
+            return ClusterPrivilegeResolver.findPrivilegesThatGrant(action, request, authentication);
+        }
 
-    private static String remoteClusterText(@Nullable String clusterAlias) {
-        return Strings.format("towards remote cluster%s ", clusterAlias == null ? "" : " [" + clusterAlias + "]");
-    }
+        protected Collection<String> findIndexPrivilegesThatGrant(String action) {
+            return IndexPrivilege.findPrivilegesThatGrant(action);
+        }
+
+        private String remoteClusterText(@Nullable String clusterAlias) {
+            return Strings.format("towards remote cluster%s ", clusterAlias == null ? "" : " [" + clusterAlias + "]");
+        }
 
-    private static String authenticatedUserDescription(Authentication authentication) {
-        String userText = (authentication.isServiceAccount() ? "service account" : "user")
-            + " ["
-            + authentication.getAuthenticatingSubject().getUser().principal()
-            + "]";
-        if (authentication.isAuthenticatedAsApiKey() || authentication.isCrossClusterAccess()) {
-            final String apiKeyId = (String) authentication.getAuthenticatingSubject()
-                .getMetadata()
-                .get(AuthenticationField.API_KEY_ID_KEY);
-            assert apiKeyId != null : "api key id must be present in the metadata";
-            userText = "API key id [" + apiKeyId + "] of " + userText;
-            if (authentication.isCrossClusterAccess()) {
-                final Authentication crossClusterAccessAuthentication = (Authentication) authentication.getAuthenticatingSubject()
+        private String authenticatedUserDescription(Authentication authentication) {
+            String userText = (authentication.isServiceAccount() ? "service account" : "user")
+                + " ["
+                + authentication.getAuthenticatingSubject().getUser().principal()
+                + "]";
+            if (authentication.isAuthenticatedAsApiKey() || authentication.isCrossClusterAccess()) {
+                final String apiKeyId = (String) authentication.getAuthenticatingSubject()
                     .getMetadata()
-                    .get(AuthenticationField.CROSS_CLUSTER_ACCESS_AUTHENTICATION_KEY);
-                assert crossClusterAccessAuthentication != null : "cross cluster access authentication must be present in the metadata";
-                userText = successfulAuthenticationDescription(crossClusterAccessAuthentication, null) + " authenticated by " + userText;
+                    .get(AuthenticationField.API_KEY_ID_KEY);
+                assert apiKeyId != null : "api key id must be present in the metadata";
+                userText = "API key id [" + apiKeyId + "] of " + userText;
+                if (authentication.isCrossClusterAccess()) {
+                    final Authentication crossClusterAccessAuthentication = (Authentication) authentication.getAuthenticatingSubject()
+                        .getMetadata()
+                        .get(AuthenticationField.CROSS_CLUSTER_ACCESS_AUTHENTICATION_KEY);
+                    assert crossClusterAccessAuthentication != null : "cross cluster access authentication must be present in the metadata";
+                    userText = successfulAuthenticationDescription(crossClusterAccessAuthentication, null)
+                        + " authenticated by "
+                        + userText;
+                }
             }
+            return userText;
         }
-        return userText;
-    }
 
-    static String rolesDescription(Subject subject, @Nullable AuthorizationInfo authorizationInfo) {
-        // We cannot print the roles if it's an API key or a service account (both do not have roles, but privileges)
-        if (subject.getType() != Subject.Type.USER) {
-            return "";
-        }
+        // package-private for tests
+        String rolesDescription(Subject subject, @Nullable AuthorizationEngine.AuthorizationInfo authorizationInfo) {
+            // We cannot print the roles if it's an API key or a service account (both do not have roles, but privileges)
+            if (subject.getType() != Subject.Type.USER) {
+                return "";
+            }
 
-        final StringBuilder sb = new StringBuilder();
-        final List<String> effectiveRoleNames = extractEffectiveRoleNames(authorizationInfo);
-        if (effectiveRoleNames == null) {
-            sb.append(" with assigned roles [").append(Strings.arrayToCommaDelimitedString(subject.getUser().roles())).append("]");
-        } else {
-            sb.append(" with effective roles [").append(Strings.collectionToCommaDelimitedString(effectiveRoleNames)).append("]");
-
-            final Set<String> assignedRoleNames = Set.of(subject.getUser().roles());
-            final SortedSet<String> unfoundedRoleNames = Sets.sortedDifference(assignedRoleNames, Set.copyOf(effectiveRoleNames));
-            if (false == unfoundedRoleNames.isEmpty()) {
-                sb.append(" (assigned roles [")
-                    .append(Strings.collectionToCommaDelimitedString(unfoundedRoleNames))
-                    .append("] were not found)");
+            final StringBuilder sb = new StringBuilder();
+            final List<String> effectiveRoleNames = extractEffectiveRoleNames(authorizationInfo);
+            if (effectiveRoleNames == null) {
+                sb.append(" with assigned roles [").append(Strings.arrayToCommaDelimitedString(subject.getUser().roles())).append("]");
+            } else {
+                sb.append(" with effective roles [").append(Strings.collectionToCommaDelimitedString(effectiveRoleNames)).append("]");
+
+                final Set<String> assignedRoleNames = Set.of(subject.getUser().roles());
+                final SortedSet<String> unfoundedRoleNames = Sets.sortedDifference(assignedRoleNames, Set.copyOf(effectiveRoleNames));
+                if (false == unfoundedRoleNames.isEmpty()) {
+                    sb.append(" (assigned roles [")
+                        .append(Strings.collectionToCommaDelimitedString(unfoundedRoleNames))
+                        .append("] were not found)");
+                }
             }
+            return sb.toString();
         }
-        return sb.toString();
-    }
 
-    static String successfulAuthenticationDescription(Authentication authentication, @Nullable AuthorizationInfo authorizationInfo) {
-        String userText = authenticatedUserDescription(authentication);
+        // package-private for tests
+        String successfulAuthenticationDescription(
+            Authentication authentication,
+            @Nullable AuthorizationEngine.AuthorizationInfo authorizationInfo
+        ) {
+            String userText = authenticatedUserDescription(authentication);
 
-        if (authentication.isRunAs()) {
-            userText = userText + " run as [" + authentication.getEffectiveSubject().getUser().principal() + "]";
+            if (authentication.isRunAs()) {
+                userText = userText + " run as [" + authentication.getEffectiveSubject().getUser().principal() + "]";
+            }
+
+            userText += rolesDescription(authentication.getEffectiveSubject(), authorizationInfo);
+            return userText;
         }
 
-        userText += rolesDescription(authentication.getEffectiveSubject(), authorizationInfo);
-        return userText;
-    }
+        private List<String> extractEffectiveRoleNames(@Nullable AuthorizationEngine.AuthorizationInfo authorizationInfo) {
+            if (authorizationInfo == null) {
+                return null;
+            }
 
-    private static List<String> extractEffectiveRoleNames(@Nullable AuthorizationInfo authorizationInfo) {
-        if (authorizationInfo == null) {
-            return null;
+            final Map<String, Object> info = authorizationInfo.asMap();
+            final Object roleNames = info.get(PRINCIPAL_ROLES_FIELD_NAME);
+            // AuthorizationInfo from custom authorization engine may not have this field or have it as a different data type
+            if (false == roleNames instanceof String[]) {
+                assert false == authorizationInfo instanceof RBACEngine.RBACAuthorizationInfo
+                    : "unexpected user.roles field [" + roleNames + "] for RBACAuthorizationInfo";
+                return null;
+            }
+            return Arrays.stream((String[]) roleNames).sorted().toList();
         }
 
-        final Map<String, Object> info = authorizationInfo.asMap();
-        final Object roleNames = info.get(PRINCIPAL_ROLES_FIELD_NAME);
-        // AuthorizationInfo from custom authorization engine may not have this field or have it as a different data type
-        if (false == roleNames instanceof String[]) {
-            assert false == authorizationInfo instanceof RBACEngine.RBACAuthorizationInfo
-                : "unexpected user.roles field [" + roleNames + "] for RBACAuthorizationInfo";
-            return null;
+        private String actionIsUnauthorizedMessage(String action, String userText) {
+            return actionIsUnauthorizedMessage(action, "", userText);
         }
-        return Arrays.stream((String[]) roleNames).sorted().toList();
-    }
-
-    private static String actionIsUnauthorizedMessage(String action, String userText) {
-        return actionIsUnauthorizedMessage(action, "", userText);
-    }
 
-    private static String actionIsUnauthorizedMessage(String action, String remoteClusterText, String userText) {
-        return "action [" + action + "] " + remoteClusterText + "is unauthorized for " + userText;
+        private String actionIsUnauthorizedMessage(String action, String remoteClusterText, String userText) {
+            return "action [" + action + "] " + remoteClusterText + "is unauthorized for " + userText;
+        }
     }
 }

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

@@ -139,6 +139,7 @@ public class AuthorizationService {
 
     private final boolean isAnonymousEnabled;
     private final boolean anonymousAuthzExceptionEnabled;
+    private final AuthorizationDenialMessages authorizationDenialMessages;
 
     public AuthorizationService(
         Settings settings,
@@ -154,7 +155,8 @@ public class AuthorizationService {
         XPackLicenseState licenseState,
         IndexNameExpressionResolver resolver,
         OperatorPrivilegesService operatorPrivilegesService,
-        RestrictedIndices restrictedIndices
+        RestrictedIndices restrictedIndices,
+        AuthorizationDenialMessages authorizationDenialMessages
     ) {
         this.clusterService = clusterService;
         this.auditTrailService = auditTrailService;
@@ -178,6 +180,7 @@ public class AuthorizationService {
         this.licenseState = licenseState;
         this.operatorPrivilegesService = operatorPrivilegesService;
         this.indicesAccessControlWrapper = new DlsFlsFeatureTrackingIndicesAccessControlWrapper(settings, licenseState);
+        this.authorizationDenialMessages = authorizationDenialMessages;
     }
 
     public void checkPrivileges(
@@ -922,7 +925,7 @@ public class AuthorizationService {
         return denialException(
             authentication,
             action,
-            () -> AuthorizationDenialMessages.runAsDenied(authentication, authorizationInfo, action),
+            () -> authorizationDenialMessages.runAsDenied(authentication, authorizationInfo, action),
             null
         );
     }
@@ -932,7 +935,7 @@ public class AuthorizationService {
         return denialException(
             authentication,
             action,
-            () -> AuthorizationDenialMessages.remoteActionDenied(authentication, authorizationInfo, action, clusterAlias),
+            () -> authorizationDenialMessages.remoteActionDenied(authentication, authorizationInfo, action, clusterAlias),
             null
         );
     }
@@ -967,7 +970,7 @@ public class AuthorizationService {
         return denialException(
             authentication,
             action,
-            () -> AuthorizationDenialMessages.actionDenied(authentication, authorizationInfo, action, request, context),
+            () -> authorizationDenialMessages.actionDenied(authentication, authorizationInfo, action, request, context),
             cause
         );
     }

+ 19 - 20
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationDenialMessagesTests.java

@@ -29,16 +29,15 @@ import static org.mockito.Mockito.when;
 
 public class AuthorizationDenialMessagesTests extends ESTestCase {
 
+    private final AuthorizationDenialMessages.Default denialMessages = new AuthorizationDenialMessages.Default();
+
     public void testNoRolesDescriptionIfSubjectIsNotAUser() {
         final Authentication authentication = randomFrom(
             AuthenticationTestHelper.builder().apiKey().build(),
             AuthenticationTestHelper.builder().serviceAccount().build()
         );
 
-        assertThat(
-            AuthorizationDenialMessages.rolesDescription(authentication.getEffectiveSubject(), mock(AuthorizationInfo.class)),
-            equalTo("")
-        );
+        assertThat(denialMessages.rolesDescription(authentication.getEffectiveSubject(), mock(AuthorizationInfo.class)), equalTo(""));
     }
 
     public void testRolesDescriptionWithNullAuthorizationInfo() {
@@ -51,7 +50,7 @@ public class AuthorizationDenialMessagesTests extends ESTestCase {
             .user(new User(randomAlphaOfLengthBetween(3, 8), assignedRoleNames.toArray(String[]::new)))
             .build(false)
             .getEffectiveSubject();
-        final String rolesDescription = AuthorizationDenialMessages.rolesDescription(subject, null);
+        final String rolesDescription = denialMessages.rolesDescription(subject, null);
 
         assertThat(
             rolesDescription,
@@ -71,7 +70,7 @@ public class AuthorizationDenialMessagesTests extends ESTestCase {
             .getEffectiveSubject();
         final AuthorizationInfo authorizationInfo = mock(AuthorizationInfo.class);
         when(authorizationInfo.asMap()).thenReturn(Map.of());
-        final String rolesDescription = AuthorizationDenialMessages.rolesDescription(subject, authorizationInfo);
+        final String rolesDescription = denialMessages.rolesDescription(subject, authorizationInfo);
 
         assertThat(
             rolesDescription,
@@ -102,7 +101,7 @@ public class AuthorizationDenialMessagesTests extends ESTestCase {
                 )
             )
         );
-        final String rolesDescription = AuthorizationDenialMessages.rolesDescription(subject, authorizationInfo);
+        final String rolesDescription = denialMessages.rolesDescription(subject, authorizationInfo);
 
         assertThat(
             rolesDescription,
@@ -123,7 +122,7 @@ public class AuthorizationDenialMessagesTests extends ESTestCase {
 
         final AuthorizationInfo authorizationInfo = mock(AuthorizationInfo.class);
         when(authorizationInfo.asMap()).thenReturn(Map.of("user.roles", Strings.EMPTY_ARRAY));
-        final String rolesDescription = AuthorizationDenialMessages.rolesDescription(subject, authorizationInfo);
+        final String rolesDescription = denialMessages.rolesDescription(subject, authorizationInfo);
 
         if (assignedRoleNames.isEmpty()) {
             assertThat(rolesDescription, equalTo(" with effective roles []"));
@@ -160,7 +159,7 @@ public class AuthorizationDenialMessagesTests extends ESTestCase {
         final AuthorizationInfo authorizationInfo = mock(AuthorizationInfo.class);
         when(authorizationInfo.asMap()).thenReturn(Map.of("user.roles", effectiveRoleNames.toArray(String[]::new)));
 
-        final String rolesDescription = AuthorizationDenialMessages.rolesDescription(subject, authorizationInfo);
+        final String rolesDescription = denialMessages.rolesDescription(subject, authorizationInfo);
 
         assertThat(
             rolesDescription,
@@ -195,7 +194,7 @@ public class AuthorizationDenialMessagesTests extends ESTestCase {
         final AuthorizationInfo authorizationInfo = mock(AuthorizationInfo.class);
         when(authorizationInfo.asMap()).thenReturn(Map.of("user.roles", effectiveRoleNames.toArray(String[]::new)));
 
-        final String rolesDescription = AuthorizationDenialMessages.rolesDescription(subject, authorizationInfo);
+        final String rolesDescription = denialMessages.rolesDescription(subject, authorizationInfo);
 
         assertThat(
             rolesDescription,
@@ -220,13 +219,13 @@ public class AuthorizationDenialMessagesTests extends ESTestCase {
             .get(AuthenticationField.CROSS_CLUSTER_ACCESS_AUTHENTICATION_KEY);
         final String action = "indices:/some/action/" + randomAlphaOfLengthBetween(0, 8);
         assertThat(
-            AuthorizationDenialMessages.actionDenied(authentication, null, action, mock(), null),
+            denialMessages.actionDenied(authentication, null, action, mock(), null),
             equalTo(
                 Strings.format(
                     "action [%s] towards remote cluster is unauthorized for %s authenticated by API key id [%s] of user [%s], "
                         + "this action is granted by the index privileges [all]",
                     action,
-                    AuthorizationDenialMessages.successfulAuthenticationDescription(innerAuthentication, null),
+                    denialMessages.successfulAuthenticationDescription(innerAuthentication, null),
                     authentication.getAuthenticatingSubject().getMetadata().get(AuthenticationField.API_KEY_ID_KEY),
                     authentication.getEffectiveSubject().getUser().principal()
                 )
@@ -237,7 +236,7 @@ public class AuthorizationDenialMessagesTests extends ESTestCase {
     public void testSuccessfulAuthenticationDescription() {
         final Authentication authentication1 = AuthenticationTestHelper.builder().realm().build(false);
         assertThat(
-            AuthorizationDenialMessages.successfulAuthenticationDescription(authentication1, null),
+            denialMessages.successfulAuthenticationDescription(authentication1, null),
             equalTo(
                 Strings.format(
                     "user [%s] with assigned roles [%s]",
@@ -249,7 +248,7 @@ public class AuthorizationDenialMessagesTests extends ESTestCase {
 
         final Authentication authentication2 = AuthenticationTestHelper.builder().realm().runAs().build();
         assertThat(
-            AuthorizationDenialMessages.successfulAuthenticationDescription(authentication2, null),
+            denialMessages.successfulAuthenticationDescription(authentication2, null),
             equalTo(
                 Strings.format(
                     "user [%s] run as [%s] with assigned roles [%s]",
@@ -262,7 +261,7 @@ public class AuthorizationDenialMessagesTests extends ESTestCase {
 
         final Authentication authentication3 = AuthenticationTestHelper.builder().apiKey().build();
         assertThat(
-            AuthorizationDenialMessages.successfulAuthenticationDescription(authentication3, null),
+            denialMessages.successfulAuthenticationDescription(authentication3, null),
             equalTo(
                 Strings.format(
                     "API key id [%s] of user [%s]",
@@ -274,7 +273,7 @@ public class AuthorizationDenialMessagesTests extends ESTestCase {
 
         final Authentication authentication4 = AuthenticationTestHelper.builder().serviceAccount().build();
         assertThat(
-            AuthorizationDenialMessages.successfulAuthenticationDescription(authentication4, null),
+            denialMessages.successfulAuthenticationDescription(authentication4, null),
             equalTo(Strings.format("service account [%s]", authentication4.getEffectiveSubject().getUser().principal()))
         );
 
@@ -286,11 +285,11 @@ public class AuthorizationDenialMessagesTests extends ESTestCase {
             .getMetadata()
             .get(AuthenticationField.CROSS_CLUSTER_ACCESS_AUTHENTICATION_KEY);
         assertThat(
-            AuthorizationDenialMessages.successfulAuthenticationDescription(authentication5, null),
+            denialMessages.successfulAuthenticationDescription(authentication5, null),
             equalTo(
                 Strings.format(
                     "%s authenticated by API key id [%s] of user [%s]",
-                    AuthorizationDenialMessages.successfulAuthenticationDescription(innerAuthentication, null),
+                    denialMessages.successfulAuthenticationDescription(innerAuthentication, null),
                     authentication5.getAuthenticatingSubject().getMetadata().get(AuthenticationField.API_KEY_ID_KEY),
                     authentication5.getEffectiveSubject().getUser().principal()
                 )
@@ -303,14 +302,14 @@ public class AuthorizationDenialMessagesTests extends ESTestCase {
         final String action = "indices:/some/action/" + randomAlphaOfLengthBetween(0, 8);
         final String clusterAlias = randomAlphaOfLengthBetween(5, 12);
         assertThat(
-            AuthorizationDenialMessages.remoteActionDenied(authentication, null, action, clusterAlias),
+            denialMessages.remoteActionDenied(authentication, null, action, clusterAlias),
             equalTo(
                 Strings.format(
                     "action [%s] towards remote cluster [%s] is unauthorized for %s"
                         + " because no remote indices privileges apply for the target cluster",
                     action,
                     clusterAlias,
-                    AuthorizationDenialMessages.successfulAuthenticationDescription(authentication, null)
+                    denialMessages.successfulAuthenticationDescription(authentication, null)
                 )
             )
         );

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

@@ -321,7 +321,8 @@ public class AuthorizationServiceTests extends ESTestCase {
             licenseState,
             TestIndexNameExpressionResolver.newInstance(),
             operatorPrivilegesService,
-            RESTRICTED_INDICES
+            RESTRICTED_INDICES,
+            new AuthorizationDenialMessages.Default()
         );
     }
 
@@ -1743,7 +1744,8 @@ public class AuthorizationServiceTests extends ESTestCase {
             new XPackLicenseState(() -> 0),
             TestIndexNameExpressionResolver.newInstance(),
             operatorPrivilegesService,
-            RESTRICTED_INDICES
+            RESTRICTED_INDICES,
+            new AuthorizationDenialMessages.Default()
         );
 
         RoleDescriptor role = new RoleDescriptor(
@@ -1791,7 +1793,8 @@ public class AuthorizationServiceTests extends ESTestCase {
             new XPackLicenseState(() -> 0),
             TestIndexNameExpressionResolver.newInstance(),
             operatorPrivilegesService,
-            RESTRICTED_INDICES
+            RESTRICTED_INDICES,
+            new AuthorizationDenialMessages.Default()
         );
 
         RoleDescriptor role = new RoleDescriptor(
@@ -3307,7 +3310,8 @@ public class AuthorizationServiceTests extends ESTestCase {
             licenseState,
             TestIndexNameExpressionResolver.newInstance(),
             operatorPrivilegesService,
-            RESTRICTED_INDICES
+            RESTRICTED_INDICES,
+            new AuthorizationDenialMessages.Default()
         );
 
         Subject subject = new Subject(new User("test", "a role"), mock(RealmRef.class));
@@ -3462,7 +3466,8 @@ public class AuthorizationServiceTests extends ESTestCase {
             licenseState,
             TestIndexNameExpressionResolver.newInstance(),
             operatorPrivilegesService,
-            RESTRICTED_INDICES
+            RESTRICTED_INDICES,
+            new AuthorizationDenialMessages.Default()
         );
         Authentication authentication;
         try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
@@ -3561,7 +3566,7 @@ public class AuthorizationServiceTests extends ESTestCase {
                         + " because no remote indices privileges apply for the target cluster",
                     action,
                     clusterAlias,
-                    AuthorizationDenialMessages.successfulAuthenticationDescription(authentication, authorizationInfo)
+                    new AuthorizationDenialMessages.Default().successfulAuthenticationDescription(authentication, authorizationInfo)
                 )
             )
         );
@@ -3583,7 +3588,7 @@ public class AuthorizationServiceTests extends ESTestCase {
                 Strings.format(
                     "action [%s] towards remote cluster is unauthorized for %s",
                     action,
-                    AuthorizationDenialMessages.successfulAuthenticationDescription(authentication, authorizationInfo)
+                    new AuthorizationDenialMessages.Default().successfulAuthenticationDescription(authentication, authorizationInfo)
                 )
             )
         );