1010package org .elasticsearch .entitlement .runtime .policy ;
1111
1212import org .elasticsearch .core .Strings ;
13+ import org .elasticsearch .entitlement .runtime .policy .entitlements .Entitlement ;
14+ import org .elasticsearch .entitlement .runtime .policy .entitlements .FilesEntitlement ;
1315import org .elasticsearch .logging .LogManager ;
1416import org .elasticsearch .logging .Logger ;
1517
2022import java .nio .file .Files ;
2123import java .nio .file .Path ;
2224import java .nio .file .StandardOpenOption ;
25+ import java .util .ArrayList ;
2326import java .util .Base64 ;
2427import java .util .Collection ;
2528import java .util .HashMap ;
2629import java .util .List ;
2730import java .util .Map ;
28- import java .util .Optional ;
2931import java .util .Set ;
3032import java .util .stream .Collectors ;
33+ import java .util .stream .Stream ;
3134
3235import static java .util .Objects .requireNonNull ;
3336import static org .elasticsearch .entitlement .runtime .policy .PolicyManager .ALL_UNNAMED ;
@@ -36,6 +39,45 @@ public class PolicyParserUtils {
3639
3740 private static final Logger logger = LogManager .getLogger (PolicyParserUtils .class );
3841
42+ public static List <Scope > mergeScopes (List <Scope > mainScopes , List <Scope > additionalScopes ) {
43+ var result = new ArrayList <Scope >();
44+ var additionalScopesMap = additionalScopes .stream ().collect (Collectors .toMap (Scope ::moduleName , Scope ::entitlements ));
45+ for (var mainScope : mainScopes ) {
46+ List <Entitlement > additionalEntitlements = additionalScopesMap .remove (mainScope .moduleName ());
47+ if (additionalEntitlements != null ) {
48+ result .add (new Scope (mainScope .moduleName (), mergeEntitlements (mainScope .entitlements (), additionalEntitlements )));
49+ } else {
50+ result .add (mainScope );
51+ }
52+ }
53+
54+ for (var remainingEntry : additionalScopesMap .entrySet ()) {
55+ result .add (new Scope (remainingEntry .getKey (), remainingEntry .getValue ()));
56+ }
57+ return result ;
58+ }
59+
60+ static List <Entitlement > mergeEntitlements (List <Entitlement > a , List <Entitlement > b ) {
61+ Map <Class <? extends Entitlement >, List <Entitlement >> allEntitlements = Stream .concat (a .stream (), b .stream ())
62+ .collect (Collectors .groupingBy (Entitlement ::getClass ));
63+
64+ List <Entitlement > result = new ArrayList <>();
65+ for (var entitlements : allEntitlements .entrySet ()) {
66+ var entitlementClass = entitlements .getKey ();
67+ if (entitlementClass .equals (FilesEntitlement .class )) {
68+ var filesData = entitlements .getValue ().stream ().flatMap (entitlement -> {
69+ FilesEntitlement filesEntitlement = (FilesEntitlement ) entitlement ;
70+ return filesEntitlement .filesData ().stream ();
71+ }).filter (x -> x .platform ().isCurrent ()).distinct ();
72+
73+ result .add (new FilesEntitlement (filesData .toList ()));
74+ } else {
75+ result .add (entitlements .getValue ().get (0 ));
76+ }
77+ }
78+ return result ;
79+ }
80+
3981 public record PluginData (Path pluginPath , boolean isModular , boolean isExternalPlugin ) {
4082 public PluginData {
4183 requireNonNull (pluginPath );
@@ -44,8 +86,6 @@ public record PluginData(Path pluginPath, boolean isModular, boolean isExternalP
4486
4587 private static final String POLICY_FILE_NAME = "entitlement-policy.yaml" ;
4688
47- public static final String POLICY_OVERRIDE_PREFIX = "es.entitlements.policy." ;
48-
4989 public static Map <String , Policy > createPluginPolicies (Collection <PluginData > pluginData , Map <String , String > overrides , String version )
5090 throws IOException {
5191 Map <String , Policy > pluginPolicies = new HashMap <>(pluginData .size ());
@@ -54,9 +94,15 @@ public static Map<String, Policy> createPluginPolicies(Collection<PluginData> pl
5494 String pluginName = pluginRoot .getFileName ().toString ();
5595 final Set <String > moduleNames = getModuleNames (pluginRoot , entry .isModular ());
5696
57- var overriddenPolicy = parsePolicyOverrideIfExists (overrides , version , entry .isExternalPlugin (), pluginName , moduleNames );
58- if (overriddenPolicy .isPresent ()) {
59- pluginPolicies .put (pluginName , overriddenPolicy .get ());
97+ var overriddenPolicy = parsePolicyOverrideIfExists (
98+ overrides .get (pluginName ),
99+ version ,
100+ entry .isExternalPlugin (),
101+ pluginName ,
102+ moduleNames
103+ );
104+ if (overriddenPolicy != null ) {
105+ pluginPolicies .put (pluginName , overriddenPolicy );
60106 } else {
61107 Path policyFile = pluginRoot .resolve (POLICY_FILE_NAME );
62108 var policy = parsePolicyIfExists (pluginName , policyFile , entry .isExternalPlugin ());
@@ -67,59 +113,55 @@ public static Map<String, Policy> createPluginPolicies(Collection<PluginData> pl
67113 return pluginPolicies ;
68114 }
69115
70- static Optional < Policy > parsePolicyOverrideIfExists (
71- Map < String , String > overrides ,
116+ public static Policy parsePolicyOverrideIfExists (
117+ String policyOverride ,
72118 String version ,
73119 boolean externalPlugin ,
74- String pluginName ,
120+ String layerName ,
75121 Set <String > moduleNames
76122 ) {
77- var policyOverride = overrides .get (pluginName );
78123 if (policyOverride != null ) {
79124 try {
80- var versionedPolicy = decodeOverriddenPluginPolicy (policyOverride , pluginName , externalPlugin );
81- validatePolicyScopes (pluginName , versionedPolicy .policy (), moduleNames , "<override>" );
125+ var versionedPolicy = decodeOverriddenPluginPolicy (policyOverride , layerName , externalPlugin );
126+ validatePolicyScopes (layerName , versionedPolicy .policy (), moduleNames , "<override>" );
82127
83128 // Empty versions defaults to "any"
84129 if (versionedPolicy .versions ().isEmpty () || versionedPolicy .versions ().contains (version )) {
85- logger .info ("Using policy override for plugin [{}]" , pluginName );
86- return Optional . of ( versionedPolicy .policy () );
130+ logger .info ("Using policy override for layer [{}]" , layerName );
131+ return versionedPolicy .policy ();
87132 } else {
88133 logger .warn (
89134 "Found a policy override with version mismatch. The override will not be applied. "
90- + "Plugin [{}]; policy versions [{}]; current version [{}]" ,
91- pluginName ,
135+ + "Layer [{}]; policy versions [{}]; current version [{}]" ,
136+ layerName ,
92137 String .join ("," , versionedPolicy .versions ()),
93138 version
94139 );
95140 }
96141 } catch (Exception ex ) {
97142 logger .warn (
98- Strings .format (
99- "Found a policy override with invalid content. The override will not be applied. Plugin [%s]" ,
100- pluginName
101- ),
143+ Strings .format ("Found a policy override with invalid content. The override will not be applied. Layer [%s]" , layerName ),
102144 ex
103145 );
104146 }
105147 }
106- return Optional . empty () ;
148+ return null ;
107149 }
108150
109- static VersionedPolicy decodeOverriddenPluginPolicy (String base64String , String pluginName , boolean isExternalPlugin )
151+ static VersionedPolicy decodeOverriddenPluginPolicy (String base64String , String layerName , boolean isExternalPlugin )
110152 throws IOException {
111153 byte [] policyDefinition = Base64 .getDecoder ().decode (base64String );
112- return new PolicyParser (new ByteArrayInputStream (policyDefinition ), pluginName , isExternalPlugin ).parseVersionedPolicy ();
154+ return new PolicyParser (new ByteArrayInputStream (policyDefinition ), layerName , isExternalPlugin ).parseVersionedPolicy ();
113155 }
114156
115- private static void validatePolicyScopes (String pluginName , Policy policy , Set <String > moduleNames , String policyLocation ) {
157+ private static void validatePolicyScopes (String layerName , Policy policy , Set <String > moduleNames , String policyLocation ) {
116158 // TODO: should this check actually be part of the parser?
117159 for (Scope scope : policy .scopes ()) {
118160 if (moduleNames .contains (scope .moduleName ()) == false ) {
119161 throw new IllegalStateException (
120162 Strings .format (
121- "Invalid module name in policy: plugin [%s] does not have module [%s]; available modules [%s]; policy path [%s]" ,
122- pluginName ,
163+ "Invalid module name in policy: layer [%s] does not have module [%s]; available modules [%s]; policy path [%s]" ,
164+ layerName ,
123165 scope .moduleName (),
124166 String .join (", " , moduleNames ),
125167 policyLocation
0 commit comments