|  | @@ -59,7 +59,10 @@ import static java.util.zip.ZipFile.OPEN_DELETE;
 | 
	
		
			
				|  |  |  import static java.util.zip.ZipFile.OPEN_READ;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  public class PolicyManager {
 | 
	
		
			
				|  |  | -    private static final Logger logger = LogManager.getLogger(PolicyManager.class);
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * Use this if you don't have a {@link ModuleEntitlements} in hand.
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private static final Logger generalLogger = LogManager.getLogger(PolicyManager.class);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      static final String UNKNOWN_COMPONENT_NAME = "(unknown)";
 | 
	
		
			
				|  |  |      static final String SERVER_COMPONENT_NAME = "(server)";
 | 
	
	
		
			
				|  | @@ -76,7 +79,8 @@ public class PolicyManager {
 | 
	
		
			
				|  |  |      record ModuleEntitlements(
 | 
	
		
			
				|  |  |          String componentName,
 | 
	
		
			
				|  |  |          Map<Class<? extends Entitlement>, List<Entitlement>> entitlementsByType,
 | 
	
		
			
				|  |  | -        FileAccessTree fileAccess
 | 
	
		
			
				|  |  | +        FileAccessTree fileAccess,
 | 
	
		
			
				|  |  | +        Logger logger
 | 
	
		
			
				|  |  |      ) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          ModuleEntitlements {
 | 
	
	
		
			
				|  | @@ -101,8 +105,13 @@ public class PolicyManager {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      // pkg private for testing
 | 
	
		
			
				|  |  | -    ModuleEntitlements defaultEntitlements(String componentName, Path componentPath) {
 | 
	
		
			
				|  |  | -        return new ModuleEntitlements(componentName, Map.of(), getDefaultFileAccess(componentName, componentPath));
 | 
	
		
			
				|  |  | +    ModuleEntitlements defaultEntitlements(String componentName, Path componentPath, String moduleName) {
 | 
	
		
			
				|  |  | +        return new ModuleEntitlements(
 | 
	
		
			
				|  |  | +            componentName,
 | 
	
		
			
				|  |  | +            Map.of(),
 | 
	
		
			
				|  |  | +            getDefaultFileAccess(componentName, componentPath),
 | 
	
		
			
				|  |  | +            getLogger(componentName, moduleName)
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      // pkg private for testing
 | 
	
	
		
			
				|  | @@ -116,7 +125,8 @@ public class PolicyManager {
 | 
	
		
			
				|  |  |          return new ModuleEntitlements(
 | 
	
		
			
				|  |  |              componentName,
 | 
	
		
			
				|  |  |              entitlements.stream().collect(groupingBy(Entitlement::getClass)),
 | 
	
		
			
				|  |  | -            FileAccessTree.of(componentName, moduleName, filesEntitlement, pathLookup, componentPath, exclusivePaths)
 | 
	
		
			
				|  |  | +            FileAccessTree.of(componentName, moduleName, filesEntitlement, pathLookup, componentPath, exclusivePaths),
 | 
	
		
			
				|  |  | +            getLogger(componentName, moduleName)
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -255,17 +265,17 @@ public class PolicyManager {
 | 
	
		
			
				|  |  |              return;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        String componentName = getEntitlements(requestingClass).componentName();
 | 
	
		
			
				|  |  | +        ModuleEntitlements entitlements = getEntitlements(requestingClass);
 | 
	
		
			
				|  |  |          notEntitled(
 | 
	
		
			
				|  |  |              Strings.format(
 | 
	
		
			
				|  |  |                  "component [%s], module [%s], class [%s], operation [%s]",
 | 
	
		
			
				|  |  | -                componentName,
 | 
	
		
			
				|  |  | +                entitlements.componentName(),
 | 
	
		
			
				|  |  |                  requestingClass.getModule().getName(),
 | 
	
		
			
				|  |  |                  requestingClass,
 | 
	
		
			
				|  |  |                  operationDescription.get()
 | 
	
		
			
				|  |  |              ),
 | 
	
		
			
				|  |  |              callerClass,
 | 
	
		
			
				|  |  | -            componentName
 | 
	
		
			
				|  |  | +            entitlements
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -323,7 +333,7 @@ public class PolicyManager {
 | 
	
		
			
				|  |  |      private static boolean isPathOnDefaultFilesystem(Path path) {
 | 
	
		
			
				|  |  |          var pathFileSystemClass = path.getFileSystem().getClass();
 | 
	
		
			
				|  |  |          if (path.getFileSystem().getClass() != DEFAULT_FILESYSTEM_CLASS) {
 | 
	
		
			
				|  |  | -            logger.trace(
 | 
	
		
			
				|  |  | +            generalLogger.trace(
 | 
	
		
			
				|  |  |                  () -> Strings.format(
 | 
	
		
			
				|  |  |                      "File entitlement trivially allowed: path [%s] is for a different FileSystem class [%s], default is [%s]",
 | 
	
		
			
				|  |  |                      path.toString(),
 | 
	
	
		
			
				|  | @@ -383,7 +393,7 @@ public class PolicyManager {
 | 
	
		
			
				|  |  |                      realPath == null ? path : Strings.format("%s -> %s", path, realPath)
 | 
	
		
			
				|  |  |                  ),
 | 
	
		
			
				|  |  |                  callerClass,
 | 
	
		
			
				|  |  | -                entitlements.componentName()
 | 
	
		
			
				|  |  | +                entitlements
 | 
	
		
			
				|  |  |              );
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -413,7 +423,7 @@ public class PolicyManager {
 | 
	
		
			
				|  |  |                      path
 | 
	
		
			
				|  |  |                  ),
 | 
	
		
			
				|  |  |                  callerClass,
 | 
	
		
			
				|  |  | -                entitlements.componentName()
 | 
	
		
			
				|  |  | +                entitlements
 | 
	
		
			
				|  |  |              );
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -502,18 +512,19 @@ public class PolicyManager {
 | 
	
		
			
				|  |  |                      PolicyParser.getEntitlementTypeName(entitlementClass)
 | 
	
		
			
				|  |  |                  ),
 | 
	
		
			
				|  |  |                  callerClass,
 | 
	
		
			
				|  |  | -                classEntitlements.componentName()
 | 
	
		
			
				|  |  | +                classEntitlements
 | 
	
		
			
				|  |  |              );
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        logger.debug(
 | 
	
		
			
				|  |  | -            () -> Strings.format(
 | 
	
		
			
				|  |  | -                "Entitled: component [%s], module [%s], class [%s], entitlement [%s]",
 | 
	
		
			
				|  |  | -                classEntitlements.componentName(),
 | 
	
		
			
				|  |  | -                requestingClass.getModule().getName(),
 | 
	
		
			
				|  |  | -                requestingClass,
 | 
	
		
			
				|  |  | -                PolicyParser.getEntitlementTypeName(entitlementClass)
 | 
	
		
			
				|  |  | -            )
 | 
	
		
			
				|  |  | -        );
 | 
	
		
			
				|  |  | +        classEntitlements.logger()
 | 
	
		
			
				|  |  | +            .debug(
 | 
	
		
			
				|  |  | +                () -> Strings.format(
 | 
	
		
			
				|  |  | +                    "Entitled: component [%s], module [%s], class [%s], entitlement [%s]",
 | 
	
		
			
				|  |  | +                    classEntitlements.componentName(),
 | 
	
		
			
				|  |  | +                    requestingClass.getModule().getName(),
 | 
	
		
			
				|  |  | +                    requestingClass,
 | 
	
		
			
				|  |  | +                    PolicyParser.getEntitlementTypeName(entitlementClass)
 | 
	
		
			
				|  |  | +                )
 | 
	
		
			
				|  |  | +            );
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void checkWriteProperty(Class<?> callerClass, String property) {
 | 
	
	
		
			
				|  | @@ -524,15 +535,16 @@ public class PolicyManager {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          ModuleEntitlements entitlements = getEntitlements(requestingClass);
 | 
	
		
			
				|  |  |          if (entitlements.getEntitlements(WriteSystemPropertiesEntitlement.class).anyMatch(e -> e.properties().contains(property))) {
 | 
	
		
			
				|  |  | -            logger.debug(
 | 
	
		
			
				|  |  | -                () -> Strings.format(
 | 
	
		
			
				|  |  | -                    "Entitled: component [%s], module [%s], class [%s], entitlement [write_system_properties], property [%s]",
 | 
	
		
			
				|  |  | -                    entitlements.componentName(),
 | 
	
		
			
				|  |  | -                    requestingClass.getModule().getName(),
 | 
	
		
			
				|  |  | -                    requestingClass,
 | 
	
		
			
				|  |  | -                    property
 | 
	
		
			
				|  |  | -                )
 | 
	
		
			
				|  |  | -            );
 | 
	
		
			
				|  |  | +            entitlements.logger()
 | 
	
		
			
				|  |  | +                .debug(
 | 
	
		
			
				|  |  | +                    () -> Strings.format(
 | 
	
		
			
				|  |  | +                        "Entitled: component [%s], module [%s], class [%s], entitlement [write_system_properties], property [%s]",
 | 
	
		
			
				|  |  | +                        entitlements.componentName(),
 | 
	
		
			
				|  |  | +                        requestingClass.getModule().getName(),
 | 
	
		
			
				|  |  | +                        requestingClass,
 | 
	
		
			
				|  |  | +                        property
 | 
	
		
			
				|  |  | +                    )
 | 
	
		
			
				|  |  | +                );
 | 
	
		
			
				|  |  |              return;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          notEntitled(
 | 
	
	
		
			
				|  | @@ -544,22 +556,34 @@ public class PolicyManager {
 | 
	
		
			
				|  |  |                  property
 | 
	
		
			
				|  |  |              ),
 | 
	
		
			
				|  |  |              callerClass,
 | 
	
		
			
				|  |  | -            entitlements.componentName()
 | 
	
		
			
				|  |  | +            entitlements
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    private void notEntitled(String message, Class<?> callerClass, String componentName) {
 | 
	
		
			
				|  |  | +    private void notEntitled(String message, Class<?> callerClass, ModuleEntitlements entitlements) {
 | 
	
		
			
				|  |  |          var exception = new NotEntitledException(message);
 | 
	
		
			
				|  |  |          // Don't emit a log for muted classes, e.g. classes containing self tests
 | 
	
		
			
				|  |  |          if (mutedClasses.contains(callerClass) == false) {
 | 
	
		
			
				|  |  | -            var moduleName = callerClass.getModule().getName();
 | 
	
		
			
				|  |  | -            var loggerSuffix = "." + componentName + "." + ((moduleName == null) ? ALL_UNNAMED : moduleName);
 | 
	
		
			
				|  |  | -            var notEntitledLogger = LogManager.getLogger(PolicyManager.class.getName() + loggerSuffix);
 | 
	
		
			
				|  |  | -            notEntitledLogger.warn("Not entitled:", exception);
 | 
	
		
			
				|  |  | +            entitlements.logger().warn("Not entitled:", exception);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          throw exception;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    private static Logger getLogger(String componentName, String moduleName) {
 | 
	
		
			
				|  |  | +        var loggerSuffix = "." + componentName + "." + ((moduleName == null) ? ALL_UNNAMED : moduleName);
 | 
	
		
			
				|  |  | +        return MODULE_LOGGERS.computeIfAbsent(PolicyManager.class.getName() + loggerSuffix, LogManager::getLogger);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * We want to use the same {@link Logger} object for a given name, because we want {@link ModuleEntitlements}
 | 
	
		
			
				|  |  | +     * {@code equals} and {@code hashCode} to work.
 | 
	
		
			
				|  |  | +     * <p>
 | 
	
		
			
				|  |  | +     * This would not be required if LogManager
 | 
	
		
			
				|  |  | +     * <a href="https://github.com/elastic/elasticsearch/issues/87511">memoized the loggers</a>,
 | 
	
		
			
				|  |  | +     * but here we are.
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private static final ConcurrentHashMap<String, Logger> MODULE_LOGGERS = new ConcurrentHashMap<>();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      public void checkManageThreadsEntitlement(Class<?> callerClass) {
 | 
	
		
			
				|  |  |          checkEntitlementPresent(callerClass, ManageThreadsEntitlement.class);
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -592,7 +616,7 @@ public class PolicyManager {
 | 
	
		
			
				|  |  |          if (pluginName != null) {
 | 
	
		
			
				|  |  |              var pluginEntitlements = pluginsEntitlements.get(pluginName);
 | 
	
		
			
				|  |  |              if (pluginEntitlements == null) {
 | 
	
		
			
				|  |  | -                return defaultEntitlements(pluginName, sourcePaths.get(pluginName));
 | 
	
		
			
				|  |  | +                return defaultEntitlements(pluginName, sourcePaths.get(pluginName), requestingModule.getName());
 | 
	
		
			
				|  |  |              } else {
 | 
	
		
			
				|  |  |                  return getModuleScopeEntitlements(
 | 
	
		
			
				|  |  |                      pluginEntitlements,
 | 
	
	
		
			
				|  | @@ -613,7 +637,7 @@ public class PolicyManager {
 | 
	
		
			
				|  |  |              );
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        return defaultEntitlements(UNKNOWN_COMPONENT_NAME, null);
 | 
	
		
			
				|  |  | +        return defaultEntitlements(UNKNOWN_COMPONENT_NAME, null, requestingModule.getName());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private static String getScopeName(Module requestingModule) {
 | 
	
	
		
			
				|  | @@ -634,7 +658,7 @@ public class PolicyManager {
 | 
	
		
			
				|  |  |              return Paths.get(codeSource.getLocation().toURI());
 | 
	
		
			
				|  |  |          } catch (Exception e) {
 | 
	
		
			
				|  |  |              // If we get a URISyntaxException, or any other Exception due to an invalid URI, we return null to safely skip this location
 | 
	
		
			
				|  |  | -            logger.info(
 | 
	
		
			
				|  |  | +            generalLogger.info(
 | 
	
		
			
				|  |  |                  "Cannot get component path for [{}]: [{}] cannot be converted to a valid Path",
 | 
	
		
			
				|  |  |                  requestingClass.getName(),
 | 
	
		
			
				|  |  |                  codeSource.getLocation().toString()
 | 
	
	
		
			
				|  | @@ -651,7 +675,7 @@ public class PolicyManager {
 | 
	
		
			
				|  |  |      ) {
 | 
	
		
			
				|  |  |          var entitlements = scopeEntitlements.get(scopeName);
 | 
	
		
			
				|  |  |          if (entitlements == null) {
 | 
	
		
			
				|  |  | -            return defaultEntitlements(componentName, componentPath);
 | 
	
		
			
				|  |  | +            return defaultEntitlements(componentName, componentPath, scopeName);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          return policyEntitlements(componentName, componentPath, scopeName, entitlements);
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -694,18 +718,18 @@ public class PolicyManager {
 | 
	
		
			
				|  |  |       * @return true if permission is granted regardless of the entitlement
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  |      private static boolean isTriviallyAllowed(Class<?> requestingClass) {
 | 
	
		
			
				|  |  | -        if (logger.isTraceEnabled()) {
 | 
	
		
			
				|  |  | -            logger.trace("Stack trace for upcoming trivially-allowed check", new Exception());
 | 
	
		
			
				|  |  | +        if (generalLogger.isTraceEnabled()) {
 | 
	
		
			
				|  |  | +            generalLogger.trace("Stack trace for upcoming trivially-allowed check", new Exception());
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          if (requestingClass == null) {
 | 
	
		
			
				|  |  | -            logger.debug("Entitlement trivially allowed: no caller frames outside the entitlement library");
 | 
	
		
			
				|  |  | +            generalLogger.debug("Entitlement trivially allowed: no caller frames outside the entitlement library");
 | 
	
		
			
				|  |  |              return true;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          if (systemModules.contains(requestingClass.getModule())) {
 | 
	
		
			
				|  |  | -            logger.debug("Entitlement trivially allowed from system module [{}]", requestingClass.getModule().getName());
 | 
	
		
			
				|  |  | +            generalLogger.debug("Entitlement trivially allowed from system module [{}]", requestingClass.getModule().getName());
 | 
	
		
			
				|  |  |              return true;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        logger.trace("Entitlement not trivially allowed");
 | 
	
		
			
				|  |  | +        generalLogger.trace("Entitlement not trivially allowed");
 | 
	
		
			
				|  |  |          return false;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 |