forked from elastic/elasticsearch
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'es/main' into block_source_loader_ignor…
…e_above
- Loading branch information
Showing
105 changed files
with
2,375 additions
and
686 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
...ement-runtime/src/main/java/org/elasticsearch/entitlement/runtime/policy/Entitlement.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the "Elastic License | ||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
* Public License v 1"; you may not use this file except in compliance with, at | ||
* your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
package org.elasticsearch.entitlement.runtime.policy; | ||
|
||
/** | ||
* Marker interface to ensure that only {@link Entitlement} are | ||
* part of a {@link Policy}. All entitlement classes should implement | ||
* this. | ||
*/ | ||
public interface Entitlement { | ||
|
||
} |
36 changes: 36 additions & 0 deletions
36
...ntime/src/main/java/org/elasticsearch/entitlement/runtime/policy/ExternalEntitlement.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the "Elastic License | ||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
* Public License v 1"; you may not use this file except in compliance with, at | ||
* your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
package org.elasticsearch.entitlement.runtime.policy; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
/** | ||
* This annotation indicates an {@link Entitlement} is available | ||
* to "external" classes such as those used in plugins. Any {@link Entitlement} | ||
* using this annotation is considered parseable as part of a policy file | ||
* for entitlements. | ||
*/ | ||
@Target(ElementType.CONSTRUCTOR) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface ExternalEntitlement { | ||
|
||
/** | ||
* This is the list of parameter names that are | ||
* parseable in {@link PolicyParser#parseEntitlement(String, String)}. | ||
* The number and order of parameter names much match the number and order | ||
* of constructor parameters as this is how the parser will pass in the | ||
* parsed values from a policy file. However, the names themselves do NOT | ||
* have to match the parameter names of the constructor. | ||
*/ | ||
String[] parameterNames() default {}; | ||
} |
67 changes: 67 additions & 0 deletions
67
...t-runtime/src/main/java/org/elasticsearch/entitlement/runtime/policy/FileEntitlement.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the "Elastic License | ||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
* Public License v 1"; you may not use this file except in compliance with, at | ||
* your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
package org.elasticsearch.entitlement.runtime.policy; | ||
|
||
import java.util.List; | ||
import java.util.Objects; | ||
|
||
/** | ||
* Describes a file entitlement with a path and actions. | ||
*/ | ||
public class FileEntitlement implements Entitlement { | ||
|
||
public static final int READ_ACTION = 0x1; | ||
public static final int WRITE_ACTION = 0x2; | ||
|
||
private final String path; | ||
private final int actions; | ||
|
||
@ExternalEntitlement(parameterNames = { "path", "actions" }) | ||
public FileEntitlement(String path, List<String> actionsList) { | ||
this.path = path; | ||
int actionsInt = 0; | ||
|
||
for (String actionString : actionsList) { | ||
if ("read".equals(actionString)) { | ||
if ((actionsInt & READ_ACTION) == READ_ACTION) { | ||
throw new IllegalArgumentException("file action [read] specified multiple times"); | ||
} | ||
actionsInt |= READ_ACTION; | ||
} else if ("write".equals(actionString)) { | ||
if ((actionsInt & WRITE_ACTION) == WRITE_ACTION) { | ||
throw new IllegalArgumentException("file action [write] specified multiple times"); | ||
} | ||
actionsInt |= WRITE_ACTION; | ||
} else { | ||
throw new IllegalArgumentException("unknown file action [" + actionString + "]"); | ||
} | ||
} | ||
|
||
this.actions = actionsInt; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) return true; | ||
if (o == null || getClass() != o.getClass()) return false; | ||
FileEntitlement that = (FileEntitlement) o; | ||
return actions == that.actions && Objects.equals(path, that.path); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(path, actions); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "FileEntitlement{" + "path='" + path + '\'' + ", actions=" + actions + '}'; | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
...ntitlement-runtime/src/main/java/org/elasticsearch/entitlement/runtime/policy/Policy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the "Elastic License | ||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
* Public License v 1"; you may not use this file except in compliance with, at | ||
* your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
package org.elasticsearch.entitlement.runtime.policy; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Objects; | ||
|
||
/** | ||
* A holder for scoped entitlements. | ||
*/ | ||
public class Policy { | ||
|
||
public final String name; | ||
public final List<Scope> scopes; | ||
|
||
public Policy(String name, List<Scope> scopes) { | ||
this.name = Objects.requireNonNull(name); | ||
this.scopes = Collections.unmodifiableList(Objects.requireNonNull(scopes)); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) return true; | ||
if (o == null || getClass() != o.getClass()) return false; | ||
Policy policy = (Policy) o; | ||
return Objects.equals(name, policy.name) && Objects.equals(scopes, policy.scopes); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(name, scopes); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Policy{" + "name='" + name + '\'' + ", scopes=" + scopes + '}'; | ||
} | ||
} |
176 changes: 176 additions & 0 deletions
176
...ment-runtime/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the "Elastic License | ||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
* Public License v 1"; you may not use this file except in compliance with, at | ||
* your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
* License v3.0 only", or the "Server Side Public License, v 1". | ||
*/ | ||
|
||
package org.elasticsearch.entitlement.runtime.policy; | ||
|
||
import org.elasticsearch.xcontent.ParseField; | ||
import org.elasticsearch.xcontent.XContentParser; | ||
import org.elasticsearch.xcontent.XContentParserConfiguration; | ||
import org.elasticsearch.xcontent.yaml.YamlXContent; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.UncheckedIOException; | ||
import java.lang.reflect.Constructor; | ||
import java.lang.reflect.InvocationTargetException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
|
||
import static org.elasticsearch.entitlement.runtime.policy.PolicyParserException.newPolicyParserException; | ||
|
||
/** | ||
* A parser to parse policy files for entitlements. | ||
*/ | ||
public class PolicyParser { | ||
|
||
protected static final ParseField ENTITLEMENTS_PARSEFIELD = new ParseField("entitlements"); | ||
|
||
protected static final String entitlementPackageName = Entitlement.class.getPackage().getName(); | ||
|
||
protected final XContentParser policyParser; | ||
protected final String policyName; | ||
|
||
public PolicyParser(InputStream inputStream, String policyName) throws IOException { | ||
this.policyParser = YamlXContent.yamlXContent.createParser(XContentParserConfiguration.EMPTY, Objects.requireNonNull(inputStream)); | ||
this.policyName = policyName; | ||
} | ||
|
||
public Policy parsePolicy() { | ||
try { | ||
if (policyParser.nextToken() != XContentParser.Token.START_OBJECT) { | ||
throw newPolicyParserException("expected object <scope name>"); | ||
} | ||
List<Scope> scopes = new ArrayList<>(); | ||
while (policyParser.nextToken() != XContentParser.Token.END_OBJECT) { | ||
if (policyParser.currentToken() != XContentParser.Token.FIELD_NAME) { | ||
throw newPolicyParserException("expected object <scope name>"); | ||
} | ||
String scopeName = policyParser.currentName(); | ||
Scope scope = parseScope(scopeName); | ||
scopes.add(scope); | ||
} | ||
return new Policy(policyName, scopes); | ||
} catch (IOException ioe) { | ||
throw new UncheckedIOException(ioe); | ||
} | ||
} | ||
|
||
protected Scope parseScope(String scopeName) throws IOException { | ||
try { | ||
if (policyParser.nextToken() != XContentParser.Token.START_OBJECT) { | ||
throw newPolicyParserException(scopeName, "expected object [" + ENTITLEMENTS_PARSEFIELD.getPreferredName() + "]"); | ||
} | ||
if (policyParser.nextToken() != XContentParser.Token.FIELD_NAME | ||
|| policyParser.currentName().equals(ENTITLEMENTS_PARSEFIELD.getPreferredName()) == false) { | ||
throw newPolicyParserException(scopeName, "expected object [" + ENTITLEMENTS_PARSEFIELD.getPreferredName() + "]"); | ||
} | ||
if (policyParser.nextToken() != XContentParser.Token.START_ARRAY) { | ||
throw newPolicyParserException(scopeName, "expected array of <entitlement type>"); | ||
} | ||
List<Entitlement> entitlements = new ArrayList<>(); | ||
while (policyParser.nextToken() != XContentParser.Token.END_ARRAY) { | ||
if (policyParser.currentToken() != XContentParser.Token.START_OBJECT) { | ||
throw newPolicyParserException(scopeName, "expected object <entitlement type>"); | ||
} | ||
if (policyParser.nextToken() != XContentParser.Token.FIELD_NAME) { | ||
throw newPolicyParserException(scopeName, "expected object <entitlement type>"); | ||
} | ||
String entitlementType = policyParser.currentName(); | ||
Entitlement entitlement = parseEntitlement(scopeName, entitlementType); | ||
entitlements.add(entitlement); | ||
if (policyParser.nextToken() != XContentParser.Token.END_OBJECT) { | ||
throw newPolicyParserException(scopeName, "expected closing object"); | ||
} | ||
} | ||
if (policyParser.nextToken() != XContentParser.Token.END_OBJECT) { | ||
throw newPolicyParserException(scopeName, "expected closing object"); | ||
} | ||
return new Scope(scopeName, entitlements); | ||
} catch (IOException ioe) { | ||
throw new UncheckedIOException(ioe); | ||
} | ||
} | ||
|
||
protected Entitlement parseEntitlement(String scopeName, String entitlementType) throws IOException { | ||
Class<?> entitlementClass; | ||
try { | ||
entitlementClass = Class.forName( | ||
entitlementPackageName | ||
+ "." | ||
+ Character.toUpperCase(entitlementType.charAt(0)) | ||
+ entitlementType.substring(1) | ||
+ "Entitlement" | ||
); | ||
} catch (ClassNotFoundException cnfe) { | ||
throw newPolicyParserException(scopeName, "unknown entitlement type [" + entitlementType + "]"); | ||
} | ||
if (Entitlement.class.isAssignableFrom(entitlementClass) == false) { | ||
throw newPolicyParserException(scopeName, "unknown entitlement type [" + entitlementType + "]"); | ||
} | ||
Constructor<?> entitlementConstructor = entitlementClass.getConstructors()[0]; | ||
ExternalEntitlement entitlementMetadata = entitlementConstructor.getAnnotation(ExternalEntitlement.class); | ||
if (entitlementMetadata == null) { | ||
throw newPolicyParserException(scopeName, "unknown entitlement type [" + entitlementType + "]"); | ||
} | ||
|
||
if (policyParser.nextToken() != XContentParser.Token.START_OBJECT) { | ||
throw newPolicyParserException(scopeName, entitlementType, "expected entitlement parameters"); | ||
} | ||
Map<String, Object> parsedValues = policyParser.map(); | ||
|
||
Class<?>[] parameterTypes = entitlementConstructor.getParameterTypes(); | ||
String[] parametersNames = entitlementMetadata.parameterNames(); | ||
Object[] parameterValues = new Object[parameterTypes.length]; | ||
for (int parameterIndex = 0; parameterIndex < parameterTypes.length; ++parameterIndex) { | ||
String parameterName = parametersNames[parameterIndex]; | ||
Object parameterValue = parsedValues.remove(parameterName); | ||
if (parameterValue == null) { | ||
throw newPolicyParserException(scopeName, entitlementType, "missing entitlement parameter [" + parameterName + "]"); | ||
} | ||
Class<?> parameterType = parameterTypes[parameterIndex]; | ||
if (parameterType.isAssignableFrom(parameterValue.getClass()) == false) { | ||
throw newPolicyParserException( | ||
scopeName, | ||
entitlementType, | ||
"unexpected parameter type [" + parameterType.getSimpleName() + "] for entitlement parameter [" + parameterName + "]" | ||
); | ||
} | ||
parameterValues[parameterIndex] = parameterValue; | ||
} | ||
if (parsedValues.isEmpty() == false) { | ||
throw newPolicyParserException(scopeName, entitlementType, "extraneous entitlement parameter(s) " + parsedValues); | ||
} | ||
|
||
try { | ||
return (Entitlement) entitlementConstructor.newInstance(parameterValues); | ||
} catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { | ||
throw new IllegalStateException("internal error"); | ||
} | ||
} | ||
|
||
protected PolicyParserException newPolicyParserException(String message) { | ||
return PolicyParserException.newPolicyParserException(policyParser.getTokenLocation(), policyName, message); | ||
} | ||
|
||
protected PolicyParserException newPolicyParserException(String scopeName, String message) { | ||
return PolicyParserException.newPolicyParserException(policyParser.getTokenLocation(), policyName, scopeName, message); | ||
} | ||
|
||
protected PolicyParserException newPolicyParserException(String scopeName, String entitlementType, String message) { | ||
return PolicyParserException.newPolicyParserException( | ||
policyParser.getTokenLocation(), | ||
policyName, | ||
scopeName, | ||
entitlementType, | ||
message | ||
); | ||
} | ||
} |
Oops, something went wrong.