1414import org .elasticsearch .core .SuppressForbidden ;
1515import org .elasticsearch .entitlement .instrumentation .InstrumentationService ;
1616import org .elasticsearch .entitlement .runtime .api .NotEntitledException ;
17+ import org .elasticsearch .entitlement .runtime .policy .EntitlementsCache .ModuleEntitlements ;
1718import org .elasticsearch .entitlement .runtime .policy .FileAccessTree .ExclusiveFileEntitlement ;
1819import org .elasticsearch .entitlement .runtime .policy .FileAccessTree .ExclusivePath ;
1920import org .elasticsearch .entitlement .runtime .policy .entitlements .CreateClassLoaderEntitlement ;
121122 * All these methods start in the same way: the components identified in the previous section are used to establish if and how to check:
122123 * If the caller class belongs to {@link PolicyManager#SYSTEM_LAYER_MODULES}, no check is performed (the call is trivially allowed, see
123124 * {@link PolicyManager#isTriviallyAllowed}).
124- * Otherwise, we lazily compute and create a {@link PolicyManager. ModuleEntitlements} record (see
125+ * Otherwise, we lazily compute and create a {@link ModuleEntitlements} record (see
125126 * {@link PolicyManager#computeEntitlements}). The record is cached so it can be used in following checks, stored in a
126127 * {@code Module -> ModuleEntitlement} map.
127128 * </p>
@@ -181,49 +182,6 @@ public enum ComponentKind {
181182 }
182183 }
183184
184- /**
185- * This class contains all the entitlements by type, plus the {@link FileAccessTree} for the special case of filesystem entitlements.
186- * <p>
187- * We use layers when computing {@link ModuleEntitlements}; first, we check whether the module we are building it for is in the
188- * server layer ({@link PolicyManager#SERVER_LAYER_MODULES}) (*).
189- * If it is, we use the server policy, using the same caller class module name as the scope, and read the entitlements for that scope.
190- * Otherwise, we use the {@code PluginResolver} to identify the correct plugin layer and find the policy for it (if any).
191- * If the plugin is modular, we again use the same caller class module name as the scope, and read the entitlements for that scope.
192- * If it's not, we use the single {@code ALL-UNNAMED} scope – in this case there is one scope and all entitlements apply
193- * to all the plugin code.
194- * </p>
195- * <p>
196- * (*) implementation detail: this is currently done in an indirect way: we know the module is not in the system layer
197- * (otherwise the check would have been already trivially allowed), so we just check that the module is named, and it belongs to the
198- * boot {@link ModuleLayer}. We might want to change this in the future to make it more consistent/easier to maintain.
199- * </p>
200- *
201- * @param componentName the plugin name or else one of the special component names like "(server)".
202- */
203- record ModuleEntitlements (
204- String componentName ,
205- Map <Class <? extends Entitlement >, List <Entitlement >> entitlementsByType ,
206- FileAccessTree fileAccess ,
207- Logger logger
208- ) {
209-
210- ModuleEntitlements {
211- entitlementsByType = Map .copyOf (entitlementsByType );
212- }
213-
214- public boolean hasEntitlement (Class <? extends Entitlement > entitlementClass ) {
215- return entitlementsByType .containsKey (entitlementClass );
216- }
217-
218- public <E extends Entitlement > Stream <E > getEntitlements (Class <E > entitlementClass ) {
219- var entitlements = entitlementsByType .get (entitlementClass );
220- if (entitlements == null ) {
221- return Stream .empty ();
222- }
223- return entitlements .stream ().map (entitlementClass ::cast );
224- }
225- }
226-
227185 private FileAccessTree getDefaultFileAccess (Path componentPath ) {
228186 return FileAccessTree .withoutExclusivePaths (FilesEntitlement .EMPTY , pathLookup , componentPath );
229187 }
@@ -249,8 +207,6 @@ ModuleEntitlements policyEntitlements(String componentName, Path componentPath,
249207 );
250208 }
251209
252- final Map <Module , ModuleEntitlements > moduleEntitlementsMap = new ConcurrentHashMap <>();
253-
254210 private final Map <String , List <Entitlement >> serverEntitlements ;
255211 private final List <Entitlement > apmAgentEntitlements ;
256212 private final Map <String , Map <String , List <Entitlement >>> pluginsEntitlements ;
@@ -303,6 +259,8 @@ private static Set<Module> findSystemLayerModules() {
303259 */
304260 private final List <ExclusivePath > exclusivePaths ;
305261
262+ final EntitlementsCache moduleEntitlementsCache ;
263+
306264 public PolicyManager (
307265 Policy serverPolicy ,
308266 List <Entitlement > apmAgentEntitlements ,
@@ -311,7 +269,8 @@ public PolicyManager(
311269 Map <String , Path > sourcePaths ,
312270 Module entitlementsModule ,
313271 PathLookup pathLookup ,
314- Set <Class <?>> suppressFailureLogClasses
272+ Set <Class <?>> suppressFailureLogClasses ,
273+ EntitlementsCache moduleEntitlementsCache
315274 ) {
316275 this .serverEntitlements = buildScopeEntitlementsMap (requireNonNull (serverPolicy ));
317276 this .apmAgentEntitlements = apmAgentEntitlements ;
@@ -323,6 +282,7 @@ public PolicyManager(
323282 this .entitlementsModule = entitlementsModule ;
324283 this .pathLookup = requireNonNull (pathLookup );
325284 this .mutedClasses = suppressFailureLogClasses ;
285+ this .moduleEntitlementsCache = moduleEntitlementsCache ;
326286
327287 List <ExclusiveFileEntitlement > exclusiveFileEntitlements = new ArrayList <>();
328288 for (var e : serverEntitlements .entrySet ()) {
@@ -723,7 +683,7 @@ private void checkEntitlementPresent(Class<?> callerClass, Class<? extends Entit
723683 }
724684
725685 ModuleEntitlements getEntitlements (Class <?> requestingClass ) {
726- return moduleEntitlementsMap .computeIfAbsent (requestingClass . getModule (), m -> computeEntitlements ( requestingClass ) );
686+ return moduleEntitlementsCache .computeIfAbsent (requestingClass , this :: computeEntitlements );
727687 }
728688
729689 private ModuleEntitlements computeEntitlements (Class <?> requestingClass ) {
@@ -827,7 +787,7 @@ Optional<StackFrame> findRequestingFrame(Stream<StackFrame> frames) {
827787 /**
828788 * @return true if permission is granted regardless of the entitlement
829789 */
830- private static boolean isTriviallyAllowed (Class <?> requestingClass ) {
790+ private boolean isTriviallyAllowed (Class <?> requestingClass ) {
831791 if (generalLogger .isTraceEnabled ()) {
832792 generalLogger .trace ("Stack trace for upcoming trivially-allowed check" , new Exception ());
833793 }
@@ -843,6 +803,10 @@ private static boolean isTriviallyAllowed(Class<?> requestingClass) {
843803 generalLogger .debug ("Entitlement trivially allowed from system module [{}]" , requestingClass .getModule ().getName ());
844804 return true ;
845805 }
806+ if (moduleEntitlementsCache .isAlwaysAllowed (requestingClass )) {
807+ generalLogger .debug ("Entitlement trivially allowed by the EntitlementsCache" );
808+ return true ;
809+ }
846810 generalLogger .trace ("Entitlement not trivially allowed" );
847811 return false ;
848812 }
0 commit comments