Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failure store authorization #114671

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ static TransportVersion def(int id) {
public static final TransportVersion ESQL_FIELD_ATTRIBUTE_PARENT_SIMPLIFIED = def(8_775_00_0);
public static final TransportVersion INFERENCE_DONT_PERSIST_ON_READ = def(8_776_00_0);
public static final TransportVersion SIMULATE_MAPPING_ADDITION = def(8_777_00_0);
public static final TransportVersion FAILURE_STORE_AUTH = def(8_778_00_0);

/*
* STOP! READ THIS FIRST! No, really,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ public void addIndex(
String[] grantedFields,
String[] deniedFields,
@Nullable BytesReference query,
boolean allowRestrictedIndices
boolean allowRestrictedIndices,
boolean dataSelector,
boolean failureSelector
) {
this.indicesPrivileges.add(
RoleDescriptor.IndicesPrivileges.builder()
Expand All @@ -124,6 +126,8 @@ public void addIndex(
.deniedFields(deniedFields)
.query(query)
.allowRestrictedIndices(allowRestrictedIndices)
.dataStoreSelector(dataSelector)
.failureStoreSelector(failureSelector)
.build()
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,11 @@ public PutRoleRequestBuilder addIndices(
String[] grantedFields,
String[] deniedFields,
@Nullable BytesReference query,
boolean allowRestrictedIndices
boolean allowRestrictedIndices,
boolean dataSelector,
boolean failureSelector
) {
request.addIndex(indices, privileges, grantedFields, deniedFields, query, allowRestrictedIndices);
request.addIndex(indices, privileges, grantedFields, deniedFields, query, allowRestrictedIndices, dataSelector, failureSelector);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import java.util.Map;
import java.util.Objects;

import static org.elasticsearch.TransportVersions.FAILURE_STORE_AUTH;
import static org.elasticsearch.common.xcontent.XContentHelper.createParserNotCompressed;
import static org.elasticsearch.xpack.core.security.authz.permission.RemoteClusterPermissions.ROLE_REMOTE_CLUSTER_PRIVS;

Expand Down Expand Up @@ -911,6 +912,8 @@ private static IndicesPrivilegesWithOptionalRemoteClusters parseIndexWithOptiona
String[] grantedFields = null;
String[] deniedFields = null;
boolean allowRestrictedIndices = false;
boolean dataSelector = IndicesPrivileges.DEFAULT_DATA_SELECTOR;
boolean failureSelector = IndicesPrivileges.DEFAULT_FAILURE_SELECTOR;
String[] remoteClusters = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
Expand Down Expand Up @@ -948,6 +951,55 @@ private static IndicesPrivilegesWithOptionalRemoteClusters parseIndexWithOptiona
token
);
}
} else if (Fields.SELECTORS.match(currentFieldName, parser.getDeprecationHandler())) {
if (token == XContentParser.Token.START_OBJECT) {
token = parser.nextToken();
do {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
token = parser.nextToken();
if (Fields.DATA_SELECTOR.match(currentFieldName, parser.getDeprecationHandler())) {
dataSelector = parser.booleanValue();
} else if (Fields.FAILURE_SELECTOR.match(currentFieldName, parser.getDeprecationHandler())) {
failureSelector = parser.booleanValue();
} else {
throw new ElasticsearchParseException(
"failed to parse indices privileges for role [{}]. "
+ "\"{}\" only accepts options {} and {}, but got: {}",
roleName,
Fields.FIELD_PERMISSIONS,
Fields.DATA_SELECTOR,
Fields.FAILURE_SELECTOR,
parser.currentName()
);
}
} else {
if (token == XContentParser.Token.END_OBJECT) {
throw new ElasticsearchParseException(
"failed to parse indices privileges for role [{}]. " + "\"{}\" must not be empty.",
roleName,
Fields.FIELD_PERMISSIONS
);
} else {
throw new ElasticsearchParseException(
"failed to parse indices privileges for role [{}]. expected {} but " + "got {}.",
roleName,
XContentParser.Token.FIELD_NAME,
token
);
}
}
} while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT);
} else {
throw new ElasticsearchParseException(
"failed to parse indices privileges for role [{}]. expected {} but got {} in \"{}\".",
roleName,
XContentParser.Token.START_OBJECT,
token,
Fields.SELECTORS
);
}

} else if (Fields.QUERY.match(currentFieldName, parser.getDeprecationHandler())) {
if (token == XContentParser.Token.START_OBJECT) {
XContentBuilder builder = JsonXContent.contentBuilder();
Expand Down Expand Up @@ -1111,6 +1163,8 @@ private static IndicesPrivilegesWithOptionalRemoteClusters parseIndexWithOptiona
.deniedFields(deniedFields)
.query(query)
.allowRestrictedIndices(allowRestrictedIndices)
.dataStoreSelector(dataSelector)
.failureStoreSelector(failureSelector)
.build(),
remoteClusters
);
Expand Down Expand Up @@ -1326,6 +1380,9 @@ public static class IndicesPrivileges implements ToXContentObject, Writeable, Co

private static final IndicesPrivileges[] NONE = new IndicesPrivileges[0];

public static final boolean DEFAULT_DATA_SELECTOR = true;
public static final boolean DEFAULT_FAILURE_SELECTOR = false;

private String[] indices;
private String[] privileges;
private String[] grantedFields = null;
Expand All @@ -1335,6 +1392,8 @@ public static class IndicesPrivileges implements ToXContentObject, Writeable, Co
// users. Setting this flag eliminates this special status, and any index name pattern in the permission will cover restricted
// indices as well.
private boolean allowRestrictedIndices = false;
private boolean dataSelector = DEFAULT_DATA_SELECTOR;
private boolean failureSelector = DEFAULT_FAILURE_SELECTOR;

private IndicesPrivileges() {}

Expand All @@ -1345,6 +1404,10 @@ public IndicesPrivileges(StreamInput in) throws IOException {
this.privileges = in.readStringArray();
this.query = in.readOptionalBytesReference();
this.allowRestrictedIndices = in.readBoolean();
if (in.getTransportVersion().onOrAfter(FAILURE_STORE_AUTH)) {
this.dataSelector = in.readBoolean();
this.failureSelector = in.readBoolean();
}
}

@Override
Expand All @@ -1355,6 +1418,10 @@ public void writeTo(StreamOutput out) throws IOException {
out.writeStringArray(privileges);
out.writeOptionalBytesReference(query);
out.writeBoolean(allowRestrictedIndices);
if (out.getTransportVersion().onOrAfter(FAILURE_STORE_AUTH)) {
out.writeBoolean(dataSelector);
out.writeBoolean(failureSelector);
}
}

public static Builder builder() {
Expand Down Expand Up @@ -1400,6 +1467,14 @@ public boolean allowRestrictedIndices() {
return allowRestrictedIndices;
}

public boolean dataSelector() {
return dataSelector;
}

public boolean failureSelector() {
return failureSelector;
}

public boolean hasDeniedFields() {
return deniedFields != null && deniedFields.length > 0;
}
Expand All @@ -1421,6 +1496,8 @@ public String toString() {
StringBuilder sb = new StringBuilder("IndicesPrivileges[");
sb.append("indices=[").append(Strings.arrayToCommaDelimitedString(indices));
sb.append("], allowRestrictedIndices=[").append(allowRestrictedIndices);
sb.append("], dataSelector=[").append(dataSelector);
sb.append("], failureSelector=[").append(failureSelector);
sb.append("], privileges=[").append(Strings.arrayToCommaDelimitedString(privileges));
sb.append("], ");
if (grantedFields != null || deniedFields != null) {
Expand Down Expand Up @@ -1462,6 +1539,8 @@ public boolean equals(Object o) {
if (Arrays.equals(privileges, that.privileges) == false) return false;
if (Arrays.equals(grantedFields, that.grantedFields) == false) return false;
if (Arrays.equals(deniedFields, that.deniedFields) == false) return false;
if (dataSelector != that.dataSelector) return false;
if (failureSelector != that.failureSelector) return false;
return Objects.equals(query, that.query);
}

Expand All @@ -1472,6 +1551,8 @@ public int hashCode() {
result = 31 * result + Arrays.hashCode(privileges);
result = 31 * result + Arrays.hashCode(grantedFields);
result = 31 * result + Arrays.hashCode(deniedFields);
result = 31 * result + (dataSelector ? 1 : 0);
result = 31 * result + (failureSelector ? 1 : 0);
result = 31 * result + (query != null ? query.hashCode() : 0);
return result;
}
Expand All @@ -1498,6 +1579,16 @@ XContentBuilder innerToXContent(XContentBuilder builder, boolean withPrivileges)
}
builder.endObject();
}
if (dataSelector != DEFAULT_DATA_SELECTOR || failureSelector != DEFAULT_FAILURE_SELECTOR) {
builder.startObject(RoleDescriptor.Fields.SELECTORS.getPreferredName());
if (dataSelector != DEFAULT_DATA_SELECTOR) {
builder.field(Fields.DATA_SELECTOR.getPreferredName(), dataSelector);
}
if (failureSelector != DEFAULT_FAILURE_SELECTOR) {
builder.field(Fields.FAILURE_SELECTOR.getPreferredName(), failureSelector);
}
builder.endObject();
}
if (query != null) {
builder.field("query", query.utf8ToString());
}
Expand All @@ -1517,6 +1608,14 @@ public int compareTo(IndicesPrivileges o) {
if (cmp != 0) {
return cmp;
}
cmp = Boolean.compare(dataSelector, o.dataSelector);
if (cmp != 0) {
return cmp;
}
cmp = Boolean.compare(failureSelector, o.failureSelector);
if (cmp != 0) {
return cmp;
}
cmp = Arrays.compare(indices, o.indices);
if (cmp != 0) {
return cmp;
Expand Down Expand Up @@ -1580,6 +1679,16 @@ public Builder allowRestrictedIndices(boolean allow) {
return this;
}

public Builder dataStoreSelector(boolean selector) {
indicesPrivileges.dataSelector = selector;
return this;
}

public Builder failureStoreSelector(boolean selector) {
indicesPrivileges.failureSelector = selector;
return this;
}

public Builder query(@Nullable BytesReference query) {
if (query == null) {
indicesPrivileges.query = null;
Expand Down Expand Up @@ -1879,6 +1988,9 @@ public interface Fields {
ParseField GRANT_FIELDS = new ParseField("grant");
ParseField EXCEPT_FIELDS = new ParseField("except");
ParseField METADATA = new ParseField("metadata");
ParseField SELECTORS = new ParseField("selectors");
ParseField DATA_SELECTOR = new ParseField("data");
ParseField FAILURE_SELECTOR = new ParseField("failure");

ParseField METADATA_FLATTENED = new ParseField("metadata_flattened");
ParseField TRANSIENT_METADATA = new ParseField("transient_metadata");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ public void testValidationErrorWithUnknownIndexPrivilegeName() {
null,
null,
null,
randomBoolean(),
randomBoolean(),
randomBoolean()
);

Expand Down Expand Up @@ -180,6 +182,8 @@ public void testValidationSuccessWithCorrectIndexPrivilegeName() {
null,
null,
null,
randomBoolean(),
randomBoolean(),
randomBoolean()
);
assertSuccessfulValidation(request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public static RoleDescriptor.RemoteIndicesPrivileges[] randomRemoteIndicesPrivil
}

public static RoleDescriptor.RemoteIndicesPrivileges[] randomRemoteIndicesPrivileges(int min, int max, Set<String> excludedPrivileges) {
final RoleDescriptor.IndicesPrivileges[] innerIndexPrivileges = randomIndicesPrivileges(min, max, excludedPrivileges);
final RoleDescriptor.IndicesPrivileges[] innerIndexPrivileges = randomIndicesPrivileges(min, max, excludedPrivileges, true);
final RoleDescriptor.RemoteIndicesPrivileges[] remoteIndexPrivileges =
new RoleDescriptor.RemoteIndicesPrivileges[innerIndexPrivileges.length];
for (int i = 0; i < remoteIndexPrivileges.length; i++) {
Expand All @@ -159,29 +159,39 @@ public static RoleDescriptor.RemoteIndicesPrivileges[] randomRemoteIndicesPrivil
return remoteIndexPrivileges;
}

public static RoleDescriptor.IndicesPrivileges[] randomIndicesPrivileges(int min, int max) {
return randomIndicesPrivileges(min, max, Set.of());
public static RoleDescriptor.IndicesPrivileges[] randomIndicesPrivileges(int min, int max, boolean allowSelectors) {
return randomIndicesPrivileges(min, max, Set.of(), allowSelectors);
}

public static RoleDescriptor.IndicesPrivileges[] randomIndicesPrivileges(int min, int max, Set<String> excludedPrivileges) {
public static RoleDescriptor.IndicesPrivileges[] randomIndicesPrivileges(
int min,
int max,
Set<String> excludedPrivileges,
boolean allowSelectors
) {
final RoleDescriptor.IndicesPrivileges[] indexPrivileges = new RoleDescriptor.IndicesPrivileges[randomIntBetween(min, max)];
for (int i = 0; i < indexPrivileges.length; i++) {
indexPrivileges[i] = randomIndicesPrivilegesBuilder(excludedPrivileges).build();
indexPrivileges[i] = randomIndicesPrivilegesBuilder(excludedPrivileges, allowSelectors).build();
}
return indexPrivileges;
}

public static RoleDescriptor.IndicesPrivileges.Builder randomIndicesPrivilegesBuilder() {
return randomIndicesPrivilegesBuilder(Set.of());
public static RoleDescriptor.IndicesPrivileges.Builder randomIndicesPrivilegesBuilder(boolean allowSelectors) {
return randomIndicesPrivilegesBuilder(Set.of(), allowSelectors);
}

private static RoleDescriptor.IndicesPrivileges.Builder randomIndicesPrivilegesBuilder(Set<String> excludedPrivileges) {
private static RoleDescriptor.IndicesPrivileges.Builder randomIndicesPrivilegesBuilder(
Set<String> excludedPrivileges,
boolean allowSelectors
) {
final Set<String> candidatePrivilegesNames = Sets.difference(IndexPrivilege.names(), excludedPrivileges);
assert false == candidatePrivilegesNames.isEmpty() : "no candidate privilege names to random from";
final RoleDescriptor.IndicesPrivileges.Builder builder = RoleDescriptor.IndicesPrivileges.builder()
.privileges(randomSubsetOf(randomIntBetween(1, 4), candidatePrivilegesNames))
.indices(generateRandomStringArray(5, randomIntBetween(3, 9), false, false))
.allowRestrictedIndices(randomBoolean());
.allowRestrictedIndices(randomBoolean())
.dataStoreSelector(allowSelectors ? randomBoolean() : RoleDescriptor.IndicesPrivileges.DEFAULT_DATA_SELECTOR)
.failureStoreSelector(allowSelectors ? randomBoolean() : RoleDescriptor.IndicesPrivileges.DEFAULT_FAILURE_SELECTOR);
randomDlsFls(builder);
return builder;
}
Expand Down Expand Up @@ -275,6 +285,7 @@ public static class Builder {
private boolean allowDescription = false;
private boolean allowRemoteClusters = false;
private boolean allowConfigurableClusterPrivileges = false;
private boolean allowSelectors = false;

public Builder() {}

Expand Down Expand Up @@ -313,6 +324,11 @@ public Builder allowRemoteClusters(boolean allowRemoteClusters) {
return this;
}

public Builder allowSelectors(boolean allowSelectors) {
this.allowSelectors = allowSelectors;
return this;
}

public RoleDescriptor build() {
final RoleDescriptor.RemoteIndicesPrivileges[] remoteIndexPrivileges;
if (alwaysIncludeRemoteIndices || (allowRemoteIndices && randomBoolean())) {
Expand All @@ -329,7 +345,7 @@ public RoleDescriptor build() {
return new RoleDescriptor(
randomAlphaOfLengthBetween(3, 90),
randomSubsetOf(ClusterPrivilegeResolver.names()).toArray(String[]::new),
randomIndicesPrivileges(0, 3),
randomIndicesPrivileges(0, 3, allowSelectors),
randomApplicationPrivileges(),
allowConfigurableClusterPrivileges ? randomClusterPrivileges() : null,
generateRandomStringArray(5, randomIntBetween(2, 8), false, true),
Expand Down
Loading