From 706b736f4b67add635e771bca87420248796c041 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Wed, 24 Oct 2018 10:15:59 +0300 Subject: [PATCH 01/26] ClusterPrivilege --- .../security/user/ClusterPrivilege.java | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ClusterPrivilege.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ClusterPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ClusterPrivilege.java new file mode 100644 index 0000000000000..463248674fc71 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ClusterPrivilege.java @@ -0,0 +1,82 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.security.user; + +import java.util.Objects; +import java.util.regex.Pattern; + +public final class ClusterPrivilege { + + private static final Pattern ALL_CLUSTER_PATTERN = Pattern.compile("^cluster:|indices:admin/template/"); + + public static final ClusterPrivilege NONE = new ClusterPrivilege("none"); + public static final ClusterPrivilege ALL = new ClusterPrivilege("all"); + public static final ClusterPrivilege MONITOR = new ClusterPrivilege("monitor"); + public static final ClusterPrivilege MONITOR_ML = new ClusterPrivilege("monitor_ml"); + public static final ClusterPrivilege MONITOR_WATCHER = new ClusterPrivilege("monitor_watcher"); + public static final ClusterPrivilege MONITOR_ROLLUP = new ClusterPrivilege("monitor_rollup"); + public static final ClusterPrivilege MANAGE = new ClusterPrivilege("manage"); + public static final ClusterPrivilege MANAGE_ML = new ClusterPrivilege("manage_ml"); + public static final ClusterPrivilege MANAGE_WATCHER = new ClusterPrivilege("manage_watcher"); + public static final ClusterPrivilege MANAGE_ROLLUP = new ClusterPrivilege("manage_rollup"); + public static final ClusterPrivilege MANAGE_IDX_TEMPLATES = new ClusterPrivilege("manage_index_templates"); + public static final ClusterPrivilege MANAGE_INGEST_PIPELINES = new ClusterPrivilege("manage_ingest_pipelines"); + public static final ClusterPrivilege TRANSPORT_CLIENT = new ClusterPrivilege("transport_client"); + public static final ClusterPrivilege MANAGE_SECURITY = new ClusterPrivilege("manage_security"); + public static final ClusterPrivilege MANAGE_SAML = new ClusterPrivilege("manage_saml"); + public static final ClusterPrivilege MANAGE_PIPELINE = new ClusterPrivilege("manage_pipeline"); + public static final ClusterPrivilege MANAGE_CCR = new ClusterPrivilege("manage_ccr"); + public static final ClusterPrivilege READ_CCR = new ClusterPrivilege("read_ccr"); + + private final String name; + + private ClusterPrivilege(String name) { + this.name = name; + } + + public static ClusterPrivilege custom(String name) { + Objects.requireNonNull(name); + if (false == ALL_CLUSTER_PATTERN.matcher(name).matches()) { + throw new IllegalArgumentException("[" + name + "] is not a cluster action privilege."); + } + return new ClusterPrivilege(name); + } + + @Override + public String toString() { + return name; + } + + @Override + public int hashCode() { + return Objects.hash(name); + } + + @Override + public boolean equals(Object other) { + if (other == null) { + return false; + } + if (false == getClass().equals(other.getClass())) { + return false; + } + return Objects.equals(name, ((ClusterPrivilege) other).name); + } +} From 21bc2805f63b13912d49b513c45bc6af0cc07057 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Wed, 24 Oct 2018 11:03:22 +0300 Subject: [PATCH 02/26] IndexPrivilege --- .../client/security/user/IndexPrivilege.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndexPrivilege.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndexPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndexPrivilege.java new file mode 100644 index 0000000000000..497f5428ea024 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndexPrivilege.java @@ -0,0 +1,78 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.security.user; + +import java.util.Objects; +import java.util.regex.Pattern; + +public class IndexPrivilege { + + private static final Pattern ALL_INDEX_PATTERN = Pattern.compile("^indices:|internal:transport/proxy/indices:"); + + public static final IndexPrivilege NONE = new IndexPrivilege("none"); + public static final IndexPrivilege ALL = new IndexPrivilege("all"); + public static final IndexPrivilege READ = new IndexPrivilege("read"); + public static final IndexPrivilege READ_CROSS_CLUSTER = new IndexPrivilege("read_cross_cluster"); + public static final IndexPrivilege CREATE = new IndexPrivilege("create"); + public static final IndexPrivilege INDEX = new IndexPrivilege("index"); + public static final IndexPrivilege DELETE = new IndexPrivilege("delete"); + public static final IndexPrivilege WRITE = new IndexPrivilege("write"); + public static final IndexPrivilege MONITOR = new IndexPrivilege("monitor"); + public static final IndexPrivilege MANAGE = new IndexPrivilege("manage"); + public static final IndexPrivilege DELETE_INDEX = new IndexPrivilege("delete_index"); + public static final IndexPrivilege CREATE_INDEX = new IndexPrivilege("create_index"); + public static final IndexPrivilege VIEW_METADATA = new IndexPrivilege("view_index_metadata"); + public static final IndexPrivilege MANAGE_FOLLOW_INDEX = new IndexPrivilege("manage_follow_index"); + + private final String name; + + private IndexPrivilege(String name) { + this.name = name; + } + + public static IndexPrivilege custom(String name) { + Objects.requireNonNull(name); + if (false == ALL_INDEX_PATTERN.matcher(name).matches()) { + throw new IllegalArgumentException("[" + name + "] is not an index action privilege."); + } + return new IndexPrivilege(name); + } + + @Override + public String toString() { + return name; + } + + @Override + public int hashCode() { + return Objects.hash(name); + } + + @Override + public boolean equals(Object other) { + if (other == null) { + return false; + } + if (false == getClass().equals(other.getClass())) { + return false; + } + return Objects.equals(name, ((IndexPrivilege) other).name); + } +} From 9e174ebc2b09c807ce1afdc88f4661e862482754 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Fri, 26 Oct 2018 15:14:48 +0300 Subject: [PATCH 03/26] Index Privileges --- .../security/user/ClusterPrivilege.java | 10 +- .../client/security/user/IndexPrivilege.java | 10 +- .../security/user/IndicesPrivileges.java | 226 ++++++++++++++++++ 3 files changed, 236 insertions(+), 10 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndicesPrivileges.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ClusterPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ClusterPrivilege.java index 463248674fc71..dfbf25e68e185 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ClusterPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ClusterPrivilege.java @@ -70,13 +70,13 @@ public int hashCode() { } @Override - public boolean equals(Object other) { - if (other == null) { - return false; + public boolean equals(Object o) { + if (this == o) { + return true; } - if (false == getClass().equals(other.getClass())) { + if (o == null || false == getClass().equals(o.getClass())) { return false; } - return Objects.equals(name, ((ClusterPrivilege) other).name); + return Objects.equals(name, ((ClusterPrivilege) o).name); } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndexPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndexPrivilege.java index 497f5428ea024..f8cdb5c40dcc9 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndexPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndexPrivilege.java @@ -66,13 +66,13 @@ public int hashCode() { } @Override - public boolean equals(Object other) { - if (other == null) { - return false; + public boolean equals(Object o) { + if (this == o) { + return true; } - if (false == getClass().equals(other.getClass())) { + if (o == null || false == getClass().equals(o.getClass())) { return false; } - return Objects.equals(name, ((IndexPrivilege) other).name); + return Objects.equals(name, ((IndexPrivilege) o).name); } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndicesPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndicesPrivileges.java new file mode 100644 index 0000000000000..aab5c2916d561 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndicesPrivileges.java @@ -0,0 +1,226 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.security.user; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.ToXContentObject; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +public final class IndicesPrivileges implements ToXContentObject { + + public static final ParseField NAMES = new ParseField("names"); + public static final ParseField PRIVILEGES = new ParseField("privileges"); + public static final ParseField FIELD_PERMISSIONS = new ParseField("field_security"); + public static final ParseField GRANT_FIELDS = new ParseField("grant"); + public static final ParseField EXCEPT_FIELDS = new ParseField("except"); + public static final ParseField QUERY = new ParseField("query"); + + private final Set indices; + private final Set privileges; + // '*' means all fields (default value), empty means no fields + private final Set grantedFields; + // empty means no field is denied + private final Set deniedFields; + // missing query means all documents, i.e. no restrictions + private final Optional query; + + private IndicesPrivileges(Set indices, Set privileges, Set grantedFields, Set deniedFields, + Optional query) { + assert false == indices.isEmpty(); + assert false == privileges.isEmpty(); + this.indices = Collections.unmodifiableSet(indices); + this.privileges = Collections.unmodifiableSet(privileges); + this.grantedFields = Collections.unmodifiableSet(grantedFields); + this.deniedFields = Collections.unmodifiableSet(deniedFields); + this.query = query; + } + + public static Builder builder() { + return new Builder(); + } + + public Set getIndices() { + return this.indices; + } + + public Set getPrivileges() { + return this.privileges; + } + + public Set getGrantedFields() { + return this.grantedFields; + } + + public Set getDeniedFields() { + return this.deniedFields; + } + + public Optional getQuery() { + return this.query; + } + + public boolean isUsingDocumentLevelSecurity() { + return query.isPresent(); + } + + public boolean isUsingFieldLevelSecurity() { + return hasDeniedFields() || hasGrantedFields(); + } + + private boolean hasDeniedFields() { + return !deniedFields.isEmpty(); + } + + private boolean hasGrantedFields() { + // we treat just '*' as no FLS since that's what the UI defaults to + if (grantedFields.size() == 1 && grantedFields.iterator().next().equals("*")) { + return false; + } + return true; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("IndicesPrivileges["); + sb.append(NAMES.getPreferredName()).append("=[").append(Strings.collectionToCommaDelimitedString(indices)).append("], "); + sb.append(PRIVILEGES.getPreferredName()).append("=[").append(Strings.collectionToCommaDelimitedString(privileges)).append("], "); + sb.append(FIELD_PERMISSIONS).append("=["); + sb.append(GRANT_FIELDS).append("=[").append(Strings.collectionToCommaDelimitedString(grantedFields)).append("], "); + sb.append(EXCEPT_FIELDS).append("=[").append(Strings.collectionToCommaDelimitedString(deniedFields)).append("]"); + sb.append("]"); + if (query.isPresent()) { + sb.append(", ").append(QUERY.getPreferredName()).append("=[").append(query).append("]"); + } + sb.append("]"); + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + IndicesPrivileges that = (IndicesPrivileges) o; + + return indices.equals(that.indices) && privileges.equals(that.privileges) && grantedFields.equals(that.grantedFields) + && deniedFields.equals(that.deniedFields) && query.equals(that.query); + } + + @Override + public int hashCode() { + return Objects.hash(indices, privileges, grantedFields, deniedFields, query); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field(NAMES.getPreferredName(), indices); + builder.field(PRIVILEGES.getPreferredName(), privileges); + builder.startObject(FIELD_PERMISSIONS.getPreferredName()); + builder.field(GRANT_FIELDS.getPreferredName(), grantedFields); + builder.field(EXCEPT_FIELDS.getPreferredName(), deniedFields); + builder.endObject(); + if (query.isPresent()) { + builder.field("query", query.get()); + } + return builder.endObject(); + } + + public static class Builder { + + private Optional> indices = Optional.empty(); + private Optional> privileges = Optional.empty(); + private Optional> grantedFields = Optional.empty(); + private Optional> deniedFields = Optional.empty(); + private Optional query = Optional.empty(); + + private Builder() { + } + + public Builder indices(String... indices) { + return indices(Arrays.asList(indices)); + } + + public Builder indices(Collection indices) { + this.indices = Optional.of(new HashSet<>(indices)); + return this; + } + + public Builder privileges(IndexPrivilege... privileges) { + return privileges(Arrays.asList(privileges)); + } + + public Builder privileges(Collection privileges) { + this.privileges = Optional.of(new HashSet<>(privileges)); + return this; + } + + public Builder grantedFields(Collection grantedFields) { + this.grantedFields = Optional.of(new HashSet<>(grantedFields)); + return this; + } + + public Builder grantedFields(String... grantedFields) { + return grantedFields(Arrays.asList(grantedFields)); + } + + public Builder deniedFields(Collection deniedFields) { + this.deniedFields = Optional.of(new HashSet<>(deniedFields)); + return this; + } + + public Builder deniedFields(String... deniedFields) { + return deniedFields(Arrays.asList(deniedFields)); + } + + public Builder query(String query) { + this.query = Optional.of(query); + return this; + } + + public IndicesPrivileges build() { + if (false == indices.isPresent() || indices.get().isEmpty()) { + throw new IllegalArgumentException("indices privileges must refer to at least one index name or index name pattern"); + } + if (false == privileges.isPresent() || privileges.get().isEmpty()) { + throw new IllegalArgumentException("indices privileges must define at least one privilege"); + } + return new IndicesPrivileges(indices.get(), privileges.get(), + grantedFields.orElse(Collections.singleton("*")), // all fields granted unless otherwise set + deniedFields.orElse(Collections.emptySet()), // no fields denied unless otherwise set + query); + } + } + +} From 64a91e208131bb60980866b45805929e5d1c2cc7 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Mon, 29 Oct 2018 13:59:31 +0200 Subject: [PATCH 04/26] ApplicationResourcePrivilege WIP --- .../user/ApplicationResourcePrivileges.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ApplicationResourcePrivileges.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ApplicationResourcePrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ApplicationResourcePrivileges.java new file mode 100644 index 0000000000000..58b37489974d8 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ApplicationResourcePrivileges.java @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.security.user; + +public class ApplicationResourcePrivileges { + +} + From aabbc5fb3d547314ca5a8f8c25e8b608d4581cc4 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Tue, 30 Oct 2018 13:12:26 +0200 Subject: [PATCH 05/26] IndicesPrivileges is neat! --- .../security/user/IndicesPrivileges.java | 106 ++++++++++-------- 1 file changed, 59 insertions(+), 47 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndicesPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndicesPrivileges.java index aab5c2916d561..392a758df0c21 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndicesPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndicesPrivileges.java @@ -19,6 +19,7 @@ package org.elasticsearch.client.security.user; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.Strings; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -30,11 +31,10 @@ import java.util.Collections; import java.util.HashSet; import java.util.Objects; -import java.util.Optional; import java.util.Set; public final class IndicesPrivileges implements ToXContentObject { - + public static final ParseField NAMES = new ParseField("names"); public static final ParseField PRIVILEGES = new ParseField("privileges"); public static final ParseField FIELD_PERMISSIONS = new ParseField("field_security"); @@ -42,23 +42,23 @@ public final class IndicesPrivileges implements ToXContentObject { public static final ParseField EXCEPT_FIELDS = new ParseField("except"); public static final ParseField QUERY = new ParseField("query"); - private final Set indices; - private final Set privileges; + private final Collection indices; + private final Collection privileges; // '*' means all fields (default value), empty means no fields - private final Set grantedFields; + private final Collection grantedFields; // empty means no field is denied - private final Set deniedFields; + private final Collection deniedFields; // missing query means all documents, i.e. no restrictions - private final Optional query; + private final @Nullable String query; - private IndicesPrivileges(Set indices, Set privileges, Set grantedFields, Set deniedFields, - Optional query) { + private IndicesPrivileges(Collection indices, Collection privileges, Collection grantedFields, + Collection deniedFields, @Nullable String query) { assert false == indices.isEmpty(); assert false == privileges.isEmpty(); - this.indices = Collections.unmodifiableSet(indices); - this.privileges = Collections.unmodifiableSet(privileges); - this.grantedFields = Collections.unmodifiableSet(grantedFields); - this.deniedFields = Collections.unmodifiableSet(deniedFields); + this.indices = Collections.unmodifiableCollection(indices); + this.privileges = Collections.unmodifiableCollection(privileges); + this.grantedFields = Collections.unmodifiableCollection(grantedFields); + this.deniedFields = Collections.unmodifiableCollection(deniedFields); this.query = query; } @@ -66,28 +66,28 @@ public static Builder builder() { return new Builder(); } - public Set getIndices() { + public Collection getIndices() { return this.indices; } - public Set getPrivileges() { + public Collection getPrivileges() { return this.privileges; } - public Set getGrantedFields() { + public Collection getGrantedFields() { return this.grantedFields; } - public Set getDeniedFields() { + public Collection getDeniedFields() { return this.deniedFields; } - public Optional getQuery() { + public @Nullable String getQuery() { return this.query; } public boolean isUsingDocumentLevelSecurity() { - return query.isPresent(); + return query != null; } public boolean isUsingFieldLevelSecurity() { @@ -95,7 +95,7 @@ public boolean isUsingFieldLevelSecurity() { } private boolean hasDeniedFields() { - return !deniedFields.isEmpty(); + return false == deniedFields.isEmpty(); } private boolean hasGrantedFields() { @@ -115,7 +115,7 @@ public String toString() { sb.append(GRANT_FIELDS).append("=[").append(Strings.collectionToCommaDelimitedString(grantedFields)).append("], "); sb.append(EXCEPT_FIELDS).append("=[").append(Strings.collectionToCommaDelimitedString(deniedFields)).append("]"); sb.append("]"); - if (query.isPresent()) { + if (query != null) { sb.append(", ").append(QUERY.getPreferredName()).append("=[").append(query).append("]"); } sb.append("]"); @@ -151,75 +151,87 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field(GRANT_FIELDS.getPreferredName(), grantedFields); builder.field(EXCEPT_FIELDS.getPreferredName(), deniedFields); builder.endObject(); - if (query.isPresent()) { - builder.field("query", query.get()); + if (query != null) { + builder.field("query", query); } return builder.endObject(); } public static class Builder { - private Optional> indices = Optional.empty(); - private Optional> privileges = Optional.empty(); - private Optional> grantedFields = Optional.empty(); - private Optional> deniedFields = Optional.empty(); - private Optional query = Optional.empty(); + private @Nullable Set indices = null; + private @Nullable Set privileges = null; + private @Nullable Set grantedFields = null; + private @Nullable Set deniedFields = null; + private @Nullable String query = null; private Builder() { } public Builder indices(String... indices) { + Objects.requireNonNull(indices, "indices name or pattern cannot be null"); return indices(Arrays.asList(indices)); } public Builder indices(Collection indices) { - this.indices = Optional.of(new HashSet<>(indices)); + Objects.requireNonNull(indices, "indices name or pattern cannot be null"); + this.indices = new HashSet<>(indices); return this; } public Builder privileges(IndexPrivilege... privileges) { + Objects.requireNonNull(privileges, "privileges cannot be null"); return privileges(Arrays.asList(privileges)); } - + public Builder privileges(Collection privileges) { - this.privileges = Optional.of(new HashSet<>(privileges)); - return this; - } - - public Builder grantedFields(Collection grantedFields) { - this.grantedFields = Optional.of(new HashSet<>(grantedFields)); + Objects.requireNonNull(privileges, "privileges cannot be null"); + this.privileges = new HashSet<>(privileges); return this; } public Builder grantedFields(String... grantedFields) { + Objects.requireNonNull(grantedFields, "granted fields cannot be null"); return grantedFields(Arrays.asList(grantedFields)); } - - public Builder deniedFields(Collection deniedFields) { - this.deniedFields = Optional.of(new HashSet<>(deniedFields)); + + public Builder grantedFields(Collection grantedFields) { + Objects.requireNonNull(grantedFields, "granted fields cannot be null"); + this.grantedFields = new HashSet<>(grantedFields); return this; } public Builder deniedFields(String... deniedFields) { + Objects.requireNonNull(deniedFields, "denied fields cannot be null"); return deniedFields(Arrays.asList(deniedFields)); } - public Builder query(String query) { - this.query = Optional.of(query); + public Builder deniedFields(Collection deniedFields) { + Objects.requireNonNull(deniedFields, "denied fields cannot be null"); + this.deniedFields = new HashSet<>(deniedFields); + return this; + } + + public Builder query(@Nullable String query) { + this.query = query; return this; } public IndicesPrivileges build() { - if (false == indices.isPresent() || indices.get().isEmpty()) { + if (null == indices || indices.isEmpty()) { throw new IllegalArgumentException("indices privileges must refer to at least one index name or index name pattern"); } - if (false == privileges.isPresent() || privileges.get().isEmpty()) { + if (null == privileges || privileges.isEmpty()) { throw new IllegalArgumentException("indices privileges must define at least one privilege"); } - return new IndicesPrivileges(indices.get(), privileges.get(), - grantedFields.orElse(Collections.singleton("*")), // all fields granted unless otherwise set - deniedFields.orElse(Collections.emptySet()), // no fields denied unless otherwise set - query); + if (grantedFields == null) { + // all fields granted unless otherwise specified + grantedFields = Collections.singleton("*"); + } + if (deniedFields == null) { + deniedFields = Collections.emptySet(); + } + return new IndicesPrivileges(indices, privileges, grantedFields, deniedFields, query); } } From 98837265eecc7533f7d9d4e2387b2bc73e62f66c Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Tue, 30 Oct 2018 16:13:10 +0200 Subject: [PATCH 06/26] Oh my! --- .../user/ApplicationResourcePrivileges.java | 174 +++++++++++++++++- .../security/user/ClusterPrivilege.java | 52 +++--- .../client/security/user/IndexPrivilege.java | 44 +++-- .../security/user/IndicesPrivileges.java | 132 +++++++++---- 4 files changed, 331 insertions(+), 71 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ApplicationResourcePrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ApplicationResourcePrivileges.java index 58b37489974d8..d032eb9d1fdd1 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ApplicationResourcePrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ApplicationResourcePrivileges.java @@ -19,7 +19,177 @@ package org.elasticsearch.client.security.user; -public class ApplicationResourcePrivileges { +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; -} +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; + +public final class ApplicationResourcePrivileges implements ToXContentObject { + + private static final ParseField APPLICATION = new ParseField("application"); + private static final ParseField PRIVILEGES = new ParseField("privileges"); + private static final ParseField RESOURCES = new ParseField("resources"); + + @SuppressWarnings("unchecked") + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "application_privileges", false, constructorObjects -> { + int i = 0; + final String application = (String) constructorObjects[i++]; + final Collection privileges = (Collection) constructorObjects[i++]; + final Collection resources = (Collection) constructorObjects[i]; + return new ApplicationResourcePrivileges(application, privileges, resources); + }); + + static { + PARSER.declareString(constructorArg(), APPLICATION); + PARSER.declareStringArray(constructorArg(), PRIVILEGES); + PARSER.declareStringArray(constructorArg(), RESOURCES); + } + + private final String application; + private final Collection privileges; + private final Collection resources; + + private ApplicationResourcePrivileges(String application, Collection privileges, Collection resources) { + // we do all null checks inside the constructor + if (Strings.isNullOrEmpty(application)) { + throw new IllegalArgumentException("application privileges must have an application name"); + } + if (null == privileges || privileges.isEmpty()) { + throw new IllegalArgumentException("application privileges must define at least one privilege"); + } + if (null == resources || resources.isEmpty()) { + throw new IllegalArgumentException("application privileges must refer to at least one resource"); + } + this.application = application; + this.privileges = Collections.unmodifiableCollection(privileges); + this.resources = Collections.unmodifiableCollection(resources); + } + + public static Builder builder() { + return new Builder(); + } + + public String getApplication() { + return application; + } + + public Collection getResources() { + return this.resources; + } + + public Collection getPrivileges() { + return this.privileges; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append("[application=").append(application) + .append(", privileges=[").append(Strings.collectionToCommaDelimitedString(privileges)).append("], resources=[") + .append(Strings.collectionToCommaDelimitedString(resources)).append("]]"); + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass() != o.getClass()) { + return false; + } + + ApplicationResourcePrivileges that = (ApplicationResourcePrivileges) o; + + return application.equals(that.application) && privileges.equals(that.privileges) && resources.equals(that.resources); + } + + @Override + public int hashCode() { + return Objects.hash(application, privileges, resources); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field(APPLICATION.getPreferredName(), application); + builder.field(PRIVILEGES.getPreferredName(), privileges); + builder.field(RESOURCES.getPreferredName(), resources); + return builder.endObject(); + } + + public static ApplicationResourcePrivileges fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } + + public static class Builder { + + private @Nullable String application = null; + private @Nullable Set privileges = null; + private @Nullable Set resources = null; + + private Builder() { + } + + public Builder application(@Nullable String appName) { + if (appName == null) { + // null is a no-op to be programmer friendly + return this; + } + this.application = appName; + return this; + } + + public Builder resources(@Nullable String... resources) { + if (resources == null) { + // null is a no-op to be programmer friendly + return this; + } + return resources(Arrays.asList(resources)); + } + + public Builder resources(@Nullable Collection resources) { + if (resources == null) { + // null is a no-op to be programmer friendly + return this; + } + this.resources = new HashSet<>(resources); + return this; + } + + public Builder privileges(@Nullable String... privileges) { + if (privileges == null) { + // null is a no-op to be programmer friendly + return this; + } + return privileges(Arrays.asList(privileges)); + } + + public Builder privileges(@Nullable Collection privileges) { + if (privileges == null) { + // null is a no-op to be programmer friendly + return this; + } + this.privileges = new HashSet<>(privileges); + return this; + } + + public ApplicationResourcePrivileges build() { + return new ApplicationResourcePrivileges(application, privileges, resources); + } + + } +} \ No newline at end of file diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ClusterPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ClusterPrivilege.java index dfbf25e68e185..97f1349e74b9b 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ClusterPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ClusterPrivilege.java @@ -19,44 +19,54 @@ package org.elasticsearch.client.security.user; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import java.util.regex.Pattern; public final class ClusterPrivilege { private static final Pattern ALL_CLUSTER_PATTERN = Pattern.compile("^cluster:|indices:admin/template/"); + private static final Map builtins = new HashMap<>(); - public static final ClusterPrivilege NONE = new ClusterPrivilege("none"); - public static final ClusterPrivilege ALL = new ClusterPrivilege("all"); - public static final ClusterPrivilege MONITOR = new ClusterPrivilege("monitor"); - public static final ClusterPrivilege MONITOR_ML = new ClusterPrivilege("monitor_ml"); - public static final ClusterPrivilege MONITOR_WATCHER = new ClusterPrivilege("monitor_watcher"); - public static final ClusterPrivilege MONITOR_ROLLUP = new ClusterPrivilege("monitor_rollup"); - public static final ClusterPrivilege MANAGE = new ClusterPrivilege("manage"); - public static final ClusterPrivilege MANAGE_ML = new ClusterPrivilege("manage_ml"); - public static final ClusterPrivilege MANAGE_WATCHER = new ClusterPrivilege("manage_watcher"); - public static final ClusterPrivilege MANAGE_ROLLUP = new ClusterPrivilege("manage_rollup"); - public static final ClusterPrivilege MANAGE_IDX_TEMPLATES = new ClusterPrivilege("manage_index_templates"); - public static final ClusterPrivilege MANAGE_INGEST_PIPELINES = new ClusterPrivilege("manage_ingest_pipelines"); - public static final ClusterPrivilege TRANSPORT_CLIENT = new ClusterPrivilege("transport_client"); - public static final ClusterPrivilege MANAGE_SECURITY = new ClusterPrivilege("manage_security"); - public static final ClusterPrivilege MANAGE_SAML = new ClusterPrivilege("manage_saml"); - public static final ClusterPrivilege MANAGE_PIPELINE = new ClusterPrivilege("manage_pipeline"); - public static final ClusterPrivilege MANAGE_CCR = new ClusterPrivilege("manage_ccr"); - public static final ClusterPrivilege READ_CCR = new ClusterPrivilege("read_ccr"); + public static final ClusterPrivilege NONE = new ClusterPrivilege("none", true); + public static final ClusterPrivilege ALL = new ClusterPrivilege("all", true); + public static final ClusterPrivilege MONITOR = new ClusterPrivilege("monitor", true); + public static final ClusterPrivilege MONITOR_ML = new ClusterPrivilege("monitor_ml", true); + public static final ClusterPrivilege MONITOR_WATCHER = new ClusterPrivilege("monitor_watcher", true); + public static final ClusterPrivilege MONITOR_ROLLUP = new ClusterPrivilege("monitor_rollup", true); + public static final ClusterPrivilege MANAGE = new ClusterPrivilege("manage", true); + public static final ClusterPrivilege MANAGE_ML = new ClusterPrivilege("manage_ml", true); + public static final ClusterPrivilege MANAGE_WATCHER = new ClusterPrivilege("manage_watcher", true); + public static final ClusterPrivilege MANAGE_ROLLUP = new ClusterPrivilege("manage_rollup", true); + public static final ClusterPrivilege MANAGE_IDX_TEMPLATES = new ClusterPrivilege("manage_index_templates", true); + public static final ClusterPrivilege MANAGE_INGEST_PIPELINES = new ClusterPrivilege("manage_ingest_pipelines", true); + public static final ClusterPrivilege TRANSPORT_CLIENT = new ClusterPrivilege("transport_client", true); + public static final ClusterPrivilege MANAGE_SECURITY = new ClusterPrivilege("manage_security", true); + public static final ClusterPrivilege MANAGE_SAML = new ClusterPrivilege("manage_saml", true); + public static final ClusterPrivilege MANAGE_PIPELINE = new ClusterPrivilege("manage_pipeline", true); + public static final ClusterPrivilege MANAGE_CCR = new ClusterPrivilege("manage_ccr", true); + public static final ClusterPrivilege READ_CCR = new ClusterPrivilege("read_ccr", true); private final String name; - private ClusterPrivilege(String name) { + private ClusterPrivilege(String name, boolean builtin) { this.name = name; + if (builtin) { + builtins.put(name, this); + } } - public static ClusterPrivilege custom(String name) { + public static ClusterPrivilege fromString(String name) { Objects.requireNonNull(name); + final ClusterPrivilege builtin = builtins.get(name); + if (builtin != null) { + return builtin; + } if (false == ALL_CLUSTER_PATTERN.matcher(name).matches()) { throw new IllegalArgumentException("[" + name + "] is not a cluster action privilege."); } - return new ClusterPrivilege(name); + return new ClusterPrivilege(name, false); } @Override diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndexPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndexPrivilege.java index f8cdb5c40dcc9..bc5a6c67b5daa 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndexPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndexPrivilege.java @@ -19,40 +19,50 @@ package org.elasticsearch.client.security.user; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import java.util.regex.Pattern; public class IndexPrivilege { private static final Pattern ALL_INDEX_PATTERN = Pattern.compile("^indices:|internal:transport/proxy/indices:"); + private static final Map builtins = new HashMap<>(); - public static final IndexPrivilege NONE = new IndexPrivilege("none"); - public static final IndexPrivilege ALL = new IndexPrivilege("all"); - public static final IndexPrivilege READ = new IndexPrivilege("read"); - public static final IndexPrivilege READ_CROSS_CLUSTER = new IndexPrivilege("read_cross_cluster"); - public static final IndexPrivilege CREATE = new IndexPrivilege("create"); - public static final IndexPrivilege INDEX = new IndexPrivilege("index"); - public static final IndexPrivilege DELETE = new IndexPrivilege("delete"); - public static final IndexPrivilege WRITE = new IndexPrivilege("write"); - public static final IndexPrivilege MONITOR = new IndexPrivilege("monitor"); - public static final IndexPrivilege MANAGE = new IndexPrivilege("manage"); - public static final IndexPrivilege DELETE_INDEX = new IndexPrivilege("delete_index"); - public static final IndexPrivilege CREATE_INDEX = new IndexPrivilege("create_index"); - public static final IndexPrivilege VIEW_METADATA = new IndexPrivilege("view_index_metadata"); - public static final IndexPrivilege MANAGE_FOLLOW_INDEX = new IndexPrivilege("manage_follow_index"); + public static final IndexPrivilege NONE = new IndexPrivilege("none", true); + public static final IndexPrivilege ALL = new IndexPrivilege("all", true); + public static final IndexPrivilege READ = new IndexPrivilege("read", true); + public static final IndexPrivilege READ_CROSS_CLUSTER = new IndexPrivilege("read_cross_cluster", true); + public static final IndexPrivilege CREATE = new IndexPrivilege("create", true); + public static final IndexPrivilege INDEX = new IndexPrivilege("index", true); + public static final IndexPrivilege DELETE = new IndexPrivilege("delete", true); + public static final IndexPrivilege WRITE = new IndexPrivilege("write", true); + public static final IndexPrivilege MONITOR = new IndexPrivilege("monitor", true); + public static final IndexPrivilege MANAGE = new IndexPrivilege("manage", true); + public static final IndexPrivilege DELETE_INDEX = new IndexPrivilege("delete_index", true); + public static final IndexPrivilege CREATE_INDEX = new IndexPrivilege("create_index", true); + public static final IndexPrivilege VIEW_METADATA = new IndexPrivilege("view_index_metadata", true); + public static final IndexPrivilege MANAGE_FOLLOW_INDEX = new IndexPrivilege("manage_follow_index", true); private final String name; - private IndexPrivilege(String name) { + private IndexPrivilege(String name, boolean builtin) { this.name = name; + if (builtin) { + builtins.put(name, this); + } } - public static IndexPrivilege custom(String name) { + public static IndexPrivilege fromString(String name) { Objects.requireNonNull(name); + final IndexPrivilege builtin = builtins.get(name); + if (builtin != null) { + return builtin; + } if (false == ALL_INDEX_PATTERN.matcher(name).matches()) { throw new IllegalArgumentException("[" + name + "] is not an index action privilege."); } - return new IndexPrivilege(name); + return new IndexPrivilege(name, false); } @Override diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndicesPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndicesPrivileges.java index 392a758df0c21..b36e4e39ca134 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndicesPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndicesPrivileges.java @@ -22,7 +22,10 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; import org.elasticsearch.common.xcontent.ToXContentObject; import java.io.IOException; @@ -32,6 +35,10 @@ import java.util.HashSet; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; + +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; public final class IndicesPrivileges implements ToXContentObject { @@ -42,6 +49,41 @@ public final class IndicesPrivileges implements ToXContentObject { public static final ParseField EXCEPT_FIELDS = new ParseField("except"); public static final ParseField QUERY = new ParseField("query"); + @SuppressWarnings("unchecked") + public static final ConstructingObjectParser, Collection>, Void> FLS_PARSER = + new ConstructingObjectParser<>( "field_level_parser", false, constructorObjects -> { + int i = 0; + final Collection grantFields = (Collection) constructorObjects[i++]; + final Collection exceptFields = (Collection) constructorObjects[i]; + return new Tuple<>(grantFields, exceptFields); + }); + + static { + FLS_PARSER.declareStringArray(optionalConstructorArg(), GRANT_FIELDS); + FLS_PARSER.declareStringArray(optionalConstructorArg(), EXCEPT_FIELDS); + } + + @SuppressWarnings("unchecked") + private static final ConstructingObjectParser PARSER = + new ConstructingObjectParser<>("indices_privileges", false, constructorObjects -> { + int i = 0; + final Collection indices = (Collection) constructorObjects[i++]; + final Collection privilegeNames = (Collection) constructorObjects[i++]; + final Collection privileges = privilegeNames.stream().map(IndexPrivilege::fromString) + .collect(Collectors.toList()); + final Tuple, Collection> fields = + (Tuple, Collection>) constructorObjects[i++]; + final String query = (String) constructorObjects[i]; + return new IndicesPrivileges(indices, privileges, fields.v1(), fields.v2(), query); + }); + + static { + PARSER.declareStringArray(constructorArg(), NAMES); + PARSER.declareStringArray(constructorArg(), PRIVILEGES); + PARSER.declareObject(optionalConstructorArg(), FLS_PARSER, FIELD_PERMISSIONS); + PARSER.declareStringOrNull(optionalConstructorArg(), QUERY); + } + private final Collection indices; private final Collection privileges; // '*' means all fields (default value), empty means no fields @@ -53,8 +95,21 @@ public final class IndicesPrivileges implements ToXContentObject { private IndicesPrivileges(Collection indices, Collection privileges, Collection grantedFields, Collection deniedFields, @Nullable String query) { - assert false == indices.isEmpty(); - assert false == privileges.isEmpty(); + // we do all null checks inside the constructor + if (null == indices || indices.isEmpty()) { + throw new IllegalArgumentException("indices privileges must refer to at least one index name or index name pattern"); + } + if (null == privileges || privileges.isEmpty()) { + throw new IllegalArgumentException("indices privileges must define at least one privilege"); + } + if (grantedFields == null) { + // all fields granted unless otherwise specified + grantedFields = Collections.singleton("*"); + } + if (deniedFields == null) { + // no fields are denied unless otherwise specified + deniedFields = Collections.emptySet(); + } this.indices = Collections.unmodifiableCollection(indices); this.privileges = Collections.unmodifiableCollection(privileges); this.grantedFields = Collections.unmodifiableCollection(grantedFields); @@ -157,6 +212,10 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder.endObject(); } + public static IndicesPrivileges fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } + public static class Builder { private @Nullable Set indices = null; @@ -168,46 +227,70 @@ public static class Builder { private Builder() { } - public Builder indices(String... indices) { - Objects.requireNonNull(indices, "indices name or pattern cannot be null"); + public Builder indices(@Nullable String... indices) { + if (indices == null) { + // null is a no-op to be programmer friendly + return this; + } return indices(Arrays.asList(indices)); } - public Builder indices(Collection indices) { - Objects.requireNonNull(indices, "indices name or pattern cannot be null"); + public Builder indices(@Nullable Collection indices) { + if (indices == null) { + // null is a no-op to be programmer friendly + return this; + } this.indices = new HashSet<>(indices); return this; } - public Builder privileges(IndexPrivilege... privileges) { - Objects.requireNonNull(privileges, "privileges cannot be null"); + public Builder privileges(@Nullable IndexPrivilege... privileges) { + if (privileges == null) { + // null is a no-op to be programmer friendly + return this; + } return privileges(Arrays.asList(privileges)); } - public Builder privileges(Collection privileges) { - Objects.requireNonNull(privileges, "privileges cannot be null"); + public Builder privileges(@Nullable Collection privileges) { + if (privileges == null) { + // null is a no-op to be programmer friendly + return this; + } this.privileges = new HashSet<>(privileges); return this; } - public Builder grantedFields(String... grantedFields) { - Objects.requireNonNull(grantedFields, "granted fields cannot be null"); + public Builder grantedFields(@Nullable String... grantedFields) { + if (grantedFields == null) { + // null is a no-op to be programmer friendly + return this; + } return grantedFields(Arrays.asList(grantedFields)); } - public Builder grantedFields(Collection grantedFields) { - Objects.requireNonNull(grantedFields, "granted fields cannot be null"); + public Builder grantedFields(@Nullable Collection grantedFields) { + if (grantedFields == null) { + // null is a no-op to be programmer friendly + return this; + } this.grantedFields = new HashSet<>(grantedFields); return this; } - public Builder deniedFields(String... deniedFields) { - Objects.requireNonNull(deniedFields, "denied fields cannot be null"); + public Builder deniedFields(@Nullable String... deniedFields) { + if (deniedFields == null) { + // null is a no-op to be programmer friendly + return this; + } return deniedFields(Arrays.asList(deniedFields)); } - public Builder deniedFields(Collection deniedFields) { - Objects.requireNonNull(deniedFields, "denied fields cannot be null"); + public Builder deniedFields(@Nullable Collection deniedFields) { + if (deniedFields == null) { + // null is a no-op to be programmer friendly + return this; + } this.deniedFields = new HashSet<>(deniedFields); return this; } @@ -218,19 +301,6 @@ public Builder query(@Nullable String query) { } public IndicesPrivileges build() { - if (null == indices || indices.isEmpty()) { - throw new IllegalArgumentException("indices privileges must refer to at least one index name or index name pattern"); - } - if (null == privileges || privileges.isEmpty()) { - throw new IllegalArgumentException("indices privileges must define at least one privilege"); - } - if (grantedFields == null) { - // all fields granted unless otherwise specified - grantedFields = Collections.singleton("*"); - } - if (deniedFields == null) { - deniedFields = Collections.emptySet(); - } return new IndicesPrivileges(indices, privileges, grantedFields, deniedFields, query); } } From ba34f6579c8e86ed8f4ea991a91af4b9e3aa63e6 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Tue, 30 Oct 2018 18:24:28 +0200 Subject: [PATCH 07/26] Proper manage app priv --- .../ApplicationResourcePrivileges.java | 2 +- .../{ => privileges}/ClusterPrivilege.java | 2 +- .../user/{ => privileges}/IndexPrivilege.java | 2 +- .../{ => privileges}/IndicesPrivileges.java | 28 ++-- .../ManageApplicationPrivileges.java | 120 ++++++++++++++++++ 5 files changed, 135 insertions(+), 19 deletions(-) rename client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/{ => privileges}/ApplicationResourcePrivileges.java (99%) rename client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/{ => privileges}/ClusterPrivilege.java (98%) rename client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/{ => privileges}/IndexPrivilege.java (98%) rename client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/{ => privileges}/IndicesPrivileges.java (93%) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivileges.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ApplicationResourcePrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java similarity index 99% rename from client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ApplicationResourcePrivileges.java rename to client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java index d032eb9d1fdd1..e0124b153871f 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ApplicationResourcePrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.client.security.user; +package org.elasticsearch.client.security.user.privileges; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ClusterPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ClusterPrivilege.java similarity index 98% rename from client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ClusterPrivilege.java rename to client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ClusterPrivilege.java index 97f1349e74b9b..a8535b07cec70 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/ClusterPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ClusterPrivilege.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.client.security.user; +package org.elasticsearch.client.security.user.privileges; import java.util.HashMap; import java.util.Map; diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndexPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndexPrivilege.java similarity index 98% rename from client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndexPrivilege.java rename to client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndexPrivilege.java index bc5a6c67b5daa..c5694acb8ffbe 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndexPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndexPrivilege.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.client.security.user; +package org.elasticsearch.client.security.user.privileges; import java.util.HashMap; import java.util.Map; diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndicesPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java similarity index 93% rename from client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndicesPrivileges.java rename to client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java index b36e4e39ca134..84596f4bd5499 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/IndicesPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.client.security.user; +package org.elasticsearch.client.security.user.privileges; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; @@ -49,20 +49,6 @@ public final class IndicesPrivileges implements ToXContentObject { public static final ParseField EXCEPT_FIELDS = new ParseField("except"); public static final ParseField QUERY = new ParseField("query"); - @SuppressWarnings("unchecked") - public static final ConstructingObjectParser, Collection>, Void> FLS_PARSER = - new ConstructingObjectParser<>( "field_level_parser", false, constructorObjects -> { - int i = 0; - final Collection grantFields = (Collection) constructorObjects[i++]; - final Collection exceptFields = (Collection) constructorObjects[i]; - return new Tuple<>(grantFields, exceptFields); - }); - - static { - FLS_PARSER.declareStringArray(optionalConstructorArg(), GRANT_FIELDS); - FLS_PARSER.declareStringArray(optionalConstructorArg(), EXCEPT_FIELDS); - } - @SuppressWarnings("unchecked") private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("indices_privileges", false, constructorObjects -> { @@ -78,9 +64,19 @@ public final class IndicesPrivileges implements ToXContentObject { }); static { + final ConstructingObjectParser, Collection>, Void> fls_parser = + new ConstructingObjectParser<>( "field_level_parser", false, constructorObjects -> { + int i = 0; + final Collection grantFields = (Collection) constructorObjects[i++]; + final Collection exceptFields = (Collection) constructorObjects[i]; + return new Tuple<>(grantFields, exceptFields); + }); + fls_parser.declareStringArray(optionalConstructorArg(), GRANT_FIELDS); + fls_parser.declareStringArray(optionalConstructorArg(), EXCEPT_FIELDS); + PARSER.declareStringArray(constructorArg(), NAMES); PARSER.declareStringArray(constructorArg(), PRIVILEGES); - PARSER.declareObject(optionalConstructorArg(), FLS_PARSER, FIELD_PERMISSIONS); + PARSER.declareObject(optionalConstructorArg(), fls_parser, FIELD_PERMISSIONS); PARSER.declareStringOrNull(optionalConstructorArg(), QUERY); } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivileges.java new file mode 100644 index 0000000000000..e75e44dcd0005 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivileges.java @@ -0,0 +1,120 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.security.user.privileges; + +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; + +public final class ManageApplicationPrivileges implements ToXContentObject { + + public static final ParseField CATEGORY = new ParseField("application"); + public static final ParseField SCOPE = new ParseField("manage"); + public static final ParseField APPLICATIONS = new ParseField("applications"); + + private static final ConstructingObjectParser PARSER = + new ConstructingObjectParser<>("application_privileges", false, constructorObjects -> { + return (ManageApplicationPrivileges) constructorObjects[0]; + }); + + static { + @SuppressWarnings("unchecked") + final ConstructingObjectParser apps_parser = + new ConstructingObjectParser<>("apps_parser", false, constructorObjects -> { + final Collection applications = (Collection) constructorObjects[0]; + return new ManageApplicationPrivileges(applications); + }); + apps_parser.declareStringArray(constructorArg(), APPLICATIONS); + + final ConstructingObjectParser scope_parser = + new ConstructingObjectParser<>("scope_parser", false, constructorObjects -> { + return (ManageApplicationPrivileges) constructorObjects[0]; + }); + scope_parser.declareObject(constructorArg(), apps_parser, SCOPE); + + PARSER.declareObject(constructorArg(), scope_parser, CATEGORY); + } + + private final Collection applications; + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.startObject(CATEGORY.getPreferredName()); + builder.startObject(SCOPE.getPreferredName()); + builder.field(APPLICATIONS.getPreferredName(), applications); + builder.endObject(); + builder.endObject(); + return builder.endObject(); + } + + public static ManageApplicationPrivileges fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } + + private ManageApplicationPrivileges(@Nullable Collection applications) { + // we do all null checks inside the constructor + if (null == applications || applications.isEmpty()) { + throw new IllegalArgumentException("managed applications list should not be null"); + } + this.applications = Collections.unmodifiableCollection(applications); + } + + public static class Builder { + + private @Nullable Set applications = null; + + private Builder() { + } + + public Builder applications(@Nullable String... applications) { + if (applications == null) { + // null is a no-op to be programmer friendly + return this; + } + return applications(Arrays.asList(applications)); + } + + public Builder applications(@Nullable Collection applications) { + if (applications == null) { + // null is a no-op to be programmer friendly + return this; + } + this.applications = new HashSet<>(applications); + return this; + } + + public ManageApplicationPrivileges build() { + return new ManageApplicationPrivileges(applications); + } + } +} From e03a05bbed1e9bcd47437202b2f955c46aaa3853 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Tue, 30 Oct 2018 18:52:46 +0200 Subject: [PATCH 08/26] Bare RoleDescriptor --- .../user/privileges/IndicesPrivileges.java | 15 ++-- .../user/privileges/RoleDescriptor.java | 68 +++++++++++++++++++ 2 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java index 84596f4bd5499..6745c80d9cf61 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java @@ -64,6 +64,7 @@ public final class IndicesPrivileges implements ToXContentObject { }); static { + @SuppressWarnings("unchecked") final ConstructingObjectParser, Collection>, Void> fls_parser = new ConstructingObjectParser<>( "field_level_parser", false, constructorObjects -> { int i = 0; @@ -98,18 +99,12 @@ private IndicesPrivileges(Collection indices, Collection if (null == privileges || privileges.isEmpty()) { throw new IllegalArgumentException("indices privileges must define at least one privilege"); } - if (grantedFields == null) { - // all fields granted unless otherwise specified - grantedFields = Collections.singleton("*"); - } - if (deniedFields == null) { - // no fields are denied unless otherwise specified - deniedFields = Collections.emptySet(); - } this.indices = Collections.unmodifiableCollection(indices); this.privileges = Collections.unmodifiableCollection(privileges); - this.grantedFields = Collections.unmodifiableCollection(grantedFields); - this.deniedFields = Collections.unmodifiableCollection(deniedFields); + // all fields granted unless otherwise specified + this.grantedFields = grantedFields != null ? Collections.unmodifiableCollection(grantedFields) : Collections.singleton("*"); + // no fields are denied unless otherwise specified + this.deniedFields = deniedFields != null ? Collections.unmodifiableCollection(deniedFields) : Collections.emptySet(); this.query = query; } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java new file mode 100644 index 0000000000000..04c9c1d450bd2 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java @@ -0,0 +1,68 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.security.user.privileges; + +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +public class RoleDescriptor implements ToXContentObject { + + private final String name; + private final Collection clusterPrivileges; + private final Collection manageApplicationPrivileges; + private final Collection indicesPrivileges; + private final Collection applicationResourcePrivileges; + private final Collection runAs; + private final Map metadata; + + private RoleDescriptor(String name, Collection clusterPrivileges, + Collection manageApplicationPrivileges, Collection indicesPrivileges, + Collection applicationResourcePrivileges, Collection runAs, + Map metadata) { + // we do all null checks inside the constructor + if (Strings.isNullOrEmpty(name)) { + throw new IllegalArgumentException("role descriptor must have a name"); + } + this.name = name; + // no cluster privileges are granted unless otherwise specified + this.clusterPrivileges = clusterPrivileges != null ? Collections.unmodifiableCollection(clusterPrivileges) : Collections.emptySet(); + // no manage application privileges are granted unless otherwise specified + this.manageApplicationPrivileges = manageApplicationPrivileges != null ? Collections.unmodifiableCollection(manageApplicationPrivileges) : Collections.emptySet(); + // no indices privileges are granted unless otherwise specified + this.indicesPrivileges = indicesPrivileges != null ? Collections.unmodifiableCollection(indicesPrivileges) : Collections.emptySet(); + // no application resource privileges are granted unless otherwise specified + this.applicationResourcePrivileges = applicationResourcePrivileges != null ? Collections.unmodifiableCollection(applicationResourcePrivileges) : Collections.emptySet(); + // no run as privileges are granted unless otherwise specified + this.runAs = runAs != null ? Collections.unmodifiableCollection(runAs) : Collections.emptySet(); + this.metadata = metadata != null ? Collections.unmodifiableMap(metadata) : Collections.emptyMap(); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return null; + } + +} From a7b176b63ece9c401bb5fab407bcf0d72aee2652 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Wed, 31 Oct 2018 09:00:38 +0200 Subject: [PATCH 09/26] Slightly less bare RoleDescriptor --- .../ApplicationResourcePrivileges.java | 27 ++++--- .../user/privileges/IndicesPrivileges.java | 48 ++++++------ ....java => ManageApplicationsPrivilege.java} | 65 ++++++++++++---- .../user/privileges/RoleDescriptor.java | 76 +++++++++++++++++-- 4 files changed, 155 insertions(+), 61 deletions(-) rename client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/{ManageApplicationPrivileges.java => ManageApplicationsPrivilege.java} (71%) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java index e0124b153871f..b01124b75f45a 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java @@ -79,10 +79,6 @@ private ApplicationResourcePrivileges(String application, Collection pri this.resources = Collections.unmodifiableCollection(resources); } - public static Builder builder() { - return new Builder(); - } - public String getApplication() { return application; } @@ -95,14 +91,6 @@ public Collection getPrivileges() { return this.privileges; } - @Override - public String toString() { - StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append("[application=").append(application) - .append(", privileges=[").append(Strings.collectionToCommaDelimitedString(privileges)).append("], resources=[") - .append(Strings.collectionToCommaDelimitedString(resources)).append("]]"); - return sb.toString(); - } - @Override public boolean equals(Object o) { if (this == o) { @@ -111,9 +99,7 @@ public boolean equals(Object o) { if (o == null || this.getClass() != o.getClass()) { return false; } - ApplicationResourcePrivileges that = (ApplicationResourcePrivileges) o; - return application.equals(that.application) && privileges.equals(that.privileges) && resources.equals(that.resources); } @@ -122,6 +108,15 @@ public int hashCode() { return Objects.hash(application, privileges, resources); } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append("["); + sb.append(APPLICATION.getPreferredName()).append("=[").append(application).append("], "); + sb.append(PRIVILEGES.getPreferredName()).append("=[").append(Strings.collectionToCommaDelimitedString(privileges)).append("], "); + sb.append(RESOURCES.getPreferredName()).append("=[").append(Strings.collectionToCommaDelimitedString(resources)).append("]]"); + return sb.toString(); + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); @@ -135,6 +130,10 @@ public static ApplicationResourcePrivileges fromXContent(XContentParser parser) return PARSER.apply(parser, null); } + public static Builder builder() { + return new Builder(); + } + public static class Builder { private @Nullable String application = null; diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java index 6745c80d9cf61..18db253af114a 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java @@ -90,8 +90,8 @@ public final class IndicesPrivileges implements ToXContentObject { // missing query means all documents, i.e. no restrictions private final @Nullable String query; - private IndicesPrivileges(Collection indices, Collection privileges, Collection grantedFields, - Collection deniedFields, @Nullable String query) { + private IndicesPrivileges(Collection indices, Collection privileges, @Nullable Collection grantedFields, + @Nullable Collection deniedFields, @Nullable String query) { // we do all null checks inside the constructor if (null == indices || indices.isEmpty()) { throw new IllegalArgumentException("indices privileges must refer to at least one index name or index name pattern"); @@ -108,10 +108,6 @@ private IndicesPrivileges(Collection indices, Collection this.query = query; } - public static Builder builder() { - return new Builder(); - } - public Collection getIndices() { return this.indices; } @@ -152,22 +148,6 @@ private boolean hasGrantedFields() { return true; } - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("IndicesPrivileges["); - sb.append(NAMES.getPreferredName()).append("=[").append(Strings.collectionToCommaDelimitedString(indices)).append("], "); - sb.append(PRIVILEGES.getPreferredName()).append("=[").append(Strings.collectionToCommaDelimitedString(privileges)).append("], "); - sb.append(FIELD_PERMISSIONS).append("=["); - sb.append(GRANT_FIELDS).append("=[").append(Strings.collectionToCommaDelimitedString(grantedFields)).append("], "); - sb.append(EXCEPT_FIELDS).append("=[").append(Strings.collectionToCommaDelimitedString(deniedFields)).append("]"); - sb.append("]"); - if (query != null) { - sb.append(", ").append(QUERY.getPreferredName()).append("=[").append(query).append("]"); - } - sb.append("]"); - return sb.toString(); - } - @Override public boolean equals(Object o) { if (this == o) { @@ -176,11 +156,9 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - IndicesPrivileges that = (IndicesPrivileges) o; - return indices.equals(that.indices) && privileges.equals(that.privileges) && grantedFields.equals(that.grantedFields) - && deniedFields.equals(that.deniedFields) && query.equals(that.query); + && deniedFields.equals(that.deniedFields) && Objects.equals(query, that.query); } @Override @@ -188,6 +166,22 @@ public int hashCode() { return Objects.hash(indices, privileges, grantedFields, deniedFields, query); } + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append("["); + sb.append(NAMES.getPreferredName()).append("=[").append(Strings.collectionToCommaDelimitedString(indices)).append("], "); + sb.append(PRIVILEGES.getPreferredName()).append("=[").append(Strings.collectionToCommaDelimitedString(privileges)).append("], "); + sb.append(FIELD_PERMISSIONS).append("=["); + sb.append(GRANT_FIELDS).append("=[").append(Strings.collectionToCommaDelimitedString(grantedFields)).append("], "); + sb.append(EXCEPT_FIELDS).append("=[").append(Strings.collectionToCommaDelimitedString(deniedFields)).append("]"); + sb.append("]"); + if (query != null) { + sb.append(", ").append(QUERY.getPreferredName()).append("=[").append(query).append("]"); + } + sb.append("]"); + return sb.toString(); + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); @@ -207,6 +201,10 @@ public static IndicesPrivileges fromXContent(XContentParser parser) { return PARSER.apply(parser, null); } + public static Builder builder() { + return new Builder(); + } + public static class Builder { private @Nullable Set indices = null; diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java similarity index 71% rename from client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivileges.java rename to client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java index e75e44dcd0005..cf9a4d187b658 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.xcontent.ConstructingObjectParser; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -35,29 +36,29 @@ import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; -public final class ManageApplicationPrivileges implements ToXContentObject { +public final class ManageApplicationsPrivilege implements ToXContentObject { public static final ParseField CATEGORY = new ParseField("application"); public static final ParseField SCOPE = new ParseField("manage"); public static final ParseField APPLICATIONS = new ParseField("applications"); - private static final ConstructingObjectParser PARSER = + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("application_privileges", false, constructorObjects -> { - return (ManageApplicationPrivileges) constructorObjects[0]; + return (ManageApplicationsPrivilege) constructorObjects[0]; }); static { @SuppressWarnings("unchecked") - final ConstructingObjectParser apps_parser = + final ConstructingObjectParser apps_parser = new ConstructingObjectParser<>("apps_parser", false, constructorObjects -> { final Collection applications = (Collection) constructorObjects[0]; - return new ManageApplicationPrivileges(applications); + return new ManageApplicationsPrivilege(applications); }); apps_parser.declareStringArray(constructorArg(), APPLICATIONS); - final ConstructingObjectParser scope_parser = + final ConstructingObjectParser scope_parser = new ConstructingObjectParser<>("scope_parser", false, constructorObjects -> { - return (ManageApplicationPrivileges) constructorObjects[0]; + return (ManageApplicationsPrivilege) constructorObjects[0]; }); scope_parser.declareObject(constructorArg(), apps_parser, SCOPE); @@ -66,6 +67,42 @@ public final class ManageApplicationPrivileges implements ToXContentObject { private final Collection applications; + private ManageApplicationsPrivilege(Collection applications) { + // we do all null checks inside the constructor + if (null == applications || applications.isEmpty()) { + throw new IllegalArgumentException("managed applications list should not be null"); + } + this.applications = Collections.unmodifiableCollection(applications); + } + + public Collection getApplications() { + return applications; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ManageApplicationsPrivilege that = (ManageApplicationsPrivilege) o; + return applications.equals(that.applications); + } + + @Override + public int hashCode() { + return applications.hashCode(); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ManageApplicationsPrivilege["); + sb.append("applications=[").append(Strings.collectionToCommaDelimitedString(applications)).append("]]"); + return sb.toString(); + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); @@ -77,16 +114,12 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder.endObject(); } - public static ManageApplicationPrivileges fromXContent(XContentParser parser) { + public static ManageApplicationsPrivilege fromXContent(XContentParser parser) { return PARSER.apply(parser, null); } - private ManageApplicationPrivileges(@Nullable Collection applications) { - // we do all null checks inside the constructor - if (null == applications || applications.isEmpty()) { - throw new IllegalArgumentException("managed applications list should not be null"); - } - this.applications = Collections.unmodifiableCollection(applications); + public static Builder builder() { + return new Builder(); } public static class Builder { @@ -113,8 +146,8 @@ public Builder applications(@Nullable Collection applications) { return this; } - public ManageApplicationPrivileges build() { - return new ManageApplicationPrivileges(applications); + public ManageApplicationsPrivilege build() { + return new ManageApplicationsPrivilege(applications); } } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java index 04c9c1d450bd2..1699e4433ee3e 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java @@ -27,19 +27,20 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; +import java.util.Objects; public class RoleDescriptor implements ToXContentObject { private final String name; private final Collection clusterPrivileges; - private final Collection manageApplicationPrivileges; + private final Collection manageApplicationPrivileges; private final Collection indicesPrivileges; private final Collection applicationResourcePrivileges; private final Collection runAs; private final Map metadata; - + private RoleDescriptor(String name, Collection clusterPrivileges, - Collection manageApplicationPrivileges, Collection indicesPrivileges, + Collection manageApplicationPrivileges, Collection indicesPrivileges, Collection applicationResourcePrivileges, Collection runAs, Map metadata) { // we do all null checks inside the constructor @@ -50,16 +51,79 @@ private RoleDescriptor(String name, Collection clusterPrivileg // no cluster privileges are granted unless otherwise specified this.clusterPrivileges = clusterPrivileges != null ? Collections.unmodifiableCollection(clusterPrivileges) : Collections.emptySet(); // no manage application privileges are granted unless otherwise specified - this.manageApplicationPrivileges = manageApplicationPrivileges != null ? Collections.unmodifiableCollection(manageApplicationPrivileges) : Collections.emptySet(); + this.manageApplicationPrivileges = manageApplicationPrivileges != null + ? Collections.unmodifiableCollection(manageApplicationPrivileges) + : Collections.emptySet(); // no indices privileges are granted unless otherwise specified this.indicesPrivileges = indicesPrivileges != null ? Collections.unmodifiableCollection(indicesPrivileges) : Collections.emptySet(); // no application resource privileges are granted unless otherwise specified - this.applicationResourcePrivileges = applicationResourcePrivileges != null ? Collections.unmodifiableCollection(applicationResourcePrivileges) : Collections.emptySet(); + this.applicationResourcePrivileges = applicationResourcePrivileges != null + ? Collections.unmodifiableCollection(applicationResourcePrivileges) + : Collections.emptySet(); // no run as privileges are granted unless otherwise specified this.runAs = runAs != null ? Collections.unmodifiableCollection(runAs) : Collections.emptySet(); this.metadata = metadata != null ? Collections.unmodifiableMap(metadata) : Collections.emptyMap(); } - + + public String getName() { + return name; + } + + public Collection getClusterPrivileges() { + return clusterPrivileges; + } + + public Collection getManageApplicationPrivileges() { + return manageApplicationPrivileges; + } + + public Collection getIndicesPrivileges() { + return indicesPrivileges; + } + + public Collection getApplicationResourcePrivileges() { + return applicationResourcePrivileges; + } + + public Collection getRunAs() { + return runAs; + } + + public Map getMetadata() { + return metadata; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RoleDescriptor that = (RoleDescriptor) o; + return name.equals(that.name) && clusterPrivileges.equals(that.clusterPrivileges) + && manageApplicationPrivileges.equals(that.manageApplicationPrivileges) && indicesPrivileges.equals(that.indicesPrivileges) + && applicationResourcePrivileges.equals(that.applicationResourcePrivileges) && runAs.equals(that.runAs) + && metadata.equals(that.metadata); + } + + @Override + public int hashCode() { + return Objects.hash(name, clusterPrivileges, manageApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, runAs, + metadata); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append("["); + sb.append("name=").append(name); + sb.append(", cluster=[").append(Strings.collectionToCommaDelimitedString(clusterPrivileges)); + sb.append("], global=[").append(Strings.collectionToCommaDelimitedString(manageApplicationPrivileges)); + sb.append("], indicesPrivileges=[").append(Strings.collectionToCommaDelimitedString(indicesPrivileges)); + sb.append("], applicationResourcePrivileges=[").append(Strings.collectionToCommaDelimitedString(applicationResourcePrivileges)); + sb.append("], runAs=[").append(Strings.collectionToCommaDelimitedString(runAs)); + sb.append("], metadata=[").append(metadata); + sb.append("]]"); + return sb.toString(); + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { return null; From 701f85ed8a74cde730f867dc74c33f8e5c71c13f Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Wed, 31 Oct 2018 09:28:45 +0200 Subject: [PATCH 10/26] All Collections are Lists, well.. --- .../ApplicationResourcePrivileges.java | 46 ++++------ .../user/privileges/IndicesPrivileges.java | 84 ++++++++----------- .../ManageApplicationsPrivilege.java | 29 +++---- 3 files changed, 63 insertions(+), 96 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java index b01124b75f45a..92541041240e5 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java @@ -29,11 +29,9 @@ import java.io.IOException; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; -import java.util.HashSet; +import java.util.List; import java.util.Objects; -import java.util.Set; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; @@ -48,8 +46,8 @@ public final class ApplicationResourcePrivileges implements ToXContentObject { "application_privileges", false, constructorObjects -> { int i = 0; final String application = (String) constructorObjects[i++]; - final Collection privileges = (Collection) constructorObjects[i++]; - final Collection resources = (Collection) constructorObjects[i]; + final List privileges = (List) constructorObjects[i++]; + final List resources = (List) constructorObjects[i]; return new ApplicationResourcePrivileges(application, privileges, resources); }); @@ -60,10 +58,10 @@ public final class ApplicationResourcePrivileges implements ToXContentObject { } private final String application; - private final Collection privileges; - private final Collection resources; + private final List privileges; + private final List resources; - private ApplicationResourcePrivileges(String application, Collection privileges, Collection resources) { + private ApplicationResourcePrivileges(String application, List privileges, List resources) { // we do all null checks inside the constructor if (Strings.isNullOrEmpty(application)) { throw new IllegalArgumentException("application privileges must have an application name"); @@ -75,19 +73,19 @@ private ApplicationResourcePrivileges(String application, Collection pri throw new IllegalArgumentException("application privileges must refer to at least one resource"); } this.application = application; - this.privileges = Collections.unmodifiableCollection(privileges); - this.resources = Collections.unmodifiableCollection(resources); + this.privileges = Collections.unmodifiableList(privileges); + this.resources = Collections.unmodifiableList(resources); } public String getApplication() { return application; } - public Collection getResources() { + public List getResources() { return this.resources; } - public Collection getPrivileges() { + public List getPrivileges() { return this.privileges; } @@ -137,17 +135,13 @@ public static Builder builder() { public static class Builder { private @Nullable String application = null; - private @Nullable Set privileges = null; - private @Nullable Set resources = null; + private @Nullable List privileges = null; + private @Nullable List resources = null; private Builder() { } public Builder application(@Nullable String appName) { - if (appName == null) { - // null is a no-op to be programmer friendly - return this; - } this.application = appName; return this; } @@ -160,12 +154,8 @@ public Builder resources(@Nullable String... resources) { return resources(Arrays.asList(resources)); } - public Builder resources(@Nullable Collection resources) { - if (resources == null) { - // null is a no-op to be programmer friendly - return this; - } - this.resources = new HashSet<>(resources); + public Builder resources(@Nullable List resources) { + this.resources = resources; return this; } @@ -177,12 +167,8 @@ public Builder privileges(@Nullable String... privileges) { return privileges(Arrays.asList(privileges)); } - public Builder privileges(@Nullable Collection privileges) { - if (privileges == null) { - // null is a no-op to be programmer friendly - return this; - } - this.privileges = new HashSet<>(privileges); + public Builder privileges(@Nullable List privileges) { + this.privileges = privileges; return this; } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java index 18db253af114a..c68092a161967 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java @@ -30,11 +30,9 @@ import java.io.IOException; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; -import java.util.HashSet; +import java.util.List; import java.util.Objects; -import java.util.Set; import java.util.stream.Collectors; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; @@ -53,23 +51,23 @@ public final class IndicesPrivileges implements ToXContentObject { private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("indices_privileges", false, constructorObjects -> { int i = 0; - final Collection indices = (Collection) constructorObjects[i++]; - final Collection privilegeNames = (Collection) constructorObjects[i++]; - final Collection privileges = privilegeNames.stream().map(IndexPrivilege::fromString) + final List indices = (List) constructorObjects[i++]; + final List privilegeNames = (List) constructorObjects[i++]; + final List privileges = privilegeNames.stream().map(IndexPrivilege::fromString) .collect(Collectors.toList()); - final Tuple, Collection> fields = - (Tuple, Collection>) constructorObjects[i++]; + final Tuple, List> fields = + (Tuple, List>) constructorObjects[i++]; final String query = (String) constructorObjects[i]; return new IndicesPrivileges(indices, privileges, fields.v1(), fields.v2(), query); }); static { @SuppressWarnings("unchecked") - final ConstructingObjectParser, Collection>, Void> fls_parser = + final ConstructingObjectParser, List>, Void> fls_parser = new ConstructingObjectParser<>( "field_level_parser", false, constructorObjects -> { int i = 0; - final Collection grantFields = (Collection) constructorObjects[i++]; - final Collection exceptFields = (Collection) constructorObjects[i]; + final List grantFields = (List) constructorObjects[i++]; + final List exceptFields = (List) constructorObjects[i]; return new Tuple<>(grantFields, exceptFields); }); fls_parser.declareStringArray(optionalConstructorArg(), GRANT_FIELDS); @@ -81,17 +79,17 @@ public final class IndicesPrivileges implements ToXContentObject { PARSER.declareStringOrNull(optionalConstructorArg(), QUERY); } - private final Collection indices; - private final Collection privileges; + private final List indices; + private final List privileges; // '*' means all fields (default value), empty means no fields - private final Collection grantedFields; + private final List grantedFields; // empty means no field is denied - private final Collection deniedFields; + private final List deniedFields; // missing query means all documents, i.e. no restrictions private final @Nullable String query; - private IndicesPrivileges(Collection indices, Collection privileges, @Nullable Collection grantedFields, - @Nullable Collection deniedFields, @Nullable String query) { + private IndicesPrivileges(List indices, List privileges, @Nullable List grantedFields, + @Nullable List deniedFields, @Nullable String query) { // we do all null checks inside the constructor if (null == indices || indices.isEmpty()) { throw new IllegalArgumentException("indices privileges must refer to at least one index name or index name pattern"); @@ -99,28 +97,28 @@ private IndicesPrivileges(Collection indices, Collection if (null == privileges || privileges.isEmpty()) { throw new IllegalArgumentException("indices privileges must define at least one privilege"); } - this.indices = Collections.unmodifiableCollection(indices); - this.privileges = Collections.unmodifiableCollection(privileges); + this.indices = Collections.unmodifiableList(indices); + this.privileges = Collections.unmodifiableList(privileges); // all fields granted unless otherwise specified - this.grantedFields = grantedFields != null ? Collections.unmodifiableCollection(grantedFields) : Collections.singleton("*"); + this.grantedFields = grantedFields != null ? Collections.unmodifiableList(grantedFields) : Collections.singletonList("*"); // no fields are denied unless otherwise specified - this.deniedFields = deniedFields != null ? Collections.unmodifiableCollection(deniedFields) : Collections.emptySet(); + this.deniedFields = deniedFields != null ? Collections.unmodifiableList(deniedFields) : Collections.emptyList(); this.query = query; } - public Collection getIndices() { + public List getIndices() { return this.indices; } - public Collection getPrivileges() { + public List getPrivileges() { return this.privileges; } - public Collection getGrantedFields() { + public List getGrantedFields() { return this.grantedFields; } - public Collection getDeniedFields() { + public List getDeniedFields() { return this.deniedFields; } @@ -207,10 +205,10 @@ public static Builder builder() { public static class Builder { - private @Nullable Set indices = null; - private @Nullable Set privileges = null; - private @Nullable Set grantedFields = null; - private @Nullable Set deniedFields = null; + private @Nullable List indices = null; + private @Nullable List privileges = null; + private @Nullable List grantedFields = null; + private @Nullable List deniedFields = null; private @Nullable String query = null; private Builder() { @@ -224,12 +222,8 @@ public Builder indices(@Nullable String... indices) { return indices(Arrays.asList(indices)); } - public Builder indices(@Nullable Collection indices) { - if (indices == null) { - // null is a no-op to be programmer friendly - return this; - } - this.indices = new HashSet<>(indices); + public Builder indices(@Nullable List indices) { + this.indices = indices; return this; } @@ -241,12 +235,8 @@ public Builder privileges(@Nullable IndexPrivilege... privileges) { return privileges(Arrays.asList(privileges)); } - public Builder privileges(@Nullable Collection privileges) { - if (privileges == null) { - // null is a no-op to be programmer friendly - return this; - } - this.privileges = new HashSet<>(privileges); + public Builder privileges(@Nullable List privileges) { + this.privileges = privileges; return this; } @@ -258,12 +248,8 @@ public Builder grantedFields(@Nullable String... grantedFields) { return grantedFields(Arrays.asList(grantedFields)); } - public Builder grantedFields(@Nullable Collection grantedFields) { - if (grantedFields == null) { - // null is a no-op to be programmer friendly - return this; - } - this.grantedFields = new HashSet<>(grantedFields); + public Builder grantedFields(@Nullable List grantedFields) { + this.grantedFields = grantedFields; return this; } @@ -275,12 +261,12 @@ public Builder deniedFields(@Nullable String... deniedFields) { return deniedFields(Arrays.asList(deniedFields)); } - public Builder deniedFields(@Nullable Collection deniedFields) { + public Builder deniedFields(@Nullable List deniedFields) { if (deniedFields == null) { // null is a no-op to be programmer friendly return this; } - this.deniedFields = new HashSet<>(deniedFields); + this.deniedFields = deniedFields; return this; } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java index cf9a4d187b658..b7ed0ac184339 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java @@ -29,10 +29,8 @@ import java.io.IOException; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; -import java.util.HashSet; -import java.util.Set; +import java.util.List; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; @@ -51,7 +49,7 @@ public final class ManageApplicationsPrivilege implements ToXContentObject { @SuppressWarnings("unchecked") final ConstructingObjectParser apps_parser = new ConstructingObjectParser<>("apps_parser", false, constructorObjects -> { - final Collection applications = (Collection) constructorObjects[0]; + final List applications = (List) constructorObjects[0]; return new ManageApplicationsPrivilege(applications); }); apps_parser.declareStringArray(constructorArg(), APPLICATIONS); @@ -65,17 +63,17 @@ public final class ManageApplicationsPrivilege implements ToXContentObject { PARSER.declareObject(constructorArg(), scope_parser, CATEGORY); } - private final Collection applications; + private final List applications; - private ManageApplicationsPrivilege(Collection applications) { + private ManageApplicationsPrivilege(List applications) { // we do all null checks inside the constructor if (null == applications || applications.isEmpty()) { throw new IllegalArgumentException("managed applications list should not be null"); } - this.applications = Collections.unmodifiableCollection(applications); + this.applications = Collections.unmodifiableList(applications); } - public Collection getApplications() { + public List getApplications() { return applications; } @@ -98,8 +96,9 @@ public int hashCode() { @Override public String toString() { - final StringBuilder sb = new StringBuilder("ManageApplicationsPrivilege["); - sb.append("applications=[").append(Strings.collectionToCommaDelimitedString(applications)).append("]]"); + final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append("["); + sb.append(APPLICATIONS.getPreferredName()).append("=[").append(Strings.collectionToCommaDelimitedString(applications)); + sb.append("]]"); return sb.toString(); } @@ -124,7 +123,7 @@ public static Builder builder() { public static class Builder { - private @Nullable Set applications = null; + private @Nullable List applications = null; private Builder() { } @@ -137,12 +136,8 @@ public Builder applications(@Nullable String... applications) { return applications(Arrays.asList(applications)); } - public Builder applications(@Nullable Collection applications) { - if (applications == null) { - // null is a no-op to be programmer friendly - return this; - } - this.applications = new HashSet<>(applications); + public Builder applications(@Nullable List applications) { + this.applications = applications; return this; } From 523f108ec06f2987a8314a1a5078477c28f947dd Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Wed, 31 Oct 2018 10:54:48 +0200 Subject: [PATCH 11/26] WIP :(( --- .../ApplicationResourcePrivileges.java | 34 ++++---- .../user/privileges/IndicesPrivileges.java | 84 ++++++++++++------- .../ManageApplicationsPrivilege.java | 21 +++-- .../user/privileges/RoleDescriptor.java | 42 +++++----- 4 files changed, 106 insertions(+), 75 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java index 92541041240e5..b3e78445de669 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java @@ -29,9 +29,12 @@ import java.io.IOException; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; -import java.util.List; import java.util.Objects; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; @@ -46,8 +49,8 @@ public final class ApplicationResourcePrivileges implements ToXContentObject { "application_privileges", false, constructorObjects -> { int i = 0; final String application = (String) constructorObjects[i++]; - final List privileges = (List) constructorObjects[i++]; - final List resources = (List) constructorObjects[i]; + final Collection privileges = (Collection) constructorObjects[i++]; + final Collection resources = (Collection) constructorObjects[i]; return new ApplicationResourcePrivileges(application, privileges, resources); }); @@ -58,10 +61,11 @@ public final class ApplicationResourcePrivileges implements ToXContentObject { } private final String application; - private final List privileges; - private final List resources; + // uniqueness and order are important for equals and hashcode + private final SortedSet privileges; + private final SortedSet resources; - private ApplicationResourcePrivileges(String application, List privileges, List resources) { + private ApplicationResourcePrivileges(String application, Collection privileges, Collection resources) { // we do all null checks inside the constructor if (Strings.isNullOrEmpty(application)) { throw new IllegalArgumentException("application privileges must have an application name"); @@ -73,19 +77,19 @@ private ApplicationResourcePrivileges(String application, List privilege throw new IllegalArgumentException("application privileges must refer to at least one resource"); } this.application = application; - this.privileges = Collections.unmodifiableList(privileges); - this.resources = Collections.unmodifiableList(resources); + this.privileges = Collections.unmodifiableSortedSet(new TreeSet<>(privileges)); + this.resources = Collections.unmodifiableSortedSet(new TreeSet<>(resources)); } public String getApplication() { return application; } - public List getResources() { + public Set getResources() { return this.resources; } - public List getPrivileges() { + public Set getPrivileges() { return this.privileges; } @@ -132,11 +136,11 @@ public static Builder builder() { return new Builder(); } - public static class Builder { + public static final class Builder { private @Nullable String application = null; - private @Nullable List privileges = null; - private @Nullable List resources = null; + private @Nullable Collection privileges = null; + private @Nullable Collection resources = null; private Builder() { } @@ -154,7 +158,7 @@ public Builder resources(@Nullable String... resources) { return resources(Arrays.asList(resources)); } - public Builder resources(@Nullable List resources) { + public Builder resources(@Nullable Collection resources) { this.resources = resources; return this; } @@ -167,7 +171,7 @@ public Builder privileges(@Nullable String... privileges) { return privileges(Arrays.asList(privileges)); } - public Builder privileges(@Nullable List privileges) { + public Builder privileges(@Nullable Collection privileges) { this.privileges = privileges; return this; } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java index c68092a161967..a3894cb5b7a72 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java @@ -30,9 +30,12 @@ import java.io.IOException; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; -import java.util.List; +import java.util.Comparator; import java.util.Objects; +import java.util.SortedSet; +import java.util.TreeSet; import java.util.stream.Collectors; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; @@ -51,23 +54,23 @@ public final class IndicesPrivileges implements ToXContentObject { private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("indices_privileges", false, constructorObjects -> { int i = 0; - final List indices = (List) constructorObjects[i++]; - final List privilegeNames = (List) constructorObjects[i++]; - final List privileges = privilegeNames.stream().map(IndexPrivilege::fromString) + final Collection indices = (Collection) constructorObjects[i++]; + final Collection privilegeNames = (Collection) constructorObjects[i++]; + final Collection privileges = privilegeNames.stream().map(IndexPrivilege::fromString) .collect(Collectors.toList()); - final Tuple, List> fields = - (Tuple, List>) constructorObjects[i++]; + final Tuple, Collection> fields = + (Tuple, Collection>) constructorObjects[i++]; final String query = (String) constructorObjects[i]; return new IndicesPrivileges(indices, privileges, fields.v1(), fields.v2(), query); }); static { @SuppressWarnings("unchecked") - final ConstructingObjectParser, List>, Void> fls_parser = + final ConstructingObjectParser, Collection>, Void> fls_parser = new ConstructingObjectParser<>( "field_level_parser", false, constructorObjects -> { int i = 0; - final List grantFields = (List) constructorObjects[i++]; - final List exceptFields = (List) constructorObjects[i]; + final Collection grantFields = (Collection) constructorObjects[i++]; + final Collection exceptFields = (Collection) constructorObjects[i]; return new Tuple<>(grantFields, exceptFields); }); fls_parser.declareStringArray(optionalConstructorArg(), GRANT_FIELDS); @@ -79,17 +82,18 @@ public final class IndicesPrivileges implements ToXContentObject { PARSER.declareStringOrNull(optionalConstructorArg(), QUERY); } - private final List indices; - private final List privileges; + // uniqueness and order are important for equals and hashcode + private final SortedSet indices; + private final SortedSet privileges; // '*' means all fields (default value), empty means no fields - private final List grantedFields; + private final SortedSet grantedFields; // empty means no field is denied - private final List deniedFields; + private final SortedSet deniedFields; // missing query means all documents, i.e. no restrictions private final @Nullable String query; - private IndicesPrivileges(List indices, List privileges, @Nullable List grantedFields, - @Nullable List deniedFields, @Nullable String query) { + private IndicesPrivileges(Collection indices, Collection privileges, @Nullable Collection grantedFields, + @Nullable Collection deniedFields, @Nullable String query) { // we do all null checks inside the constructor if (null == indices || indices.isEmpty()) { throw new IllegalArgumentException("indices privileges must refer to at least one index name or index name pattern"); @@ -97,28 +101,30 @@ private IndicesPrivileges(List indices, List privileges, if (null == privileges || privileges.isEmpty()) { throw new IllegalArgumentException("indices privileges must define at least one privilege"); } - this.indices = Collections.unmodifiableList(indices); - this.privileges = Collections.unmodifiableList(privileges); + this.indices = Collections.unmodifiableSortedSet(new TreeSet<>(indices)); + this.privileges = Collections.unmodifiableSortedSet(new TreeSet<>(privileges)); // all fields granted unless otherwise specified - this.grantedFields = grantedFields != null ? Collections.unmodifiableList(grantedFields) : Collections.singletonList("*"); + this.grantedFields = Collections + .unmodifiableSortedSet(new TreeSet<>(grantedFields != null ? grantedFields : Collections.singletonList("*"))); // no fields are denied unless otherwise specified - this.deniedFields = deniedFields != null ? Collections.unmodifiableList(deniedFields) : Collections.emptyList(); + this.deniedFields = Collections + .unmodifiableSortedSet(new TreeSet<>(deniedFields != null ? deniedFields : Collections.emptyList())); this.query = query; } - public List getIndices() { + public SortedSet getIndices() { return this.indices; } - public List getPrivileges() { + public SortedSet getPrivileges() { return this.privileges; } - public List getGrantedFields() { + public SortedSet getGrantedFields() { return this.grantedFields; } - public List getDeniedFields() { + public SortedSet getDeniedFields() { return this.deniedFields; } @@ -203,12 +209,12 @@ public static Builder builder() { return new Builder(); } - public static class Builder { + public static final class Builder { - private @Nullable List indices = null; - private @Nullable List privileges = null; - private @Nullable List grantedFields = null; - private @Nullable List deniedFields = null; + private @Nullable Collection indices = null; + private @Nullable Collection privileges = null; + private @Nullable Collection grantedFields = null; + private @Nullable Collection deniedFields = null; private @Nullable String query = null; private Builder() { @@ -222,7 +228,7 @@ public Builder indices(@Nullable String... indices) { return indices(Arrays.asList(indices)); } - public Builder indices(@Nullable List indices) { + public Builder indices(@Nullable Collection indices) { this.indices = indices; return this; } @@ -235,7 +241,7 @@ public Builder privileges(@Nullable IndexPrivilege... privileges) { return privileges(Arrays.asList(privileges)); } - public Builder privileges(@Nullable List privileges) { + public Builder privileges(@Nullable Collection privileges) { this.privileges = privileges; return this; } @@ -248,7 +254,7 @@ public Builder grantedFields(@Nullable String... grantedFields) { return grantedFields(Arrays.asList(grantedFields)); } - public Builder grantedFields(@Nullable List grantedFields) { + public Builder grantedFields(@Nullable Collection grantedFields) { this.grantedFields = grantedFields; return this; } @@ -261,7 +267,7 @@ public Builder deniedFields(@Nullable String... deniedFields) { return deniedFields(Arrays.asList(deniedFields)); } - public Builder deniedFields(@Nullable List deniedFields) { + public Builder deniedFields(@Nullable Collection deniedFields) { if (deniedFields == null) { // null is a no-op to be programmer friendly return this; @@ -280,4 +286,18 @@ public IndicesPrivileges build() { } } + private static final class AlphaNumericComparator implements Comparator { + + static final AlphaNumericComparator INSTANCE = new AlphaNumericComparator(); + + private AlphaNumericComparator() { + } + + @Override + public int compare(IndexPrivilege o1, IndexPrivilege o2) { + return o1.toString().compareTo(o2.toString()); + } + + } + } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java index b7ed0ac184339..daa2616b51455 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java @@ -29,8 +29,10 @@ import java.io.IOException; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; -import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; @@ -49,7 +51,7 @@ public final class ManageApplicationsPrivilege implements ToXContentObject { @SuppressWarnings("unchecked") final ConstructingObjectParser apps_parser = new ConstructingObjectParser<>("apps_parser", false, constructorObjects -> { - final List applications = (List) constructorObjects[0]; + final Collection applications = (Collection) constructorObjects[0]; return new ManageApplicationsPrivilege(applications); }); apps_parser.declareStringArray(constructorArg(), APPLICATIONS); @@ -63,17 +65,18 @@ public final class ManageApplicationsPrivilege implements ToXContentObject { PARSER.declareObject(constructorArg(), scope_parser, CATEGORY); } - private final List applications; + // uniqueness and order are important for equals and hashcode + private final SortedSet applications; - private ManageApplicationsPrivilege(List applications) { + private ManageApplicationsPrivilege(Collection applications) { // we do all null checks inside the constructor if (null == applications || applications.isEmpty()) { throw new IllegalArgumentException("managed applications list should not be null"); } - this.applications = Collections.unmodifiableList(applications); + this.applications = Collections.unmodifiableSortedSet(new TreeSet<>(applications)); } - public List getApplications() { + public SortedSet getApplications() { return applications; } @@ -121,9 +124,9 @@ public static Builder builder() { return new Builder(); } - public static class Builder { + public static final class Builder { - private @Nullable List applications = null; + private @Nullable Collection applications = null; private Builder() { } @@ -136,7 +139,7 @@ public Builder applications(@Nullable String... applications) { return applications(Arrays.asList(applications)); } - public Builder applications(@Nullable List applications) { + public Builder applications(@Nullable Collection applications) { this.applications = applications; return this; } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java index 1699e4433ee3e..afda34176a86b 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java @@ -28,15 +28,19 @@ import java.util.Collections; import java.util.Map; import java.util.Objects; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; public class RoleDescriptor implements ToXContentObject { private final String name; - private final Collection clusterPrivileges; - private final Collection manageApplicationPrivileges; - private final Collection indicesPrivileges; - private final Collection applicationResourcePrivileges; - private final Collection runAs; + // uniqueness and order are important for equals and hashcode + private final SortedSet clusterPrivileges; + private final SortedSet manageApplicationPrivileges; + private final SortedSet indicesPrivileges; + private final SortedSet applicationResourcePrivileges; + private final SortedSet runAs; private final Map metadata; private RoleDescriptor(String name, Collection clusterPrivileges, @@ -49,19 +53,19 @@ private RoleDescriptor(String name, Collection clusterPrivileg } this.name = name; // no cluster privileges are granted unless otherwise specified - this.clusterPrivileges = clusterPrivileges != null ? Collections.unmodifiableCollection(clusterPrivileges) : Collections.emptySet(); + this.clusterPrivileges = Collections + .unmodifiableSortedSet(new TreeSet<>(clusterPrivileges != null ? clusterPrivileges : Collections.emptyList())); // no manage application privileges are granted unless otherwise specified - this.manageApplicationPrivileges = manageApplicationPrivileges != null - ? Collections.unmodifiableCollection(manageApplicationPrivileges) - : Collections.emptySet(); + this.manageApplicationPrivileges = Collections.unmodifiableSortedSet( + new TreeSet<>(manageApplicationPrivileges != null ? manageApplicationPrivileges : Collections.emptyList())); // no indices privileges are granted unless otherwise specified - this.indicesPrivileges = indicesPrivileges != null ? Collections.unmodifiableCollection(indicesPrivileges) : Collections.emptySet(); + this.indicesPrivileges = Collections + .unmodifiableSortedSet(new TreeSet<>(indicesPrivileges != null ? indicesPrivileges : Collections.emptyList())); // no application resource privileges are granted unless otherwise specified - this.applicationResourcePrivileges = applicationResourcePrivileges != null - ? Collections.unmodifiableCollection(applicationResourcePrivileges) - : Collections.emptySet(); + this.applicationResourcePrivileges = Collections.unmodifiableSortedSet( + new TreeSet<>(applicationResourcePrivileges != null ? applicationResourcePrivileges : Collections.emptyList())); // no run as privileges are granted unless otherwise specified - this.runAs = runAs != null ? Collections.unmodifiableCollection(runAs) : Collections.emptySet(); + this.runAs = Collections.unmodifiableSortedSet(new TreeSet<>(runAs != null ? runAs : Collections.emptyList())); this.metadata = metadata != null ? Collections.unmodifiableMap(metadata) : Collections.emptyMap(); } @@ -69,23 +73,23 @@ public String getName() { return name; } - public Collection getClusterPrivileges() { + public Set getClusterPrivileges() { return clusterPrivileges; } - public Collection getManageApplicationPrivileges() { + public Set getManageApplicationPrivileges() { return manageApplicationPrivileges; } - public Collection getIndicesPrivileges() { + public Set getIndicesPrivileges() { return indicesPrivileges; } - public Collection getApplicationResourcePrivileges() { + public Set getApplicationResourcePrivileges() { return applicationResourcePrivileges; } - public Collection getRunAs() { + public SortedSet getRunAs() { return runAs; } From ee37904078a334dedfee8629e2b75e00f4285359 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Wed, 31 Oct 2018 12:22:35 +0200 Subject: [PATCH 12/26] Fields are Set --- .../ApplicationResourcePrivileges.java | 12 +++-- .../user/privileges/IndicesPrivileges.java | 44 ++++++------------- .../ManageApplicationsPrivilege.java | 11 +++-- .../user/privileges/RoleDescriptor.java | 29 ++++++------ 4 files changed, 38 insertions(+), 58 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java index b3e78445de669..80c06d0e54e11 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java @@ -31,10 +31,9 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.Objects; import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; @@ -61,9 +60,8 @@ public final class ApplicationResourcePrivileges implements ToXContentObject { } private final String application; - // uniqueness and order are important for equals and hashcode - private final SortedSet privileges; - private final SortedSet resources; + private final Set privileges; + private final Set resources; private ApplicationResourcePrivileges(String application, Collection privileges, Collection resources) { // we do all null checks inside the constructor @@ -77,8 +75,8 @@ private ApplicationResourcePrivileges(String application, Collection pri throw new IllegalArgumentException("application privileges must refer to at least one resource"); } this.application = application; - this.privileges = Collections.unmodifiableSortedSet(new TreeSet<>(privileges)); - this.resources = Collections.unmodifiableSortedSet(new TreeSet<>(resources)); + this.privileges = Collections.unmodifiableSet(new HashSet<>(privileges)); + this.resources = Collections.unmodifiableSet(new HashSet<>(resources)); } public String getApplication() { diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java index a3894cb5b7a72..6b795025333fe 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java @@ -32,10 +32,9 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; +import java.util.HashSet; import java.util.Objects; -import java.util.SortedSet; -import java.util.TreeSet; +import java.util.Set; import java.util.stream.Collectors; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; @@ -82,13 +81,12 @@ public final class IndicesPrivileges implements ToXContentObject { PARSER.declareStringOrNull(optionalConstructorArg(), QUERY); } - // uniqueness and order are important for equals and hashcode - private final SortedSet indices; - private final SortedSet privileges; + private final Set indices; + private final Set privileges; // '*' means all fields (default value), empty means no fields - private final SortedSet grantedFields; + private final Set grantedFields; // empty means no field is denied - private final SortedSet deniedFields; + private final Set deniedFields; // missing query means all documents, i.e. no restrictions private final @Nullable String query; @@ -101,30 +99,30 @@ private IndicesPrivileges(Collection indices, Collection if (null == privileges || privileges.isEmpty()) { throw new IllegalArgumentException("indices privileges must define at least one privilege"); } - this.indices = Collections.unmodifiableSortedSet(new TreeSet<>(indices)); - this.privileges = Collections.unmodifiableSortedSet(new TreeSet<>(privileges)); + this.indices = Collections.unmodifiableSet(new HashSet<>(indices)); + this.privileges = Collections.unmodifiableSet(new HashSet<>(privileges)); // all fields granted unless otherwise specified this.grantedFields = Collections - .unmodifiableSortedSet(new TreeSet<>(grantedFields != null ? grantedFields : Collections.singletonList("*"))); + .unmodifiableSet(grantedFields != null ? new HashSet<>(grantedFields) : Collections.singleton("*")); // no fields are denied unless otherwise specified this.deniedFields = Collections - .unmodifiableSortedSet(new TreeSet<>(deniedFields != null ? deniedFields : Collections.emptyList())); + .unmodifiableSet(deniedFields != null ? new HashSet<>(deniedFields) : Collections.emptySet()); this.query = query; } - public SortedSet getIndices() { + public Set getIndices() { return this.indices; } - public SortedSet getPrivileges() { + public Set getPrivileges() { return this.privileges; } - public SortedSet getGrantedFields() { + public Set getGrantedFields() { return this.grantedFields; } - public SortedSet getDeniedFields() { + public Set getDeniedFields() { return this.deniedFields; } @@ -286,18 +284,4 @@ public IndicesPrivileges build() { } } - private static final class AlphaNumericComparator implements Comparator { - - static final AlphaNumericComparator INSTANCE = new AlphaNumericComparator(); - - private AlphaNumericComparator() { - } - - @Override - public int compare(IndexPrivilege o1, IndexPrivilege o2) { - return o1.toString().compareTo(o2.toString()); - } - - } - } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java index daa2616b51455..eea288bfcd4cb 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java @@ -31,8 +31,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.SortedSet; -import java.util.TreeSet; +import java.util.HashSet; +import java.util.Set; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; @@ -65,18 +65,17 @@ public final class ManageApplicationsPrivilege implements ToXContentObject { PARSER.declareObject(constructorArg(), scope_parser, CATEGORY); } - // uniqueness and order are important for equals and hashcode - private final SortedSet applications; + private final Set applications; private ManageApplicationsPrivilege(Collection applications) { // we do all null checks inside the constructor if (null == applications || applications.isEmpty()) { throw new IllegalArgumentException("managed applications list should not be null"); } - this.applications = Collections.unmodifiableSortedSet(new TreeSet<>(applications)); + this.applications = Collections.unmodifiableSet(new HashSet<>(applications)); } - public SortedSet getApplications() { + public Set getApplications() { return applications; } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java index afda34176a86b..ca8c91bc41b7a 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java @@ -26,21 +26,20 @@ import java.io.IOException; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; public class RoleDescriptor implements ToXContentObject { private final String name; // uniqueness and order are important for equals and hashcode - private final SortedSet clusterPrivileges; - private final SortedSet manageApplicationPrivileges; - private final SortedSet indicesPrivileges; - private final SortedSet applicationResourcePrivileges; - private final SortedSet runAs; + private final Set clusterPrivileges; + private final Set manageApplicationPrivileges; + private final Set indicesPrivileges; + private final Set applicationResourcePrivileges; + private final Set runAs; private final Map metadata; private RoleDescriptor(String name, Collection clusterPrivileges, @@ -54,18 +53,18 @@ private RoleDescriptor(String name, Collection clusterPrivileg this.name = name; // no cluster privileges are granted unless otherwise specified this.clusterPrivileges = Collections - .unmodifiableSortedSet(new TreeSet<>(clusterPrivileges != null ? clusterPrivileges : Collections.emptyList())); + .unmodifiableSet(clusterPrivileges != null ? new HashSet<>(clusterPrivileges) : Collections.emptySet()); // no manage application privileges are granted unless otherwise specified - this.manageApplicationPrivileges = Collections.unmodifiableSortedSet( - new TreeSet<>(manageApplicationPrivileges != null ? manageApplicationPrivileges : Collections.emptyList())); + this.manageApplicationPrivileges = Collections + .unmodifiableSet(manageApplicationPrivileges != null ? new HashSet<>(manageApplicationPrivileges) : Collections.emptySet()); // no indices privileges are granted unless otherwise specified this.indicesPrivileges = Collections - .unmodifiableSortedSet(new TreeSet<>(indicesPrivileges != null ? indicesPrivileges : Collections.emptyList())); + .unmodifiableSet(indicesPrivileges != null ? new HashSet<>(indicesPrivileges) : Collections.emptySet()); // no application resource privileges are granted unless otherwise specified - this.applicationResourcePrivileges = Collections.unmodifiableSortedSet( - new TreeSet<>(applicationResourcePrivileges != null ? applicationResourcePrivileges : Collections.emptyList())); + this.applicationResourcePrivileges = Collections.unmodifiableSet( + applicationResourcePrivileges != null ? new HashSet<>(applicationResourcePrivileges) : Collections.emptySet()); // no run as privileges are granted unless otherwise specified - this.runAs = Collections.unmodifiableSortedSet(new TreeSet<>(runAs != null ? runAs : Collections.emptyList())); + this.runAs = Collections.unmodifiableSet(runAs != null ? new HashSet<>(runAs) : Collections.emptySet()); this.metadata = metadata != null ? Collections.unmodifiableMap(metadata) : Collections.emptyMap(); } @@ -89,7 +88,7 @@ public Set getApplicationResourcePrivileges() { return applicationResourcePrivileges; } - public SortedSet getRunAs() { + public Set getRunAs() { return runAs; } From 36f7be5bfbcc88ffa9a90e009d50f6585eaf5435 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Wed, 31 Oct 2018 14:22:43 +0200 Subject: [PATCH 13/26] Role entity --- .../ApplicationResourcePrivileges.java | 2 +- .../user/privileges/IndicesPrivileges.java | 2 +- .../ManageApplicationsPrivilege.java | 2 +- .../client/security/user/privileges/Role.java | 271 ++++++++++++++++++ .../user/privileges/RoleDescriptor.java | 135 --------- 5 files changed, 274 insertions(+), 138 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java delete mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java index 80c06d0e54e11..80f8f799bc2cc 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java @@ -44,7 +44,7 @@ public final class ApplicationResourcePrivileges implements ToXContentObject { private static final ParseField RESOURCES = new ParseField("resources"); @SuppressWarnings("unchecked") - private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( "application_privileges", false, constructorObjects -> { int i = 0; final String application = (String) constructorObjects[i++]; diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java index 6b795025333fe..adeeecd86d46d 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java @@ -50,7 +50,7 @@ public final class IndicesPrivileges implements ToXContentObject { public static final ParseField QUERY = new ParseField("query"); @SuppressWarnings("unchecked") - private static final ConstructingObjectParser PARSER = + static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("indices_privileges", false, constructorObjects -> { int i = 0; final Collection indices = (Collection) constructorObjects[i++]; diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java index eea288bfcd4cb..b5900af64f121 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java @@ -42,7 +42,7 @@ public final class ManageApplicationsPrivilege implements ToXContentObject { public static final ParseField SCOPE = new ParseField("manage"); public static final ParseField APPLICATIONS = new ParseField("applications"); - private static final ConstructingObjectParser PARSER = + static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("application_privileges", false, constructorObjects -> { return (ManageApplicationsPrivilege) constructorObjects[0]; }); diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java new file mode 100644 index 0000000000000..de287e76c546e --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java @@ -0,0 +1,271 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.security.user.privileges; + +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.ObjectParser.ValueType; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; + +public final class Role implements ToXContentObject { + + public static final ParseField CLUSTER = new ParseField("cluster"); + public static final ParseField GLOBAL = new ParseField("global"); + public static final ParseField INDICES = new ParseField("indices"); + public static final ParseField APPLICATIONS = new ParseField("applications"); + public static final ParseField RUN_AS = new ParseField("run_as"); + public static final ParseField METADATA = new ParseField("metadata"); + + @SuppressWarnings("unchecked") + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("role_descriptor", false, + constructorObjects -> { + int i = 0; + final Collection clusterPrivileges = (Collection) constructorObjects[i++]; + final ManageApplicationsPrivilege manageApplicationPrivileges = (ManageApplicationsPrivilege) constructorObjects[i++]; + final Collection indicesPrivileges = (Collection) constructorObjects[i++]; + final Collection applicationResourcePrivileges = + (Collection) constructorObjects[i++]; + final Collection runAs = (Collection) constructorObjects[i++]; + final Map metadata = (Map) constructorObjects[i]; + return new Role(clusterPrivileges, manageApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, + runAs, metadata); + }); + + static { + PARSER.declareFieldArray(optionalConstructorArg(), (p, c) -> ClusterPrivilege.fromString(p.text()), CLUSTER, + ValueType.STRING_ARRAY); + PARSER.declareObject(optionalConstructorArg(), ManageApplicationsPrivilege.PARSER, GLOBAL); + PARSER.declareFieldArray(optionalConstructorArg(), IndicesPrivileges.PARSER, INDICES, ValueType.OBJECT_ARRAY); + PARSER.declareFieldArray(optionalConstructorArg(), ApplicationResourcePrivileges.PARSER, APPLICATIONS, ValueType.OBJECT_ARRAY); + PARSER.declareStringArray(optionalConstructorArg(), RUN_AS); + PARSER.>declareObject(constructorArg(), (parser, c) -> parser.map(), METADATA); + } + + private final Set clusterPrivileges; + private final @Nullable ManageApplicationsPrivilege manageApplicationPrivileges; + private final Set indicesPrivileges; + private final Set applicationResourcePrivileges; + private final Set runAs; + private final Map metadata; + + private Role(Collection clusterPrivileges, + @Nullable ManageApplicationsPrivilege manageApplicationPrivileges, Collection indicesPrivileges, + Collection applicationResourcePrivileges, Collection runAs, + Map metadata) { + // we do all null checks inside the constructor + // no cluster privileges are granted unless otherwise specified + this.clusterPrivileges = Collections + .unmodifiableSet(clusterPrivileges != null ? new HashSet<>(clusterPrivileges) : Collections.emptySet()); + this.manageApplicationPrivileges = manageApplicationPrivileges; + // no indices privileges are granted unless otherwise specified + this.indicesPrivileges = Collections + .unmodifiableSet(indicesPrivileges != null ? new HashSet<>(indicesPrivileges) : Collections.emptySet()); + // no application resource privileges are granted unless otherwise specified + this.applicationResourcePrivileges = Collections.unmodifiableSet( + applicationResourcePrivileges != null ? new HashSet<>(applicationResourcePrivileges) : Collections.emptySet()); + // no run as privileges are granted unless otherwise specified + this.runAs = Collections.unmodifiableSet(runAs != null ? new HashSet<>(runAs) : Collections.emptySet()); + this.metadata = metadata != null ? Collections.unmodifiableMap(metadata) : Collections.emptyMap(); + } + + public Set getClusterPrivileges() { + return clusterPrivileges; + } + + public ManageApplicationsPrivilege getManageApplicationPrivileges() { + return manageApplicationPrivileges; + } + + public Set getIndicesPrivileges() { + return indicesPrivileges; + } + + public Set getApplicationResourcePrivileges() { + return applicationResourcePrivileges; + } + + public Set getRunAs() { + return runAs; + } + + public Map getMetadata() { + return metadata; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Role that = (Role) o; + return clusterPrivileges.equals(that.clusterPrivileges) + && Objects.equals(manageApplicationPrivileges, that.manageApplicationPrivileges) + && indicesPrivileges.equals(that.indicesPrivileges) + && applicationResourcePrivileges.equals(that.applicationResourcePrivileges) && runAs.equals(that.runAs) + && metadata.equals(that.metadata); + } + + @Override + public int hashCode() { + return Objects.hash(clusterPrivileges, manageApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, runAs, + metadata); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append("["); + sb.append("cluster=[").append(Strings.collectionToCommaDelimitedString(clusterPrivileges)); + sb.append("], global[").append(manageApplicationPrivileges); + sb.append("], indicesPrivileges=[").append(Strings.collectionToCommaDelimitedString(indicesPrivileges)); + sb.append("], applicationResourcePrivileges=[").append(Strings.collectionToCommaDelimitedString(applicationResourcePrivileges)); + sb.append("], runAs=[").append(Strings.collectionToCommaDelimitedString(runAs)); + sb.append("], metadata=[").append(metadata); + sb.append("]]"); + return sb.toString(); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + if (false == clusterPrivileges.isEmpty()) { + builder.field(CLUSTER.getPreferredName(), clusterPrivileges); + } + if (null != manageApplicationPrivileges) { + builder.field(GLOBAL.getPreferredName(), manageApplicationPrivileges); + } + if (false == indicesPrivileges.isEmpty()) { + builder.field(INDICES.getPreferredName(), indicesPrivileges); + } + if (false == applicationResourcePrivileges.isEmpty()) { + builder.field(APPLICATIONS.getPreferredName(), applicationResourcePrivileges); + } + if (false == runAs.isEmpty()) { + builder.field(RUN_AS.getPreferredName(), runAs); + } + if (false == metadata.isEmpty()) { + builder.field(METADATA.getPreferredName(), metadata); + } + return builder.endObject(); + } + + public static Role fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + + private @Nullable Collection clusterPrivileges = null; + private @Nullable ManageApplicationsPrivilege manageApplicationPrivileges = null; + private @Nullable Collection indicesPrivileges = null; + private @Nullable Collection applicationResourcePrivileges = null; + private @Nullable Collection runAs = null; + private @Nullable Map metadata = null; + + private Builder() { + } + + public Builder clusterPrivileges(@Nullable ClusterPrivilege... clusterPrivileges) { + if (clusterPrivileges == null) { + // null is a no-op to be programmer friendly + return this; + } + return clusterPrivileges(Arrays.asList(clusterPrivileges)); + } + + public Builder clusterPrivileges(@Nullable Collection clusterPrivileges) { + this.clusterPrivileges = clusterPrivileges; + return this; + } + + public Builder manageApplicationPrivileges(@Nullable ManageApplicationsPrivilege manageApplicationPrivileges) { + this.manageApplicationPrivileges = manageApplicationPrivileges; + return this; + } + + public Builder indicesPrivileges(@Nullable IndicesPrivileges... indicesPrivileges) { + if (indicesPrivileges == null) { + // null is a no-op to be programmer friendly + return this; + } + return indicesPrivileges(Arrays.asList(indicesPrivileges)); + } + + public Builder indicesPrivileges(@Nullable Collection indicesPrivileges) { + this.indicesPrivileges = indicesPrivileges; + return this; + } + + public Builder applicationResourcePrivileges(@Nullable ApplicationResourcePrivileges... applicationResourcePrivileges) { + if (applicationResourcePrivileges == null) { + // null is a no-op to be programmer friendly + return this; + } + return applicationResourcePrivileges(Arrays.asList(applicationResourcePrivileges)); + } + + public Builder applicationResourcePrivileges(@Nullable Collection applicationResourcePrivileges) { + this.applicationResourcePrivileges = applicationResourcePrivileges; + return this; + } + + public Builder runAs(@Nullable String... runAs) { + if (runAs == null) { + // null is a no-op to be programmer friendly + return this; + } + return runAs(Arrays.asList(runAs)); + } + + public Builder runAs(@Nullable Collection runAs) { + this.runAs = runAs; + return this; + } + + public Builder metadata(@Nullable Map metadata) { + this.metadata = metadata; + return this; + } + + public Role build() { + return new Role(clusterPrivileges, manageApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, runAs, + metadata); + } + } + +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java deleted file mode 100644 index ca8c91bc41b7a..0000000000000 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/RoleDescriptor.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.elasticsearch.client.security.user.privileges; - -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.xcontent.ToXContentObject; -import org.elasticsearch.common.xcontent.XContentBuilder; - -import java.io.IOException; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -public class RoleDescriptor implements ToXContentObject { - - private final String name; - // uniqueness and order are important for equals and hashcode - private final Set clusterPrivileges; - private final Set manageApplicationPrivileges; - private final Set indicesPrivileges; - private final Set applicationResourcePrivileges; - private final Set runAs; - private final Map metadata; - - private RoleDescriptor(String name, Collection clusterPrivileges, - Collection manageApplicationPrivileges, Collection indicesPrivileges, - Collection applicationResourcePrivileges, Collection runAs, - Map metadata) { - // we do all null checks inside the constructor - if (Strings.isNullOrEmpty(name)) { - throw new IllegalArgumentException("role descriptor must have a name"); - } - this.name = name; - // no cluster privileges are granted unless otherwise specified - this.clusterPrivileges = Collections - .unmodifiableSet(clusterPrivileges != null ? new HashSet<>(clusterPrivileges) : Collections.emptySet()); - // no manage application privileges are granted unless otherwise specified - this.manageApplicationPrivileges = Collections - .unmodifiableSet(manageApplicationPrivileges != null ? new HashSet<>(manageApplicationPrivileges) : Collections.emptySet()); - // no indices privileges are granted unless otherwise specified - this.indicesPrivileges = Collections - .unmodifiableSet(indicesPrivileges != null ? new HashSet<>(indicesPrivileges) : Collections.emptySet()); - // no application resource privileges are granted unless otherwise specified - this.applicationResourcePrivileges = Collections.unmodifiableSet( - applicationResourcePrivileges != null ? new HashSet<>(applicationResourcePrivileges) : Collections.emptySet()); - // no run as privileges are granted unless otherwise specified - this.runAs = Collections.unmodifiableSet(runAs != null ? new HashSet<>(runAs) : Collections.emptySet()); - this.metadata = metadata != null ? Collections.unmodifiableMap(metadata) : Collections.emptyMap(); - } - - public String getName() { - return name; - } - - public Set getClusterPrivileges() { - return clusterPrivileges; - } - - public Set getManageApplicationPrivileges() { - return manageApplicationPrivileges; - } - - public Set getIndicesPrivileges() { - return indicesPrivileges; - } - - public Set getApplicationResourcePrivileges() { - return applicationResourcePrivileges; - } - - public Set getRunAs() { - return runAs; - } - - public Map getMetadata() { - return metadata; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - RoleDescriptor that = (RoleDescriptor) o; - return name.equals(that.name) && clusterPrivileges.equals(that.clusterPrivileges) - && manageApplicationPrivileges.equals(that.manageApplicationPrivileges) && indicesPrivileges.equals(that.indicesPrivileges) - && applicationResourcePrivileges.equals(that.applicationResourcePrivileges) && runAs.equals(that.runAs) - && metadata.equals(that.metadata); - } - - @Override - public int hashCode() { - return Objects.hash(name, clusterPrivileges, manageApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, runAs, - metadata); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append("["); - sb.append("name=").append(name); - sb.append(", cluster=[").append(Strings.collectionToCommaDelimitedString(clusterPrivileges)); - sb.append("], global=[").append(Strings.collectionToCommaDelimitedString(manageApplicationPrivileges)); - sb.append("], indicesPrivileges=[").append(Strings.collectionToCommaDelimitedString(indicesPrivileges)); - sb.append("], applicationResourcePrivileges=[").append(Strings.collectionToCommaDelimitedString(applicationResourcePrivileges)); - sb.append("], runAs=[").append(Strings.collectionToCommaDelimitedString(runAs)); - sb.append("], metadata=[").append(metadata); - sb.append("]]"); - return sb.toString(); - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - return null; - } - -} From 460412721d3e5ec3d85ac83919cb1a07e77f6891 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Sun, 4 Nov 2018 21:22:46 +0200 Subject: [PATCH 14/26] Follow Tim's advice --- .../ApplicationResourcePrivileges.java | 68 ++-------- .../user/privileges/ClusterPrivilege.java | 92 ------------- .../user/privileges/IndexPrivilege.java | 88 ------------ .../user/privileges/IndicesPrivileges.java | 118 +++++++--------- .../client/security/user/privileges/Role.java | 128 ++++++++---------- 5 files changed, 120 insertions(+), 374 deletions(-) delete mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ClusterPrivilege.java delete mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndexPrivilege.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java index 80f8f799bc2cc..c3b3b37a4a143 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java @@ -19,16 +19,16 @@ package org.elasticsearch.client.security.user.privileges; -import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.Strings; import org.elasticsearch.common.xcontent.ConstructingObjectParser; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; import java.io.IOException; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -64,7 +64,6 @@ public final class ApplicationResourcePrivileges implements ToXContentObject { private final Set resources; private ApplicationResourcePrivileges(String application, Collection privileges, Collection resources) { - // we do all null checks inside the constructor if (Strings.isNullOrEmpty(application)) { throw new IllegalArgumentException("application privileges must have an application name"); } @@ -100,7 +99,9 @@ public boolean equals(Object o) { return false; } ApplicationResourcePrivileges that = (ApplicationResourcePrivileges) o; - return application.equals(that.application) && privileges.equals(that.privileges) && resources.equals(that.resources); + return application.equals(that.application) + && privileges.equals(that.privileges) + && resources.equals(that.resources); } @Override @@ -110,11 +111,11 @@ public int hashCode() { @Override public String toString() { - StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append("["); - sb.append(APPLICATION.getPreferredName()).append("=[").append(application).append("], "); - sb.append(PRIVILEGES.getPreferredName()).append("=[").append(Strings.collectionToCommaDelimitedString(privileges)).append("], "); - sb.append(RESOURCES.getPreferredName()).append("=[").append(Strings.collectionToCommaDelimitedString(resources)).append("]]"); - return sb.toString(); + try { + return XContentHelper.toXContent(this, XContentType.JSON, true).utf8ToString(); + } catch (IOException e) { + throw new RuntimeException("Unexpected", e); + } } @Override @@ -130,53 +131,4 @@ public static ApplicationResourcePrivileges fromXContent(XContentParser parser) return PARSER.apply(parser, null); } - public static Builder builder() { - return new Builder(); - } - - public static final class Builder { - - private @Nullable String application = null; - private @Nullable Collection privileges = null; - private @Nullable Collection resources = null; - - private Builder() { - } - - public Builder application(@Nullable String appName) { - this.application = appName; - return this; - } - - public Builder resources(@Nullable String... resources) { - if (resources == null) { - // null is a no-op to be programmer friendly - return this; - } - return resources(Arrays.asList(resources)); - } - - public Builder resources(@Nullable Collection resources) { - this.resources = resources; - return this; - } - - public Builder privileges(@Nullable String... privileges) { - if (privileges == null) { - // null is a no-op to be programmer friendly - return this; - } - return privileges(Arrays.asList(privileges)); - } - - public Builder privileges(@Nullable Collection privileges) { - this.privileges = privileges; - return this; - } - - public ApplicationResourcePrivileges build() { - return new ApplicationResourcePrivileges(application, privileges, resources); - } - - } } \ No newline at end of file diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ClusterPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ClusterPrivilege.java deleted file mode 100644 index a8535b07cec70..0000000000000 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ClusterPrivilege.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.elasticsearch.client.security.user.privileges; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.regex.Pattern; - -public final class ClusterPrivilege { - - private static final Pattern ALL_CLUSTER_PATTERN = Pattern.compile("^cluster:|indices:admin/template/"); - private static final Map builtins = new HashMap<>(); - - public static final ClusterPrivilege NONE = new ClusterPrivilege("none", true); - public static final ClusterPrivilege ALL = new ClusterPrivilege("all", true); - public static final ClusterPrivilege MONITOR = new ClusterPrivilege("monitor", true); - public static final ClusterPrivilege MONITOR_ML = new ClusterPrivilege("monitor_ml", true); - public static final ClusterPrivilege MONITOR_WATCHER = new ClusterPrivilege("monitor_watcher", true); - public static final ClusterPrivilege MONITOR_ROLLUP = new ClusterPrivilege("monitor_rollup", true); - public static final ClusterPrivilege MANAGE = new ClusterPrivilege("manage", true); - public static final ClusterPrivilege MANAGE_ML = new ClusterPrivilege("manage_ml", true); - public static final ClusterPrivilege MANAGE_WATCHER = new ClusterPrivilege("manage_watcher", true); - public static final ClusterPrivilege MANAGE_ROLLUP = new ClusterPrivilege("manage_rollup", true); - public static final ClusterPrivilege MANAGE_IDX_TEMPLATES = new ClusterPrivilege("manage_index_templates", true); - public static final ClusterPrivilege MANAGE_INGEST_PIPELINES = new ClusterPrivilege("manage_ingest_pipelines", true); - public static final ClusterPrivilege TRANSPORT_CLIENT = new ClusterPrivilege("transport_client", true); - public static final ClusterPrivilege MANAGE_SECURITY = new ClusterPrivilege("manage_security", true); - public static final ClusterPrivilege MANAGE_SAML = new ClusterPrivilege("manage_saml", true); - public static final ClusterPrivilege MANAGE_PIPELINE = new ClusterPrivilege("manage_pipeline", true); - public static final ClusterPrivilege MANAGE_CCR = new ClusterPrivilege("manage_ccr", true); - public static final ClusterPrivilege READ_CCR = new ClusterPrivilege("read_ccr", true); - - private final String name; - - private ClusterPrivilege(String name, boolean builtin) { - this.name = name; - if (builtin) { - builtins.put(name, this); - } - } - - public static ClusterPrivilege fromString(String name) { - Objects.requireNonNull(name); - final ClusterPrivilege builtin = builtins.get(name); - if (builtin != null) { - return builtin; - } - if (false == ALL_CLUSTER_PATTERN.matcher(name).matches()) { - throw new IllegalArgumentException("[" + name + "] is not a cluster action privilege."); - } - return new ClusterPrivilege(name, false); - } - - @Override - public String toString() { - return name; - } - - @Override - public int hashCode() { - return Objects.hash(name); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || false == getClass().equals(o.getClass())) { - return false; - } - return Objects.equals(name, ((ClusterPrivilege) o).name); - } -} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndexPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndexPrivilege.java deleted file mode 100644 index c5694acb8ffbe..0000000000000 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndexPrivilege.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.elasticsearch.client.security.user.privileges; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.regex.Pattern; - -public class IndexPrivilege { - - private static final Pattern ALL_INDEX_PATTERN = Pattern.compile("^indices:|internal:transport/proxy/indices:"); - private static final Map builtins = new HashMap<>(); - - public static final IndexPrivilege NONE = new IndexPrivilege("none", true); - public static final IndexPrivilege ALL = new IndexPrivilege("all", true); - public static final IndexPrivilege READ = new IndexPrivilege("read", true); - public static final IndexPrivilege READ_CROSS_CLUSTER = new IndexPrivilege("read_cross_cluster", true); - public static final IndexPrivilege CREATE = new IndexPrivilege("create", true); - public static final IndexPrivilege INDEX = new IndexPrivilege("index", true); - public static final IndexPrivilege DELETE = new IndexPrivilege("delete", true); - public static final IndexPrivilege WRITE = new IndexPrivilege("write", true); - public static final IndexPrivilege MONITOR = new IndexPrivilege("monitor", true); - public static final IndexPrivilege MANAGE = new IndexPrivilege("manage", true); - public static final IndexPrivilege DELETE_INDEX = new IndexPrivilege("delete_index", true); - public static final IndexPrivilege CREATE_INDEX = new IndexPrivilege("create_index", true); - public static final IndexPrivilege VIEW_METADATA = new IndexPrivilege("view_index_metadata", true); - public static final IndexPrivilege MANAGE_FOLLOW_INDEX = new IndexPrivilege("manage_follow_index", true); - - private final String name; - - private IndexPrivilege(String name, boolean builtin) { - this.name = name; - if (builtin) { - builtins.put(name, this); - } - } - - public static IndexPrivilege fromString(String name) { - Objects.requireNonNull(name); - final IndexPrivilege builtin = builtins.get(name); - if (builtin != null) { - return builtin; - } - if (false == ALL_INDEX_PATTERN.matcher(name).matches()) { - throw new IllegalArgumentException("[" + name + "] is not an index action privilege."); - } - return new IndexPrivilege(name, false); - } - - @Override - public String toString() { - return name; - } - - @Override - public int hashCode() { - return Objects.hash(name); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || false == getClass().equals(o.getClass())) { - return false; - } - return Objects.equals(name, ((IndexPrivilege) o).name); - } -} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java index adeeecd86d46d..a326c605d70c5 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java @@ -21,10 +21,11 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.ConstructingObjectParser; import org.elasticsearch.common.xcontent.ToXContentObject; @@ -35,7 +36,6 @@ import java.util.HashSet; import java.util.Objects; import java.util.Set; -import java.util.stream.Collectors; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; @@ -54,9 +54,7 @@ public final class IndicesPrivileges implements ToXContentObject { new ConstructingObjectParser<>("indices_privileges", false, constructorObjects -> { int i = 0; final Collection indices = (Collection) constructorObjects[i++]; - final Collection privilegeNames = (Collection) constructorObjects[i++]; - final Collection privileges = privilegeNames.stream().map(IndexPrivilege::fromString) - .collect(Collectors.toList()); + final Collection privileges = (Collection) constructorObjects[i++]; final Tuple, Collection> fields = (Tuple, Collection>) constructorObjects[i++]; final String query = (String) constructorObjects[i]; @@ -82,15 +80,15 @@ public final class IndicesPrivileges implements ToXContentObject { } private final Set indices; - private final Set privileges; - // '*' means all fields (default value), empty means no fields - private final Set grantedFields; - // empty means no field is denied - private final Set deniedFields; + private final Set privileges; + // null or singleton '*' means all fields are granted, empty means no fields are granted + private final @Nullable Set grantedFields; + // null or empty means no fields are denied + private final @Nullable Set deniedFields; // missing query means all documents, i.e. no restrictions private final @Nullable String query; - private IndicesPrivileges(Collection indices, Collection privileges, @Nullable Collection grantedFields, + private IndicesPrivileges(Collection indices, Collection privileges, @Nullable Collection grantedFields, @Nullable Collection deniedFields, @Nullable String query) { // we do all null checks inside the constructor if (null == indices || indices.isEmpty()) { @@ -101,12 +99,10 @@ private IndicesPrivileges(Collection indices, Collection } this.indices = Collections.unmodifiableSet(new HashSet<>(indices)); this.privileges = Collections.unmodifiableSet(new HashSet<>(privileges)); - // all fields granted unless otherwise specified - this.grantedFields = Collections - .unmodifiableSet(grantedFields != null ? new HashSet<>(grantedFields) : Collections.singleton("*")); - // no fields are denied unless otherwise specified - this.deniedFields = Collections - .unmodifiableSet(deniedFields != null ? new HashSet<>(deniedFields) : Collections.emptySet()); + // unspecified granted fields means no restriction + this.grantedFields = grantedFields == null ? null : Collections.unmodifiableSet(new HashSet<>(grantedFields)); + // unspecified denied fields means no restriction + this.deniedFields = deniedFields == null ? null : Collections.unmodifiableSet(new HashSet<>(deniedFields)); this.query = query; } @@ -114,15 +110,15 @@ public Set getIndices() { return this.indices; } - public Set getPrivileges() { + public Set getPrivileges() { return this.privileges; } - public Set getGrantedFields() { + public @Nullable Set getGrantedFields() { return this.grantedFields; } - public Set getDeniedFields() { + public @Nullable Set getDeniedFields() { return this.deniedFields; } @@ -135,16 +131,16 @@ public boolean isUsingDocumentLevelSecurity() { } public boolean isUsingFieldLevelSecurity() { - return hasDeniedFields() || hasGrantedFields(); + return limitsGrantedFields() || limitsDeniedFields(); } - private boolean hasDeniedFields() { - return false == deniedFields.isEmpty(); + private boolean limitsDeniedFields() { + return deniedFields != null && false == deniedFields.isEmpty(); } - private boolean hasGrantedFields() { + private boolean limitsGrantedFields() { // we treat just '*' as no FLS since that's what the UI defaults to - if (grantedFields.size() == 1 && grantedFields.iterator().next().equals("*")) { + if (grantedFields == null || (grantedFields.size() == 1 && grantedFields.iterator().next().equals("*"))) { return false; } return true; @@ -159,8 +155,11 @@ public boolean equals(Object o) { return false; } IndicesPrivileges that = (IndicesPrivileges) o; - return indices.equals(that.indices) && privileges.equals(that.privileges) && grantedFields.equals(that.grantedFields) - && deniedFields.equals(that.deniedFields) && Objects.equals(query, that.query); + return indices.equals(that.indices) + && privileges.equals(that.privileges) + && Objects.equals(grantedFields, that.grantedFields) + && Objects.equals(deniedFields, that.deniedFields) + && Objects.equals(query, that.query); } @Override @@ -170,18 +169,11 @@ public int hashCode() { @Override public String toString() { - final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append("["); - sb.append(NAMES.getPreferredName()).append("=[").append(Strings.collectionToCommaDelimitedString(indices)).append("], "); - sb.append(PRIVILEGES.getPreferredName()).append("=[").append(Strings.collectionToCommaDelimitedString(privileges)).append("], "); - sb.append(FIELD_PERMISSIONS).append("=["); - sb.append(GRANT_FIELDS).append("=[").append(Strings.collectionToCommaDelimitedString(grantedFields)).append("], "); - sb.append(EXCEPT_FIELDS).append("=[").append(Strings.collectionToCommaDelimitedString(deniedFields)).append("]"); - sb.append("]"); - if (query != null) { - sb.append(", ").append(QUERY.getPreferredName()).append("=[").append(query).append("]"); + try { + return XContentHelper.toXContent(this, XContentType.JSON, true).utf8ToString(); + } catch (IOException e) { + throw new RuntimeException("Unexpected", e); } - sb.append("]"); - return sb.toString(); } @Override @@ -189,11 +181,17 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.startObject(); builder.field(NAMES.getPreferredName(), indices); builder.field(PRIVILEGES.getPreferredName(), privileges); - builder.startObject(FIELD_PERMISSIONS.getPreferredName()); - builder.field(GRANT_FIELDS.getPreferredName(), grantedFields); - builder.field(EXCEPT_FIELDS.getPreferredName(), deniedFields); - builder.endObject(); - if (query != null) { + if (isUsingFieldLevelSecurity()) { + builder.startObject(FIELD_PERMISSIONS.getPreferredName()); + if (limitsGrantedFields()) { + builder.field(GRANT_FIELDS.getPreferredName(), grantedFields); + } + if (limitsDeniedFields()) { + builder.field(EXCEPT_FIELDS.getPreferredName(), deniedFields); + } + builder.endObject(); + } + if (isUsingDocumentLevelSecurity()) { builder.field("query", query); } return builder.endObject(); @@ -210,7 +208,7 @@ public static Builder builder() { public static final class Builder { private @Nullable Collection indices = null; - private @Nullable Collection privileges = null; + private @Nullable Collection privileges = null; private @Nullable Collection grantedFields = null; private @Nullable Collection deniedFields = null; private @Nullable String query = null; @@ -218,35 +216,27 @@ public static final class Builder { private Builder() { } - public Builder indices(@Nullable String... indices) { - if (indices == null) { - // null is a no-op to be programmer friendly - return this; - } - return indices(Arrays.asList(indices)); + public Builder indices(String... indices) { + return indices(Arrays.asList(Objects.requireNonNull(indices, "indices required"))); } - public Builder indices(@Nullable Collection indices) { - this.indices = indices; + public Builder indices(Collection indices) { + this.indices = Objects.requireNonNull(indices, "indices required"); return this; } - public Builder privileges(@Nullable IndexPrivilege... privileges) { - if (privileges == null) { - // null is a no-op to be programmer friendly - return this; - } - return privileges(Arrays.asList(privileges)); + public Builder privileges(String... privileges) { + return privileges(Arrays.asList(Objects.requireNonNull(privileges, "privileges required"))); } - public Builder privileges(@Nullable Collection privileges) { - this.privileges = privileges; + public Builder privileges(Collection privileges) { + this.privileges = Objects.requireNonNull(privileges, "privileges required"); return this; } public Builder grantedFields(@Nullable String... grantedFields) { if (grantedFields == null) { - // null is a no-op to be programmer friendly + this.grantedFields = null; return this; } return grantedFields(Arrays.asList(grantedFields)); @@ -259,17 +249,13 @@ public Builder grantedFields(@Nullable Collection grantedFields) { public Builder deniedFields(@Nullable String... deniedFields) { if (deniedFields == null) { - // null is a no-op to be programmer friendly + this.deniedFields = null; return this; } return deniedFields(Arrays.asList(deniedFields)); } public Builder deniedFields(@Nullable Collection deniedFields) { - if (deniedFields == null) { - // null is a no-op to be programmer friendly - return this; - } this.deniedFields = deniedFields; return this; } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java index de287e76c546e..3b28801894ed8 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java @@ -21,11 +21,12 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.xcontent.ConstructingObjectParser; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.ObjectParser.ValueType; import java.io.IOException; @@ -53,20 +54,19 @@ public final class Role implements ToXContentObject { private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("role_descriptor", false, constructorObjects -> { int i = 0; - final Collection clusterPrivileges = (Collection) constructorObjects[i++]; + final Collection clusterPrivileges = (Collection) constructorObjects[i++]; final ManageApplicationsPrivilege manageApplicationPrivileges = (ManageApplicationsPrivilege) constructorObjects[i++]; final Collection indicesPrivileges = (Collection) constructorObjects[i++]; final Collection applicationResourcePrivileges = (Collection) constructorObjects[i++]; - final Collection runAs = (Collection) constructorObjects[i++]; + final Collection runAsPrivilege = (Collection) constructorObjects[i++]; final Map metadata = (Map) constructorObjects[i]; return new Role(clusterPrivileges, manageApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, - runAs, metadata); + runAsPrivilege, metadata); }); static { - PARSER.declareFieldArray(optionalConstructorArg(), (p, c) -> ClusterPrivilege.fromString(p.text()), CLUSTER, - ValueType.STRING_ARRAY); + PARSER.declareStringArray(optionalConstructorArg(), CLUSTER); PARSER.declareObject(optionalConstructorArg(), ManageApplicationsPrivilege.PARSER, GLOBAL); PARSER.declareFieldArray(optionalConstructorArg(), IndicesPrivileges.PARSER, INDICES, ValueType.OBJECT_ARRAY); PARSER.declareFieldArray(optionalConstructorArg(), ApplicationResourcePrivileges.PARSER, APPLICATIONS, ValueType.OBJECT_ARRAY); @@ -74,18 +74,17 @@ public final class Role implements ToXContentObject { PARSER.>declareObject(constructorArg(), (parser, c) -> parser.map(), METADATA); } - private final Set clusterPrivileges; + private final Set clusterPrivileges; private final @Nullable ManageApplicationsPrivilege manageApplicationPrivileges; private final Set indicesPrivileges; private final Set applicationResourcePrivileges; - private final Set runAs; + private final Set runAsPrivilege; private final Map metadata; - private Role(Collection clusterPrivileges, - @Nullable ManageApplicationsPrivilege manageApplicationPrivileges, Collection indicesPrivileges, - Collection applicationResourcePrivileges, Collection runAs, - Map metadata) { - // we do all null checks inside the constructor + private Role(@Nullable Collection clusterPrivileges, @Nullable ManageApplicationsPrivilege manageApplicationPrivileges, + @Nullable Collection indicesPrivileges, + @Nullable Collection applicationResourcePrivileges, @Nullable Collection runAsPrivilege, + @Nullable Map metadata) { // no cluster privileges are granted unless otherwise specified this.clusterPrivileges = Collections .unmodifiableSet(clusterPrivileges != null ? new HashSet<>(clusterPrivileges) : Collections.emptySet()); @@ -97,11 +96,11 @@ private Role(Collection clusterPrivileges, this.applicationResourcePrivileges = Collections.unmodifiableSet( applicationResourcePrivileges != null ? new HashSet<>(applicationResourcePrivileges) : Collections.emptySet()); // no run as privileges are granted unless otherwise specified - this.runAs = Collections.unmodifiableSet(runAs != null ? new HashSet<>(runAs) : Collections.emptySet()); + this.runAsPrivilege = Collections.unmodifiableSet(runAsPrivilege != null ? new HashSet<>(runAsPrivilege) : Collections.emptySet()); this.metadata = metadata != null ? Collections.unmodifiableMap(metadata) : Collections.emptyMap(); } - public Set getClusterPrivileges() { + public Set getClusterPrivileges() { return clusterPrivileges; } @@ -117,8 +116,8 @@ public Set getApplicationResourcePrivileges() { return applicationResourcePrivileges; } - public Set getRunAs() { - return runAs; + public Set getRunAsPrivilege() { + return runAsPrivilege; } public Map getMetadata() { @@ -133,27 +132,24 @@ public boolean equals(Object o) { return clusterPrivileges.equals(that.clusterPrivileges) && Objects.equals(manageApplicationPrivileges, that.manageApplicationPrivileges) && indicesPrivileges.equals(that.indicesPrivileges) - && applicationResourcePrivileges.equals(that.applicationResourcePrivileges) && runAs.equals(that.runAs) + && applicationResourcePrivileges.equals(that.applicationResourcePrivileges) + && runAsPrivilege.equals(that.runAsPrivilege) && metadata.equals(that.metadata); } @Override public int hashCode() { - return Objects.hash(clusterPrivileges, manageApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, runAs, - metadata); + return Objects.hash(clusterPrivileges, manageApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, + runAsPrivilege, metadata); } @Override public String toString() { - StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append("["); - sb.append("cluster=[").append(Strings.collectionToCommaDelimitedString(clusterPrivileges)); - sb.append("], global[").append(manageApplicationPrivileges); - sb.append("], indicesPrivileges=[").append(Strings.collectionToCommaDelimitedString(indicesPrivileges)); - sb.append("], applicationResourcePrivileges=[").append(Strings.collectionToCommaDelimitedString(applicationResourcePrivileges)); - sb.append("], runAs=[").append(Strings.collectionToCommaDelimitedString(runAs)); - sb.append("], metadata=[").append(metadata); - sb.append("]]"); - return sb.toString(); + try { + return XContentHelper.toXContent(this, XContentType.JSON, true).utf8ToString(); + } catch (IOException e) { + throw new RuntimeException("Unexpected", e); + } } @Override @@ -171,8 +167,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (false == applicationResourcePrivileges.isEmpty()) { builder.field(APPLICATIONS.getPreferredName(), applicationResourcePrivileges); } - if (false == runAs.isEmpty()) { - builder.field(RUN_AS.getPreferredName(), runAs); + if (false == runAsPrivilege.isEmpty()) { + builder.field(RUN_AS.getPreferredName(), runAsPrivilege); } if (false == metadata.isEmpty()) { builder.field(METADATA.getPreferredName(), metadata); @@ -190,81 +186,73 @@ public static Builder builder() { public static final class Builder { - private @Nullable Collection clusterPrivileges = null; + private @Nullable Collection clusterPrivileges = null; private @Nullable ManageApplicationsPrivilege manageApplicationPrivileges = null; private @Nullable Collection indicesPrivileges = null; private @Nullable Collection applicationResourcePrivileges = null; - private @Nullable Collection runAs = null; + private @Nullable Collection runAsPrivilege = null; private @Nullable Map metadata = null; private Builder() { } - public Builder clusterPrivileges(@Nullable ClusterPrivilege... clusterPrivileges) { - if (clusterPrivileges == null) { - // null is a no-op to be programmer friendly - return this; - } - return clusterPrivileges(Arrays.asList(clusterPrivileges)); + public Builder clusterPrivileges(String... clusterPrivileges) { + return clusterPrivileges(Arrays + .asList(Objects.requireNonNull(clusterPrivileges, "Cluster privileges cannot be null. Pass an empty array instead."))); } - public Builder clusterPrivileges(@Nullable Collection clusterPrivileges) { - this.clusterPrivileges = clusterPrivileges; + public Builder clusterPrivileges(Collection clusterPrivileges) { + this.clusterPrivileges = Objects.requireNonNull(clusterPrivileges, + "Cluster privileges cannot be null. Pass an empty collection instead."); return this; } - public Builder manageApplicationPrivileges(@Nullable ManageApplicationsPrivilege manageApplicationPrivileges) { + public Builder manageApplicationPrivileges(ManageApplicationsPrivilege manageApplicationPrivileges) { this.manageApplicationPrivileges = manageApplicationPrivileges; return this; } - public Builder indicesPrivileges(@Nullable IndicesPrivileges... indicesPrivileges) { - if (indicesPrivileges == null) { - // null is a no-op to be programmer friendly - return this; - } - return indicesPrivileges(Arrays.asList(indicesPrivileges)); + public Builder indicesPrivileges(IndicesPrivileges... indicesPrivileges) { + return indicesPrivileges(Arrays + .asList(Objects.requireNonNull(indicesPrivileges, "Indices privileges cannot be null. Pass an empty array instead."))); } - public Builder indicesPrivileges(@Nullable Collection indicesPrivileges) { - this.indicesPrivileges = indicesPrivileges; + public Builder indicesPrivileges(Collection indicesPrivileges) { + this.indicesPrivileges = Objects.requireNonNull(indicesPrivileges, + "Indices privileges cannot be null. Pass an empty collection instead."); return this; } - public Builder applicationResourcePrivileges(@Nullable ApplicationResourcePrivileges... applicationResourcePrivileges) { - if (applicationResourcePrivileges == null) { - // null is a no-op to be programmer friendly - return this; - } - return applicationResourcePrivileges(Arrays.asList(applicationResourcePrivileges)); + public Builder applicationResourcePrivileges(ApplicationResourcePrivileges... applicationResourcePrivileges) { + return applicationResourcePrivileges(Arrays.asList(Objects.requireNonNull(applicationResourcePrivileges, + "Application resource privileges cannot be null. Pass an empty array instead."))); } - public Builder applicationResourcePrivileges(@Nullable Collection applicationResourcePrivileges) { - this.applicationResourcePrivileges = applicationResourcePrivileges; + public Builder applicationResourcePrivileges(Collection applicationResourcePrivileges) { + this.applicationResourcePrivileges = Objects.requireNonNull(applicationResourcePrivileges, + "Application resource privileges cannot be null. Pass an empty collection instead."); return this; } - public Builder runAs(@Nullable String... runAs) { - if (runAs == null) { - // null is a no-op to be programmer friendly - return this; - } - return runAs(Arrays.asList(runAs)); + public Builder runAsPrivilege(String... runAsPrivilege) { + return runAsPrivilege(Arrays + .asList(Objects.requireNonNull(runAsPrivilege, "Run as privilege cannot be null. Pass an empty array instead."))); } - public Builder runAs(@Nullable Collection runAs) { - this.runAs = runAs; + public Builder runAsPrivilege(Collection runAsPrivilege) { + this.runAsPrivilege = Objects.requireNonNull(runAsPrivilege, + "Run as privilege cannot be null. Pass an empty collection instead."); return this; } - public Builder metadata(@Nullable Map metadata) { - this.metadata = metadata; + public Builder metadata(Map metadata) { + this.metadata = Objects.requireNonNull(metadata, "Metadata cannot be null. Pass an empty map instead."); return this; } public Role build() { - return new Role(clusterPrivileges, manageApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, runAs, - metadata); + return new Role(clusterPrivileges, manageApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, + runAsPrivilege, metadata); } } From beaa373287a2f2a796d44d397abee9385342b50b Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Mon, 5 Nov 2018 15:19:45 +0200 Subject: [PATCH 15/26] Follow Tim's advice --- .../GlobalApplicationPrivileges.java | 91 +++++++++++++++++++ .../privileges/GlobalScopedPrivilege.java | 68 ++++++++++++++ .../user/privileges/IndicesPrivileges.java | 6 +- 3 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalApplicationPrivileges.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalApplicationPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalApplicationPrivileges.java new file mode 100644 index 0000000000000..f2f893da9cb50 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalApplicationPrivileges.java @@ -0,0 +1,91 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.security.user.privileges; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; + +public class GlobalApplicationPrivileges implements ToXContentObject { + + static final ParseField APPLICATION = new ParseField("application"); + + @SuppressWarnings("unchecked") + static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "global_application_privileges", false, constructorObjects -> { + return new GlobalApplicationPrivileges((Collection) constructorObjects[0]); + }); + + static { + PARSER.declareNamedObjects(constructorArg(), (p, c, n) -> GlobalScopedPrivilege.fromXContent(n, p), APPLICATION); + } + + private final Set privileges; + + public GlobalApplicationPrivileges(Collection privileges) { + this.privileges = Collections.unmodifiableSet(new HashSet<>(Objects.requireNonNull(privileges))); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + for (final GlobalScopedPrivilege privilege : privileges) { + builder.field(privilege.getScope(), privilege.getRaw()); + } + return builder.endObject(); + } + + public static GlobalApplicationPrivileges fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } + + public Set getPrivileges() { + return privileges; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass() != o.getClass()) { + return false; + } + final GlobalApplicationPrivileges that = (GlobalApplicationPrivileges) o; + return privileges.equals(that.privileges); + } + + @Override + public int hashCode() { + return Objects.hash(privileges); + } + +} \ No newline at end of file diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java new file mode 100644 index 0000000000000..2f1b8722c0279 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java @@ -0,0 +1,68 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.security.user.privileges; + +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; + +public class GlobalScopedPrivilege { + + private final String scope; + private final Map privilege; + + public GlobalScopedPrivilege(String scope, Map privilege) { + this.scope = Objects.requireNonNull(scope); + this.privilege = Collections.unmodifiableMap(Objects.requireNonNull(privilege)); + } + + public String getScope() { + return scope; + } + + public Map getRaw() { + return privilege; + } + + public static GlobalScopedPrivilege fromXContent(String scope, XContentParser parser) throws IOException { + return new GlobalScopedPrivilege(scope, parser.map()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass() != o.getClass()) { + return false; + } + final GlobalScopedPrivilege that = (GlobalScopedPrivilege) o; + return scope.equals(that.scope) && privilege.equals(that.privilege); + } + + @Override + public int hashCode() { + return Objects.hash(scope, privilege); + } + +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java index a326c605d70c5..9175b7977f3b0 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java @@ -131,10 +131,10 @@ public boolean isUsingDocumentLevelSecurity() { } public boolean isUsingFieldLevelSecurity() { - return limitsGrantedFields() || limitsDeniedFields(); + return limitsGrantedFields() || hasDeniedFields(); } - private boolean limitsDeniedFields() { + private boolean hasDeniedFields() { return deniedFields != null && false == deniedFields.isEmpty(); } @@ -186,7 +186,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (limitsGrantedFields()) { builder.field(GRANT_FIELDS.getPreferredName(), grantedFields); } - if (limitsDeniedFields()) { + if (hasDeniedFields()) { builder.field(EXCEPT_FIELDS.getPreferredName(), deniedFields); } builder.endObject(); From 05ccbd023c4b2927301ac83ba6be89a30fbefea2 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Mon, 5 Nov 2018 15:24:35 +0200 Subject: [PATCH 16/26] Removed ManageApplicationsPrivilege --- .../ManageApplicationsPrivilege.java | 150 ------------------ 1 file changed, 150 deletions(-) delete mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java deleted file mode 100644 index b5900af64f121..0000000000000 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationsPrivilege.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.elasticsearch.client.security.user.privileges; - -import org.elasticsearch.common.Nullable; -import org.elasticsearch.common.ParseField; -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.xcontent.ConstructingObjectParser; -import org.elasticsearch.common.xcontent.ToXContentObject; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentParser; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; - -public final class ManageApplicationsPrivilege implements ToXContentObject { - - public static final ParseField CATEGORY = new ParseField("application"); - public static final ParseField SCOPE = new ParseField("manage"); - public static final ParseField APPLICATIONS = new ParseField("applications"); - - static final ConstructingObjectParser PARSER = - new ConstructingObjectParser<>("application_privileges", false, constructorObjects -> { - return (ManageApplicationsPrivilege) constructorObjects[0]; - }); - - static { - @SuppressWarnings("unchecked") - final ConstructingObjectParser apps_parser = - new ConstructingObjectParser<>("apps_parser", false, constructorObjects -> { - final Collection applications = (Collection) constructorObjects[0]; - return new ManageApplicationsPrivilege(applications); - }); - apps_parser.declareStringArray(constructorArg(), APPLICATIONS); - - final ConstructingObjectParser scope_parser = - new ConstructingObjectParser<>("scope_parser", false, constructorObjects -> { - return (ManageApplicationsPrivilege) constructorObjects[0]; - }); - scope_parser.declareObject(constructorArg(), apps_parser, SCOPE); - - PARSER.declareObject(constructorArg(), scope_parser, CATEGORY); - } - - private final Set applications; - - private ManageApplicationsPrivilege(Collection applications) { - // we do all null checks inside the constructor - if (null == applications || applications.isEmpty()) { - throw new IllegalArgumentException("managed applications list should not be null"); - } - this.applications = Collections.unmodifiableSet(new HashSet<>(applications)); - } - - public Set getApplications() { - return applications; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ManageApplicationsPrivilege that = (ManageApplicationsPrivilege) o; - return applications.equals(that.applications); - } - - @Override - public int hashCode() { - return applications.hashCode(); - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append("["); - sb.append(APPLICATIONS.getPreferredName()).append("=[").append(Strings.collectionToCommaDelimitedString(applications)); - sb.append("]]"); - return sb.toString(); - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); - builder.startObject(CATEGORY.getPreferredName()); - builder.startObject(SCOPE.getPreferredName()); - builder.field(APPLICATIONS.getPreferredName(), applications); - builder.endObject(); - builder.endObject(); - return builder.endObject(); - } - - public static ManageApplicationsPrivilege fromXContent(XContentParser parser) { - return PARSER.apply(parser, null); - } - - public static Builder builder() { - return new Builder(); - } - - public static final class Builder { - - private @Nullable Collection applications = null; - - private Builder() { - } - - public Builder applications(@Nullable String... applications) { - if (applications == null) { - // null is a no-op to be programmer friendly - return this; - } - return applications(Arrays.asList(applications)); - } - - public Builder applications(@Nullable Collection applications) { - this.applications = applications; - return this; - } - - public ManageApplicationsPrivilege build() { - return new ManageApplicationsPrivilege(applications); - } - } -} From 4c339e917c04f0c1a340ad20f94efff1e947a4ad Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Mon, 5 Nov 2018 15:49:19 +0200 Subject: [PATCH 17/26] Rename to GlobalPrivileges --- ...nPrivileges.java => GlobalPrivileges.java} | 31 +++++++++--------- .../client/security/user/privileges/Role.java | 32 +++++++++---------- 2 files changed, 32 insertions(+), 31 deletions(-) rename client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/{GlobalApplicationPrivileges.java => GlobalPrivileges.java} (63%) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalApplicationPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java similarity index 63% rename from client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalApplicationPrivileges.java rename to client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java index f2f893da9cb50..1b563c5545594 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalApplicationPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java @@ -32,43 +32,44 @@ import java.util.Objects; import java.util.Set; -import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; -public class GlobalApplicationPrivileges implements ToXContentObject { +public class GlobalPrivileges implements ToXContentObject { static final ParseField APPLICATION = new ParseField("application"); @SuppressWarnings("unchecked") - static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - "global_application_privileges", false, constructorObjects -> { - return new GlobalApplicationPrivileges((Collection) constructorObjects[0]); + static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "global_application_privileges", true, constructorObjects -> { + return new GlobalPrivileges((Collection) constructorObjects[0]); }); static { - PARSER.declareNamedObjects(constructorArg(), (p, c, n) -> GlobalScopedPrivilege.fromXContent(n, p), APPLICATION); + PARSER.declareNamedObjects(optionalConstructorArg(), (p, c, n) -> GlobalScopedPrivilege.fromXContent(n, p), APPLICATION); } - private final Set privileges; + private final Set applicationPrivileges; - public GlobalApplicationPrivileges(Collection privileges) { - this.privileges = Collections.unmodifiableSet(new HashSet<>(Objects.requireNonNull(privileges))); + public GlobalPrivileges(Collection applicationPrivileges) { + this.applicationPrivileges = applicationPrivileges == null ? Collections.emptySet() + : Collections.unmodifiableSet(new HashSet<>(applicationPrivileges)); } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); - for (final GlobalScopedPrivilege privilege : privileges) { + for (final GlobalScopedPrivilege privilege : applicationPrivileges) { builder.field(privilege.getScope(), privilege.getRaw()); } return builder.endObject(); } - public static GlobalApplicationPrivileges fromXContent(XContentParser parser) { + public static GlobalPrivileges fromXContent(XContentParser parser) { return PARSER.apply(parser, null); } public Set getPrivileges() { - return privileges; + return applicationPrivileges; } @Override @@ -79,13 +80,13 @@ public boolean equals(Object o) { if (o == null || this.getClass() != o.getClass()) { return false; } - final GlobalApplicationPrivileges that = (GlobalApplicationPrivileges) o; - return privileges.equals(that.privileges); + final GlobalPrivileges that = (GlobalPrivileges) o; + return applicationPrivileges.equals(that.applicationPrivileges); } @Override public int hashCode() { - return Objects.hash(privileges); + return Objects.hash(applicationPrivileges); } } \ No newline at end of file diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java index 3b28801894ed8..0f1632a84648c 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java @@ -55,19 +55,19 @@ public final class Role implements ToXContentObject { constructorObjects -> { int i = 0; final Collection clusterPrivileges = (Collection) constructorObjects[i++]; - final ManageApplicationsPrivilege manageApplicationPrivileges = (ManageApplicationsPrivilege) constructorObjects[i++]; + final GlobalPrivileges globalApplicationPrivileges = (GlobalPrivileges) constructorObjects[i++]; final Collection indicesPrivileges = (Collection) constructorObjects[i++]; final Collection applicationResourcePrivileges = (Collection) constructorObjects[i++]; final Collection runAsPrivilege = (Collection) constructorObjects[i++]; final Map metadata = (Map) constructorObjects[i]; - return new Role(clusterPrivileges, manageApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, + return new Role(clusterPrivileges, globalApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, runAsPrivilege, metadata); }); static { PARSER.declareStringArray(optionalConstructorArg(), CLUSTER); - PARSER.declareObject(optionalConstructorArg(), ManageApplicationsPrivilege.PARSER, GLOBAL); + PARSER.declareObject(optionalConstructorArg(), GlobalPrivileges.PARSER, GLOBAL); PARSER.declareFieldArray(optionalConstructorArg(), IndicesPrivileges.PARSER, INDICES, ValueType.OBJECT_ARRAY); PARSER.declareFieldArray(optionalConstructorArg(), ApplicationResourcePrivileges.PARSER, APPLICATIONS, ValueType.OBJECT_ARRAY); PARSER.declareStringArray(optionalConstructorArg(), RUN_AS); @@ -75,20 +75,20 @@ public final class Role implements ToXContentObject { } private final Set clusterPrivileges; - private final @Nullable ManageApplicationsPrivilege manageApplicationPrivileges; + private final @Nullable GlobalPrivileges globalApplicationPrivileges; private final Set indicesPrivileges; private final Set applicationResourcePrivileges; private final Set runAsPrivilege; private final Map metadata; - private Role(@Nullable Collection clusterPrivileges, @Nullable ManageApplicationsPrivilege manageApplicationPrivileges, + private Role(@Nullable Collection clusterPrivileges, @Nullable GlobalPrivileges globalApplicationPrivileges, @Nullable Collection indicesPrivileges, @Nullable Collection applicationResourcePrivileges, @Nullable Collection runAsPrivilege, @Nullable Map metadata) { // no cluster privileges are granted unless otherwise specified this.clusterPrivileges = Collections .unmodifiableSet(clusterPrivileges != null ? new HashSet<>(clusterPrivileges) : Collections.emptySet()); - this.manageApplicationPrivileges = manageApplicationPrivileges; + this.globalApplicationPrivileges = globalApplicationPrivileges; // no indices privileges are granted unless otherwise specified this.indicesPrivileges = Collections .unmodifiableSet(indicesPrivileges != null ? new HashSet<>(indicesPrivileges) : Collections.emptySet()); @@ -104,8 +104,8 @@ public Set getClusterPrivileges() { return clusterPrivileges; } - public ManageApplicationsPrivilege getManageApplicationPrivileges() { - return manageApplicationPrivileges; + public GlobalPrivileges getGlobalApplicationPrivileges() { + return globalApplicationPrivileges; } public Set getIndicesPrivileges() { @@ -130,7 +130,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; Role that = (Role) o; return clusterPrivileges.equals(that.clusterPrivileges) - && Objects.equals(manageApplicationPrivileges, that.manageApplicationPrivileges) + && Objects.equals(globalApplicationPrivileges, that.globalApplicationPrivileges) && indicesPrivileges.equals(that.indicesPrivileges) && applicationResourcePrivileges.equals(that.applicationResourcePrivileges) && runAsPrivilege.equals(that.runAsPrivilege) @@ -139,7 +139,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(clusterPrivileges, manageApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, + return Objects.hash(clusterPrivileges, globalApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, runAsPrivilege, metadata); } @@ -158,8 +158,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (false == clusterPrivileges.isEmpty()) { builder.field(CLUSTER.getPreferredName(), clusterPrivileges); } - if (null != manageApplicationPrivileges) { - builder.field(GLOBAL.getPreferredName(), manageApplicationPrivileges); + if (null != globalApplicationPrivileges) { + builder.field(GLOBAL.getPreferredName(), globalApplicationPrivileges); } if (false == indicesPrivileges.isEmpty()) { builder.field(INDICES.getPreferredName(), indicesPrivileges); @@ -187,7 +187,7 @@ public static Builder builder() { public static final class Builder { private @Nullable Collection clusterPrivileges = null; - private @Nullable ManageApplicationsPrivilege manageApplicationPrivileges = null; + private @Nullable GlobalPrivileges globalApplicationPrivileges = null; private @Nullable Collection indicesPrivileges = null; private @Nullable Collection applicationResourcePrivileges = null; private @Nullable Collection runAsPrivilege = null; @@ -207,8 +207,8 @@ public Builder clusterPrivileges(Collection clusterPrivileges) { return this; } - public Builder manageApplicationPrivileges(ManageApplicationsPrivilege manageApplicationPrivileges) { - this.manageApplicationPrivileges = manageApplicationPrivileges; + public Builder manageApplicationPrivileges(GlobalPrivileges globalApplicationPrivileges) { + this.globalApplicationPrivileges = globalApplicationPrivileges; return this; } @@ -251,7 +251,7 @@ public Builder metadata(Map metadata) { } public Role build() { - return new Role(clusterPrivileges, manageApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, + return new Role(clusterPrivileges, globalApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges, runAsPrivilege, metadata); } } From 9b75cdbb472e85ad197ce95ca0b7403eb08eb39a Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Mon, 5 Nov 2018 16:06:56 +0200 Subject: [PATCH 18/26] public ApplicationResourcePrivileges constructor --- .../security/user/privileges/ApplicationResourcePrivileges.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java index c3b3b37a4a143..b29bbc6f50826 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java @@ -63,7 +63,7 @@ public final class ApplicationResourcePrivileges implements ToXContentObject { private final Set privileges; private final Set resources; - private ApplicationResourcePrivileges(String application, Collection privileges, Collection resources) { + public ApplicationResourcePrivileges(String application, Collection privileges, Collection resources) { if (Strings.isNullOrEmpty(application)) { throw new IllegalArgumentException("application privileges must have an application name"); } From d83113d0f91493a07bb7c0c040b3389bf306a12c Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Wed, 7 Nov 2018 16:21:02 +0200 Subject: [PATCH 19/26] Nits + javadoc --- .../ApplicationResourcePrivileges.java | 19 +++++++ .../user/privileges/GlobalPrivileges.java | 25 +++++++-- .../privileges/GlobalScopedPrivilege.java | 11 ++++ .../user/privileges/IndicesPrivileges.java | 55 ++++++++++++++++++- .../ManageApplicationPrivilege.java | 48 ++++++++++++++++ .../client/security/user/privileges/Role.java | 25 ++++++++- 6 files changed, 175 insertions(+), 8 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java index b29bbc6f50826..1baf6f00be119 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java @@ -37,6 +37,12 @@ import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +/** + * Represents privileges over resources that are scoped under an application. + * The application, resources and privileges are completely managed by the + * client and can be "arbitrary" string identifiers. Elasticsearch is not + * concerned by resources under an application scope. + */ public final class ApplicationResourcePrivileges implements ToXContentObject { private static final ParseField APPLICATION = new ParseField("application"); @@ -63,6 +69,19 @@ public final class ApplicationResourcePrivileges implements ToXContentObject { private final Set privileges; private final Set resources; + /** + * Constructs privileges for resources under an application scope. + * + * @param application + * The application name. This identifier is completely under the + * clients control. + * @param privileges + * The privileges names. Cannot be null or empty. Privilege + * identifiers are completely under the clients control. + * @param resources + * The resources names. Cannot be null or empty. Resource identifiers + * are completely under the clients control. + */ public ApplicationResourcePrivileges(String application, Collection privileges, Collection resources) { if (Strings.isNullOrEmpty(application)) { throw new IllegalArgumentException("application privileges must have an application name"); diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java index 1b563c5545594..ca2b926a1f5db 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java @@ -34,13 +34,20 @@ import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; -public class GlobalPrivileges implements ToXContentObject { - +/** + * Represents global privileges. "Global Privilege" is a mantra for granular + * privileges over applications. {@code ApplicationResourcePrivileges} model + * application privileges over resources. This models user privileges over + * applications. Every client is responsible to manage the applications as well + * as the privileges for them. + */ +public final class GlobalPrivileges implements ToXContentObject { + static final ParseField APPLICATION = new ParseField("application"); - + @SuppressWarnings("unchecked") - static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - "global_application_privileges", true, constructorObjects -> { + static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("global_application_privileges", + true, constructorObjects -> { return new GlobalPrivileges((Collection) constructorObjects[0]); }); @@ -50,6 +57,12 @@ public class GlobalPrivileges implements ToXContentObject { private final Set applicationPrivileges; + /** + * Constructs global privileges from the set of application privileges. + * + * @param applicationPrivileges + * The privileges over applications. + */ public GlobalPrivileges(Collection applicationPrivileges) { this.applicationPrivileges = applicationPrivileges == null ? Collections.emptySet() : Collections.unmodifiableSet(new HashSet<>(applicationPrivileges)); @@ -58,9 +71,11 @@ public GlobalPrivileges(Collection applicationP @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); + builder.startObject(APPLICATION.getPreferredName()); for (final GlobalScopedPrivilege privilege : applicationPrivileges) { builder.field(privilege.getScope(), privilege.getRaw()); } + builder.endObject(); return builder.endObject(); } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java index 2f1b8722c0279..8d7c28eda733b 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java @@ -26,11 +26,22 @@ import java.util.Map; import java.util.Objects; +/** + * Represents generic global application privileges that can be scoped for each + * application. The privilege definition is outside the Elasticsearch control. + */ public class GlobalScopedPrivilege { private final String scope; private final Map privilege; + /** + * Constructs privilege over some "scope". The "scope" is usually an application + * name but there is no constraint over this identifier. + * + * @param scope The scope of the privilege. + * @param privilege The privilege definition. This is out of the Elasticsearch control. + */ public GlobalScopedPrivilege(String scope, Map privilege) { this.scope = Objects.requireNonNull(scope); this.privilege = Collections.unmodifiableMap(Objects.requireNonNull(privilege)); diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java index 9175b7977f3b0..17c0642995608 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java @@ -40,8 +40,30 @@ import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +/** + * Represents privileges over indices. There is a canonical set of privilege + * names (eg. {@code IndicesPrivileges#READ_PRIVILEGE_NAME}) but there is + * flexibility in defining finer grained, more specialized, privileges. This + * also encapsulates field and document level security privileges. These allow + * to control what fields or documents are readable or queryable. + */ public final class IndicesPrivileges implements ToXContentObject { + public static final String NONE_PRIVILEGE_NAME = "none"; + public static final String ALL_PRIVILEGE_NAME = "all"; + public static final String READ_PRIVILEGE_NAME = "read"; + public static final String READ_CROSS_CLUSTER_PRIVILEGE_NAME = "read_cross_cluster"; + public static final String CREATE_PRIVILEGE_NAME = "create"; + public static final String INDEX_PRIVILEGE_NAME = "index"; + public static final String DELETE_PRIVILEGE_NAME = "delete"; + public static final String WRITE_PRIVILEGE_NAME = "write"; + public static final String MONITOR_PRIVILEGE_NAME = "monitor"; + public static final String MANAGE_PRIVILEGE_NAME = "manage"; + public static final String DELETE_INDEX_PRIVILEGE_NAME = "delete_index"; + public static final String CREATE_INDEX_PRIVILEGE_NAME = "create_index"; + public static final String VIEW_INDEX_METADATA_PRIVILEGE_NAME = "view_index_metadata"; + public static final String MANAGE_FOLLOW_INDEX_PRIVILEGE_NAME = "manage_follow_index"; + public static final ParseField NAMES = new ParseField("names"); public static final ParseField PRIVILEGES = new ParseField("privileges"); public static final ParseField FIELD_PERMISSIONS = new ParseField("field_security"); @@ -57,8 +79,10 @@ public final class IndicesPrivileges implements ToXContentObject { final Collection privileges = (Collection) constructorObjects[i++]; final Tuple, Collection> fields = (Tuple, Collection>) constructorObjects[i++]; + final Collection grantFields = fields != null ? fields.v1() : null; + final Collection exceptFields = fields != null ? fields.v2() : null; final String query = (String) constructorObjects[i]; - return new IndicesPrivileges(indices, privileges, fields.v1(), fields.v2(), query); + return new IndicesPrivileges(indices, privileges, grantFields, exceptFields, query); }); static { @@ -90,7 +114,6 @@ public final class IndicesPrivileges implements ToXContentObject { private IndicesPrivileges(Collection indices, Collection privileges, @Nullable Collection grantedFields, @Nullable Collection deniedFields, @Nullable String query) { - // we do all null checks inside the constructor if (null == indices || indices.isEmpty()) { throw new IllegalArgumentException("indices privileges must refer to at least one index name or index name pattern"); } @@ -106,30 +129,58 @@ private IndicesPrivileges(Collection indices, Collection privile this.query = query; } + /** + * The indices names covered by the privileges. + */ public Set getIndices() { return this.indices; } + /** + * The privileges acting over indices. There is a canonical predefined set of + * such privileges, but the {@code String} datatype allows for flexibility in defining + * finer grained privileges. + */ public Set getPrivileges() { return this.privileges; } + /** + * The document fields that can be read or queried. Can be null, in this case + * all the document's fields are granted access to. Can also be empty, in which + * case no fields are granted access to. + */ public @Nullable Set getGrantedFields() { return this.grantedFields; } + /** + * The document fields that cannot be accessed or queried. Can be null or empty, + * in which case no fields are denied. + */ public @Nullable Set getDeniedFields() { return this.deniedFields; } + /** + * A query limiting the visible documents in the indices. Can be null, in which + * case all documents are visible. + */ public @Nullable String getQuery() { return this.query; } + /** + * If {@code true} some documents might not be visible. Only the documents + * matching {@code query} will be readable. + */ public boolean isUsingDocumentLevelSecurity() { return query != null; } + /** + * If {@code true} some document fields might not be visible. + */ public boolean isUsingFieldLevelSecurity() { return limitsGrantedFields() || hasDeniedFields(); } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java new file mode 100644 index 0000000000000..c698ad611414b --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java @@ -0,0 +1,48 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.security.user.privileges; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +/** + * Represents the privilege to "manage" certain applications. The "manage" + * privilege is actually defined outside of Elasticsearch. + */ +public class ManageApplicationPrivilege extends GlobalScopedPrivilege { + + private static final String SCOPE = "manage"; + private static final String APPLICATIONS = "applications"; + + public ManageApplicationPrivilege(Collection applications) { + super(SCOPE, Collections.singletonMap(APPLICATIONS, new HashSet(Objects.requireNonNull(applications)))); + if (applications.isEmpty()) { + throw new IllegalArgumentException("Applications cannot be empty. Simply don't add this privilege at all."); + } + } + + @SuppressWarnings("unchecked") + public Set getApplications() { + return (Set)getRaw().get(APPLICATIONS); + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java index 0f1632a84648c..dc77661848153 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java @@ -41,8 +41,31 @@ import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +/** + * Represents an aggregation of privileges. This does not have a name + * identifier. + */ public final class Role implements ToXContentObject { + public static final String NONE_CLUSTER_PRIVILEGE_NAME = "none"; + public static final String ALL_CLUSTER_PRIVILEGE_NAME = "all"; + public static final String MONITOR_CLUSTER_PRIVILEGE_NAME = "monitor"; + public static final String MONITOR_ML_CLUSTER_PRIVILEGE_NAME = "monitor_ml"; + public static final String MONITOR_WATCHER_CLUSTER_PRIVILEGE_NAME = "monitor_watcher"; + public static final String MONITOR_ROLLUP_CLUSTER_PRIVILEGE_NAME = "monitor_rollup"; + public static final String MANAGE_CLUSTER_PRIVILEGE_NAME = "manage"; + public static final String MANAGE_ML_CLUSTER_PRIVILEGE_NAME = "manage_ml"; + public static final String MANAGE_WATCHER_CLUSTER_PRIVILEGE_NAME = "manage_watcher"; + public static final String MANAGE_ROLLUP_CLUSTER_PRIVILEGE_NAME = "manage_rollup"; + public static final String MANAGE_INDEX_TEMPLATES_CLUSTER_PRIVILEGE_NAME = "manage_index_templates"; + public static final String MANAGE_INGEST_PIPELINES_CLUSTER_PRIVILEGE_NAME = "manage_ingest_pipelines"; + public static final String TRANSPORT_CLIENT_CLUSTER_PRIVILEGE_NAME = "transport_client"; + public static final String MANAGE_SECURITY_CLUSTER_PRIVILEGE_NAME = "manage_security"; + public static final String MANAGE_SAML_CLUSTER_PRIVILEGE_NAME = "manage_saml"; + public static final String MANAGE_PIPELINE_CLUSTER_PRIVILEGE_NAME = "manage_pipeline"; + public static final String MANAGE_CCR_CLUSTER_PRIVILEGE_NAME = "manage_ccr"; + public static final String READ_CCR_CLUSTER_PRIVILEGE_NAME = "read_ccr"; + public static final ParseField CLUSTER = new ParseField("cluster"); public static final ParseField GLOBAL = new ParseField("global"); public static final ParseField INDICES = new ParseField("indices"); @@ -71,7 +94,7 @@ public final class Role implements ToXContentObject { PARSER.declareFieldArray(optionalConstructorArg(), IndicesPrivileges.PARSER, INDICES, ValueType.OBJECT_ARRAY); PARSER.declareFieldArray(optionalConstructorArg(), ApplicationResourcePrivileges.PARSER, APPLICATIONS, ValueType.OBJECT_ARRAY); PARSER.declareStringArray(optionalConstructorArg(), RUN_AS); - PARSER.>declareObject(constructorArg(), (parser, c) -> parser.map(), METADATA); + PARSER.declareObject(constructorArg(), (parser, c) -> parser.map(), METADATA); } private final Set clusterPrivileges; From 1d9f874c7afe4ebfd5d894852563f45a8112c084 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Wed, 7 Nov 2018 17:01:43 +0200 Subject: [PATCH 20/26] ApplicationResourcePrivilegesTests --- .../ApplicationResourcePrivilegesTests.java | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivilegesTests.java diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivilegesTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivilegesTests.java new file mode 100644 index 0000000000000..9575363a40963 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivilegesTests.java @@ -0,0 +1,77 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.security.user.privileges; + +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractXContentTestCase; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import static org.hamcrest.Matchers.is; + +public class ApplicationResourcePrivilegesTests extends AbstractXContentTestCase { + + @Override + protected ApplicationResourcePrivileges createTestInstance() { + return new ApplicationResourcePrivileges(randomAlphaOfLengthBetween(1, 8), + Arrays.asList(randomArray(1, 8, size -> new String[size], () -> randomAlphaOfLengthBetween(1, 8))), + Arrays.asList(randomArray(1, 8, size -> new String[size], () -> randomAlphaOfLengthBetween(1, 8)))); + } + + @Override + protected ApplicationResourcePrivileges doParseInstance(XContentParser parser) throws IOException { + return ApplicationResourcePrivileges.fromXContent(parser); + } + + @Override + protected boolean supportsUnknownFields() { + return false; + } + + public void testEmptyApplicationName() { + final String emptyApplicationName = randomBoolean() ? "" : null; + final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> new ApplicationResourcePrivileges(emptyApplicationName, + Arrays.asList(randomArray(1, 8, size -> new String[size], () -> randomAlphaOfLengthBetween(1, 8))), + Arrays.asList(randomArray(1, 8, size -> new String[size], () -> randomAlphaOfLengthBetween(1, 8))))); + assertThat(e.getMessage(), is("application privileges must have an application name")); + } + + public void testEmptyPrivileges() { + final Collection emptyPrivileges = randomBoolean() ? Collections.emptyList() : null; + final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> new ApplicationResourcePrivileges(randomAlphaOfLengthBetween(1, 8), + emptyPrivileges, + Arrays.asList(randomArray(1, 8, size -> new String[size], () -> randomAlphaOfLengthBetween(1, 8))))); + assertThat(e.getMessage(), is("application privileges must define at least one privilege")); + } + + public void testEmptyResources() { + final Collection emptyResources = randomBoolean() ? Collections.emptyList() : null; + final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> new ApplicationResourcePrivileges(randomAlphaOfLengthBetween(1, 8), + Arrays.asList(randomArray(1, 8, size -> new String[size], () -> randomAlphaOfLengthBetween(1, 8))), + emptyResources)); + assertThat(e.getMessage(), is("application privileges must refer to at least one resource")); + } +} From d4170f2406c169341274aaacfa578833305840ce Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Thu, 8 Nov 2018 00:53:05 +0200 Subject: [PATCH 21/26] WIP --- .../ApplicationResourcePrivileges.java | 5 +- .../user/privileges/GlobalPrivileges.java | 6 +- .../privileges/GlobalScopedPrivilege.java | 22 +++++-- .../user/privileges/IndicesPrivileges.java | 6 +- .../ManageApplicationPrivilege.java | 3 - .../privileges/GlobalPrivilegesTests.java | 57 +++++++++++++++++++ 6 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java index 1baf6f00be119..c7ac6502649d7 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java @@ -41,7 +41,7 @@ * Represents privileges over resources that are scoped under an application. * The application, resources and privileges are completely managed by the * client and can be "arbitrary" string identifiers. Elasticsearch is not - * concerned by resources under an application scope. + * concerned by any resources under an application scope. */ public final class ApplicationResourcePrivileges implements ToXContentObject { @@ -52,6 +52,9 @@ public final class ApplicationResourcePrivileges implements ToXContentObject { @SuppressWarnings("unchecked") static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( "application_privileges", false, constructorObjects -> { + // Don't ignore unknown fields. It is dangerous if the object we parse is also + // part of a request that we build later on, and the fields that we now ignore will + // end up being implicitly set to null in that request. int i = 0; final String application = (String) constructorObjects[i++]; final Collection privileges = (Collection) constructorObjects[i++]; diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java index ca2b926a1f5db..fb2b1f2b9c8f7 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java @@ -47,7 +47,9 @@ public final class GlobalPrivileges implements ToXContentObject { @SuppressWarnings("unchecked") static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("global_application_privileges", - true, constructorObjects -> { + false, constructorObjects -> { + // ignore_unknown_fields is irrelevant here anyway, but I prefer it to false, + // because it conveys strictness (woop woop) return new GlobalPrivileges((Collection) constructorObjects[0]); }); @@ -58,7 +60,7 @@ public final class GlobalPrivileges implements ToXContentObject { private final Set applicationPrivileges; /** - * Constructs global privileges from the set of application privileges. + * Constructs global privileges by bundling the set of application privileges. * * @param applicationPrivileges * The privileges over applications. diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java index 8d7c28eda733b..2074eb2386198 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java @@ -28,7 +28,8 @@ /** * Represents generic global application privileges that can be scoped for each - * application. The privilege definition is outside the Elasticsearch control. + * application. The privilege definition, as well as the scope identifier, are + * outside of the Elasticsearch jurisdiction. */ public class GlobalScopedPrivilege { @@ -36,15 +37,23 @@ public class GlobalScopedPrivilege { private final Map privilege; /** - * Constructs privilege over some "scope". The "scope" is usually an application - * name but there is no constraint over this identifier. + * Constructs privileges under some "scope". The "scope" is commonly an + * "application" name but there is really no constraint over this identifier + * from Elasticsearch's POV. The privilege definition is also out of + * Elasticsearch's control. * - * @param scope The scope of the privilege. - * @param privilege The privilege definition. This is out of the Elasticsearch control. + * @param scope + * The scope of the privilege. + * @param privilege + * The privilege definition. This is out of the Elasticsearch's + * control. */ public GlobalScopedPrivilege(String scope, Map privilege) { this.scope = Objects.requireNonNull(scope); this.privilege = Collections.unmodifiableMap(Objects.requireNonNull(privilege)); + if (privilege.isEmpty()) { + throw new IllegalArgumentException("Privileges cannot be empty. Simply don't add this privilege at all."); + } } public String getScope() { @@ -56,6 +65,9 @@ public Map getRaw() { } public static GlobalScopedPrivilege fromXContent(String scope, XContentParser parser) throws IOException { + // parser is still placed on the field name, advance to next token (field value) + assert parser.currentToken().equals(XContentParser.Token.FIELD_NAME); + parser.nextToken(); return new GlobalScopedPrivilege(scope, parser.map()); } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java index 17c0642995608..d83f8bcc56458 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java @@ -43,9 +43,9 @@ /** * Represents privileges over indices. There is a canonical set of privilege * names (eg. {@code IndicesPrivileges#READ_PRIVILEGE_NAME}) but there is - * flexibility in defining finer grained, more specialized, privileges. This - * also encapsulates field and document level security privileges. These allow - * to control what fields or documents are readable or queryable. + * flexibility in the definition of finer grained, more specialized, privileges. + * This also encapsulates field and document level security privileges. These + * allow to control what fields or documents are readable or queryable. */ public final class IndicesPrivileges implements ToXContentObject { diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java index c698ad611414b..cda9a25d18971 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java @@ -36,9 +36,6 @@ public class ManageApplicationPrivilege extends GlobalScopedPrivilege { public ManageApplicationPrivilege(Collection applications) { super(SCOPE, Collections.singletonMap(APPLICATIONS, new HashSet(Objects.requireNonNull(applications)))); - if (applications.isEmpty()) { - throw new IllegalArgumentException("Applications cannot be empty. Simply don't add this privilege at all."); - } } @SuppressWarnings("unchecked") diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java new file mode 100644 index 0000000000000..b548cc56e4a65 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java @@ -0,0 +1,57 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.security.user.privileges; + +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractXContentTestCase; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class GlobalPrivilegesTests extends AbstractXContentTestCase { + + @Override + protected GlobalPrivileges createTestInstance() { + final List privilegeList = Arrays + .asList(randomArray(0, 2, size -> new GlobalScopedPrivilege[size], () -> buildRandomGlobalScopedPrivilege())); + return new GlobalPrivileges(privilegeList); + } + + @Override + protected GlobalPrivileges doParseInstance(XContentParser parser) throws IOException { + return GlobalPrivileges.fromXContent(parser); + } + + @Override + protected boolean supportsUnknownFields() { + return false; // true really means inserting bogus privileges + } + + private static GlobalScopedPrivilege buildRandomGlobalScopedPrivilege() { + final Map privilege = new HashMap<>(); + for (int i = 0; i < randomIntBetween(1, 3); i++) { + privilege.put(randomAlphaOfLength(8), ""); + } + return new GlobalScopedPrivilege(randomAlphaOfLengthBetween(0, 2), privilege); + } +} From 986912402a6fb38b13daf9826cfb42aef8bb9807 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Thu, 8 Nov 2018 03:40:46 +0200 Subject: [PATCH 22/26] More tests --- .../user/privileges/GlobalPrivileges.java | 12 ++++- .../privileges/GlobalScopedPrivilege.java | 6 +-- .../privileges/GlobalPrivilegesTests.java | 45 +++++++++++++++++-- 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java index fb2b1f2b9c8f7..b8e980f8a47b7 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java @@ -31,6 +31,7 @@ import java.util.HashSet; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; @@ -66,8 +67,15 @@ public final class GlobalPrivileges implements ToXContentObject { * The privileges over applications. */ public GlobalPrivileges(Collection applicationPrivileges) { - this.applicationPrivileges = applicationPrivileges == null ? Collections.emptySet() - : Collections.unmodifiableSet(new HashSet<>(applicationPrivileges)); + if (applicationPrivileges == null || applicationPrivileges.isEmpty()) { + throw new IllegalArgumentException("Application privileges cannot be empty or null"); + } + this.applicationPrivileges = Collections.unmodifiableSet(new HashSet<>(Objects.requireNonNull(applicationPrivileges))); + final Set allScopes = this.applicationPrivileges.stream().map(p -> p.getScope()).collect(Collectors.toSet()); + if (allScopes.size() != this.applicationPrivileges.size()) { + throw new IllegalArgumentException( + "Application privileges have the same scope but the privileges differ. Only one privilege for any one scope is allowed."); + } } @Override diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java index 2074eb2386198..7c1dda9c1e7df 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java @@ -50,10 +50,10 @@ public class GlobalScopedPrivilege { */ public GlobalScopedPrivilege(String scope, Map privilege) { this.scope = Objects.requireNonNull(scope); - this.privilege = Collections.unmodifiableMap(Objects.requireNonNull(privilege)); - if (privilege.isEmpty()) { - throw new IllegalArgumentException("Privileges cannot be empty. Simply don't add this privilege at all."); + if (privilege == null || privilege.isEmpty()) { + throw new IllegalArgumentException("Privileges cannot be empty or null"); } + this.privilege = Collections.unmodifiableMap(privilege); } public String getScope() { diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java index b548cc56e4a65..66321cc3990ea 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java @@ -24,16 +24,21 @@ import java.io.IOException; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import static org.hamcrest.Matchers.is; + public class GlobalPrivilegesTests extends AbstractXContentTestCase { + private static long idCounter = 0; + @Override protected GlobalPrivileges createTestInstance() { final List privilegeList = Arrays - .asList(randomArray(0, 2, size -> new GlobalScopedPrivilege[size], () -> buildRandomGlobalScopedPrivilege())); + .asList(randomArray(1, 4, size -> new GlobalScopedPrivilege[size], () -> buildRandomGlobalScopedPrivilege())); return new GlobalPrivileges(privilegeList); } @@ -46,12 +51,44 @@ protected GlobalPrivileges doParseInstance(XContentParser parser) throws IOExcep protected boolean supportsUnknownFields() { return false; // true really means inserting bogus privileges } + + public void testEmptyOrNullGlobalScopedPrivilege() { + final Map privilege = randomBoolean() ? null : Collections.emptyMap(); + final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> new GlobalScopedPrivilege(randomAlphaOfLength(2), privilege)); + assertThat(e.getMessage(), is("Privileges cannot be empty or null")); + } + + public void testEmptyOrNullGlobalPrivileges() { + final List privileges = randomBoolean() ? null : Collections.emptyList(); + final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new GlobalPrivileges(privileges)); + assertThat(e.getMessage(), is("Application privileges cannot be empty or null")); + } + + public void testDuplicateGlobalScopedPrivilege() { + final GlobalScopedPrivilege privilege = buildRandomGlobalScopedPrivilege(); + // duplicate + final GlobalScopedPrivilege privilege2 = new GlobalScopedPrivilege(privilege.getScope(), new HashMap<>(privilege.getRaw())); + final GlobalPrivileges globalPrivilege = new GlobalPrivileges(Arrays.asList(privilege, privilege2)); + assertThat(globalPrivilege.getPrivileges().size(), is(1)); + assertThat(globalPrivilege.getPrivileges().iterator().next(), is(privilege)); + } + + public void testSameScopeGlobalScopedPrivilege() { + final GlobalScopedPrivilege privilege = buildRandomGlobalScopedPrivilege(); + final GlobalScopedPrivilege sameScopePrivilege = new GlobalScopedPrivilege(privilege.getScope(), + buildRandomGlobalScopedPrivilege().getRaw()); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> new GlobalPrivileges(Arrays.asList(privilege, sameScopePrivilege))); + assertThat(e.getMessage(), is( + "Application privileges have the same scope but the privileges differ. Only one privilege for any one scope is allowed.")); + } private static GlobalScopedPrivilege buildRandomGlobalScopedPrivilege() { final Map privilege = new HashMap<>(); - for (int i = 0; i < randomIntBetween(1, 3); i++) { - privilege.put(randomAlphaOfLength(8), ""); + for (int i = 0; i < randomIntBetween(1, 4); i++) { + privilege.put(randomAlphaOfLength(2) + idCounter++, randomAlphaOfLengthBetween(1, 4)); } - return new GlobalScopedPrivilege(randomAlphaOfLengthBetween(0, 2), privilege); + return new GlobalScopedPrivilege(randomAlphaOfLength(2) + idCounter++, privilege); } } From 3ead50cc3adb6dd33e4eb5b06c78270cfef41072 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Thu, 8 Nov 2018 03:43:40 +0200 Subject: [PATCH 23/26] More docs --- .../client/security/user/privileges/GlobalPrivileges.java | 4 ++-- .../elasticsearch/client/security/user/privileges/Role.java | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java index b8e980f8a47b7..00e46ded20917 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java @@ -49,8 +49,8 @@ public final class GlobalPrivileges implements ToXContentObject { @SuppressWarnings("unchecked") static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("global_application_privileges", false, constructorObjects -> { - // ignore_unknown_fields is irrelevant here anyway, but I prefer it to false, - // because it conveys strictness (woop woop) + // ignore_unknown_fields is irrelevant here anyway, but let's keep it to false + // because this conveys strictness (woop woop) return new GlobalPrivileges((Collection) constructorObjects[0]); }); diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java index dc77661848153..659a1fa596de1 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java @@ -76,6 +76,9 @@ public final class Role implements ToXContentObject { @SuppressWarnings("unchecked") private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("role_descriptor", false, constructorObjects -> { + // Don't ignore unknown fields. It is dangerous if the object we parse is also + // part of a request that we build later on, and the fields that we now ignore + // will end up being implicitly set to null in that request. int i = 0; final Collection clusterPrivileges = (Collection) constructorObjects[i++]; final GlobalPrivileges globalApplicationPrivileges = (GlobalPrivileges) constructorObjects[i++]; From b68a3cf87926dea558916d1107b12eac030a4967 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Thu, 8 Nov 2018 18:36:21 +0200 Subject: [PATCH 24/26] WIP --- ...ege.java => GlobalOperationPrivilege.java} | 28 ++++++++----- .../user/privileges/GlobalPrivileges.java | 41 +++++++++++-------- .../ManageApplicationPrivilege.java | 2 +- .../privileges/GlobalPrivilegesTests.java | 20 ++++----- 4 files changed, 53 insertions(+), 38 deletions(-) rename client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/{GlobalScopedPrivilege.java => GlobalOperationPrivilege.java} (73%) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalOperationPrivilege.java similarity index 73% rename from client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java rename to client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalOperationPrivilege.java index 7c1dda9c1e7df..f8d6f321c206b 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalScopedPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalOperationPrivilege.java @@ -31,9 +31,10 @@ * application. The privilege definition, as well as the scope identifier, are * outside of the Elasticsearch jurisdiction. */ -public class GlobalScopedPrivilege { +public class GlobalOperationPrivilege { - private final String scope; + private final String category; + private final String operation; private final Map privilege; /** @@ -48,27 +49,32 @@ public class GlobalScopedPrivilege { * The privilege definition. This is out of the Elasticsearch's * control. */ - public GlobalScopedPrivilege(String scope, Map privilege) { - this.scope = Objects.requireNonNull(scope); + public GlobalOperationPrivilege(String category, String operation, Map privilege) { + this.category = Objects.requireNonNull(category); + this.operation = Objects.requireNonNull(operation); if (privilege == null || privilege.isEmpty()) { throw new IllegalArgumentException("Privileges cannot be empty or null"); } this.privilege = Collections.unmodifiableMap(privilege); } - public String getScope() { - return scope; + public String getCategory() { + return category; + } + + public String getOperation() { + return operation; } public Map getRaw() { return privilege; } - public static GlobalScopedPrivilege fromXContent(String scope, XContentParser parser) throws IOException { + public static GlobalOperationPrivilege fromXContent(String category, String scope, XContentParser parser) throws IOException { // parser is still placed on the field name, advance to next token (field value) assert parser.currentToken().equals(XContentParser.Token.FIELD_NAME); parser.nextToken(); - return new GlobalScopedPrivilege(scope, parser.map()); + return new GlobalOperationPrivilege(category, scope, parser.map()); } @Override @@ -79,13 +85,13 @@ public boolean equals(Object o) { if (o == null || this.getClass() != o.getClass()) { return false; } - final GlobalScopedPrivilege that = (GlobalScopedPrivilege) o; - return scope.equals(that.scope) && privilege.equals(that.privilege); + final GlobalOperationPrivilege that = (GlobalOperationPrivilege) o; + return category.equals(that.category) && operation.equals(that.operation) && privilege.equals(that.privilege); } @Override public int hashCode() { - return Objects.hash(scope, privilege); + return Objects.hash(category, operation, privilege); } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java index 00e46ded20917..7b44f505149d1 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java @@ -26,9 +26,11 @@ import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -44,21 +46,26 @@ */ public final class GlobalPrivileges implements ToXContentObject { - static final ParseField APPLICATION = new ParseField("application"); + // when categories change, adapting this field should suffice + static final List CATEGORIES = Collections.unmodifiableList(Arrays.asList("application")); @SuppressWarnings("unchecked") static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("global_application_privileges", false, constructorObjects -> { // ignore_unknown_fields is irrelevant here anyway, but let's keep it to false // because this conveys strictness (woop woop) - return new GlobalPrivileges((Collection) constructorObjects[0]); + return new GlobalPrivileges((Collection) constructorObjects[0]); }); static { - PARSER.declareNamedObjects(optionalConstructorArg(), (p, c, n) -> GlobalScopedPrivilege.fromXContent(n, p), APPLICATION); + for (final String category : CATEGORIES) { + PARSER.declareNamedObjects(optionalConstructorArg(), + (parser, context, operation) -> GlobalOperationPrivilege.fromXContent(category, operation, parser), + new ParseField(category)); + } } - private final Set applicationPrivileges; + private final Set privileges; /** * Constructs global privileges by bundling the set of application privileges. @@ -66,13 +73,13 @@ public final class GlobalPrivileges implements ToXContentObject { * @param applicationPrivileges * The privileges over applications. */ - public GlobalPrivileges(Collection applicationPrivileges) { + public GlobalPrivileges(Collection applicationPrivileges) { if (applicationPrivileges == null || applicationPrivileges.isEmpty()) { throw new IllegalArgumentException("Application privileges cannot be empty or null"); } - this.applicationPrivileges = Collections.unmodifiableSet(new HashSet<>(Objects.requireNonNull(applicationPrivileges))); - final Set allScopes = this.applicationPrivileges.stream().map(p -> p.getScope()).collect(Collectors.toSet()); - if (allScopes.size() != this.applicationPrivileges.size()) { + this.privileges = Collections.unmodifiableSet(new HashSet<>(Objects.requireNonNull(applicationPrivileges))); + final Set allScopes = this.privileges.stream().map(p -> p.getScope()).collect(Collectors.toSet()); + if (allScopes.size() != this.privileges.size()) { throw new IllegalArgumentException( "Application privileges have the same scope but the privileges differ. Only one privilege for any one scope is allowed."); } @@ -81,11 +88,13 @@ public GlobalPrivileges(Collection applicationP @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); - builder.startObject(APPLICATION.getPreferredName()); - for (final GlobalScopedPrivilege privilege : applicationPrivileges) { - builder.field(privilege.getScope(), privilege.getRaw()); + for (final String category : CATEGORIES) { + builder.startObject(category); + for (final GlobalOperationPrivilege privilege : privileges) { + builder.field(privilege.getScope(), privilege.getRaw()); + } + builder.endObject(); } - builder.endObject(); return builder.endObject(); } @@ -93,8 +102,8 @@ public static GlobalPrivileges fromXContent(XContentParser parser) { return PARSER.apply(parser, null); } - public Set getPrivileges() { - return applicationPrivileges; + public Set getPrivileges() { + return privileges; } @Override @@ -106,12 +115,12 @@ public boolean equals(Object o) { return false; } final GlobalPrivileges that = (GlobalPrivileges) o; - return applicationPrivileges.equals(that.applicationPrivileges); + return privileges.equals(that.privileges); } @Override public int hashCode() { - return Objects.hash(applicationPrivileges); + return Objects.hash(privileges); } } \ No newline at end of file diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java index cda9a25d18971..69d51a94a8f85 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java @@ -29,7 +29,7 @@ * Represents the privilege to "manage" certain applications. The "manage" * privilege is actually defined outside of Elasticsearch. */ -public class ManageApplicationPrivilege extends GlobalScopedPrivilege { +public class ManageApplicationPrivilege extends GlobalOperationPrivilege { private static final String SCOPE = "manage"; private static final String APPLICATIONS = "applications"; diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java index 66321cc3990ea..42b12386456dd 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java @@ -37,8 +37,8 @@ public class GlobalPrivilegesTests extends AbstractXContentTestCase privilegeList = Arrays - .asList(randomArray(1, 4, size -> new GlobalScopedPrivilege[size], () -> buildRandomGlobalScopedPrivilege())); + final List privilegeList = Arrays + .asList(randomArray(1, 4, size -> new GlobalOperationPrivilege[size], () -> buildRandomGlobalScopedPrivilege())); return new GlobalPrivileges(privilegeList); } @@ -55,28 +55,28 @@ protected boolean supportsUnknownFields() { public void testEmptyOrNullGlobalScopedPrivilege() { final Map privilege = randomBoolean() ? null : Collections.emptyMap(); final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> new GlobalScopedPrivilege(randomAlphaOfLength(2), privilege)); + () -> new GlobalOperationPrivilege(randomAlphaOfLength(2), privilege)); assertThat(e.getMessage(), is("Privileges cannot be empty or null")); } public void testEmptyOrNullGlobalPrivileges() { - final List privileges = randomBoolean() ? null : Collections.emptyList(); + final List privileges = randomBoolean() ? null : Collections.emptyList(); final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new GlobalPrivileges(privileges)); assertThat(e.getMessage(), is("Application privileges cannot be empty or null")); } public void testDuplicateGlobalScopedPrivilege() { - final GlobalScopedPrivilege privilege = buildRandomGlobalScopedPrivilege(); + final GlobalOperationPrivilege privilege = buildRandomGlobalScopedPrivilege(); // duplicate - final GlobalScopedPrivilege privilege2 = new GlobalScopedPrivilege(privilege.getScope(), new HashMap<>(privilege.getRaw())); + final GlobalOperationPrivilege privilege2 = new GlobalOperationPrivilege(privilege.getScope(), new HashMap<>(privilege.getRaw())); final GlobalPrivileges globalPrivilege = new GlobalPrivileges(Arrays.asList(privilege, privilege2)); assertThat(globalPrivilege.getPrivileges().size(), is(1)); assertThat(globalPrivilege.getPrivileges().iterator().next(), is(privilege)); } public void testSameScopeGlobalScopedPrivilege() { - final GlobalScopedPrivilege privilege = buildRandomGlobalScopedPrivilege(); - final GlobalScopedPrivilege sameScopePrivilege = new GlobalScopedPrivilege(privilege.getScope(), + final GlobalOperationPrivilege privilege = buildRandomGlobalScopedPrivilege(); + final GlobalOperationPrivilege sameScopePrivilege = new GlobalOperationPrivilege(privilege.getScope(), buildRandomGlobalScopedPrivilege().getRaw()); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new GlobalPrivileges(Arrays.asList(privilege, sameScopePrivilege))); @@ -84,11 +84,11 @@ public void testSameScopeGlobalScopedPrivilege() { "Application privileges have the same scope but the privileges differ. Only one privilege for any one scope is allowed.")); } - private static GlobalScopedPrivilege buildRandomGlobalScopedPrivilege() { + private static GlobalOperationPrivilege buildRandomGlobalScopedPrivilege() { final Map privilege = new HashMap<>(); for (int i = 0; i < randomIntBetween(1, 4); i++) { privilege.put(randomAlphaOfLength(2) + idCounter++, randomAlphaOfLengthBetween(1, 4)); } - return new GlobalScopedPrivilege(randomAlphaOfLength(2) + idCounter++, privilege); + return new GlobalOperationPrivilege(randomAlphaOfLength(2) + idCounter++, privilege); } } From 24351b451adc29792663676f8bdd6de15e923111 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Thu, 8 Nov 2018 22:16:39 +0200 Subject: [PATCH 25/26] Done! --- .../ApplicationResourcePrivileges.java | 2 +- .../privileges/GlobalOperationPrivilege.java | 28 ++++---- .../user/privileges/GlobalPrivileges.java | 51 +++++++++------ .../user/privileges/IndicesPrivileges.java | 17 +---- .../ManageApplicationPrivilege.java | 21 ++++-- .../client/security/user/privileges/Role.java | 65 +++++++++++++------ .../privileges/GlobalPrivilegesTests.java | 24 +++---- 7 files changed, 121 insertions(+), 87 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java index c7ac6502649d7..8846e259e26b3 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ApplicationResourcePrivileges.java @@ -40,7 +40,7 @@ /** * Represents privileges over resources that are scoped under an application. * The application, resources and privileges are completely managed by the - * client and can be "arbitrary" string identifiers. Elasticsearch is not + * client and can be arbitrary string identifiers. Elasticsearch is not * concerned by any resources under an application scope. */ public final class ApplicationResourcePrivileges implements ToXContentObject { diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalOperationPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalOperationPrivilege.java index f8d6f321c206b..6957e10011926 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalOperationPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalOperationPrivilege.java @@ -27,9 +27,11 @@ import java.util.Objects; /** - * Represents generic global application privileges that can be scoped for each - * application. The privilege definition, as well as the scope identifier, are - * outside of the Elasticsearch jurisdiction. + * Represents generic global cluster privileges that can be scoped by categories + * and then by operations. The privilege definition, as well as the operation + * identifier, are outside of the Elasticsearch jurisdiction. Categories are + * predefined and enforced by Elasticsearch. It is not permitted to define + * different privileges for the same category and operation. */ public class GlobalOperationPrivilege { @@ -38,13 +40,15 @@ public class GlobalOperationPrivilege { private final Map privilege; /** - * Constructs privileges under some "scope". The "scope" is commonly an - * "application" name but there is really no constraint over this identifier - * from Elasticsearch's POV. The privilege definition is also out of - * Elasticsearch's control. + * Constructs privileges under a certain {@code category} and for some + * {@code operation}. There is no constraint over the {@code operation} + * identifier, only the categories are predefined. The privilege definition is + * also out of Elasticsearch's control. * - * @param scope - * The scope of the privilege. + * @param category + * The category of the privilege. + * @param operation + * The operation of the privilege. * @param privilege * The privilege definition. This is out of the Elasticsearch's * control. @@ -70,11 +74,11 @@ public Map getRaw() { return privilege; } - public static GlobalOperationPrivilege fromXContent(String category, String scope, XContentParser parser) throws IOException { + public static GlobalOperationPrivilege fromXContent(String category, String operation, XContentParser parser) throws IOException { // parser is still placed on the field name, advance to next token (field value) assert parser.currentToken().equals(XContentParser.Token.FIELD_NAME); parser.nextToken(); - return new GlobalOperationPrivilege(category, scope, parser.map()); + return new GlobalOperationPrivilege(category, operation, parser.map()); } @Override @@ -82,7 +86,7 @@ public boolean equals(Object o) { if (this == o) { return true; } - if (o == null || this.getClass() != o.getClass()) { + if (o == null || (false == this instanceof GlobalOperationPrivilege)) { return false; } final GlobalOperationPrivilege that = (GlobalOperationPrivilege) o; diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java index 7b44f505149d1..5cff9206f362b 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java @@ -31,6 +31,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -39,18 +40,19 @@ /** * Represents global privileges. "Global Privilege" is a mantra for granular - * privileges over applications. {@code ApplicationResourcePrivileges} model - * application privileges over resources. This models user privileges over - * applications. Every client is responsible to manage the applications as well - * as the privileges for them. + * generic cluster privileges. These privileges are organized into categories. + * Elasticsearch defines the set of categories. Under each category there are + * operations that are under the clients jurisdiction. The privilege is hence + * defined under an operation under a category. */ public final class GlobalPrivileges implements ToXContentObject { - // when categories change, adapting this field should suffice + // When categories change, adapting this field should suffice. Categories are NOT + // opaque "named_objects", we wish to maintain control over these namespaces static final List CATEGORIES = Collections.unmodifiableList(Arrays.asList("application")); @SuppressWarnings("unchecked") - static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("global_application_privileges", + static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("global_category_privileges", false, constructorObjects -> { // ignore_unknown_fields is irrelevant here anyway, but let's keep it to false // because this conveys strictness (woop woop) @@ -68,30 +70,37 @@ public final class GlobalPrivileges implements ToXContentObject { private final Set privileges; /** - * Constructs global privileges by bundling the set of application privileges. + * Constructs global privileges by bundling the set of privileges. * - * @param applicationPrivileges - * The privileges over applications. + * @param privileges + * The privileges under a category and for an operation in that category. */ - public GlobalPrivileges(Collection applicationPrivileges) { - if (applicationPrivileges == null || applicationPrivileges.isEmpty()) { - throw new IllegalArgumentException("Application privileges cannot be empty or null"); + public GlobalPrivileges(Collection privileges) { + if (privileges == null || privileges.isEmpty()) { + throw new IllegalArgumentException("Privileges cannot be empty or null"); } - this.privileges = Collections.unmodifiableSet(new HashSet<>(Objects.requireNonNull(applicationPrivileges))); - final Set allScopes = this.privileges.stream().map(p -> p.getScope()).collect(Collectors.toSet()); - if (allScopes.size() != this.privileges.size()) { - throw new IllegalArgumentException( - "Application privileges have the same scope but the privileges differ. Only one privilege for any one scope is allowed."); + // duplicates are just ignored + this.privileges = Collections.unmodifiableSet(new HashSet<>(Objects.requireNonNull(privileges))); + final Map> privilegesByCategoryMap = + this.privileges.stream().collect(Collectors.groupingBy(GlobalOperationPrivilege::getCategory)); + for (final Map.Entry> privilegesByCategory : privilegesByCategoryMap.entrySet()) { + // all operations for a specific category + final Set allOperations = privilegesByCategory.getValue().stream().map(p -> p.getOperation()).collect(Collectors.toSet()); + if (allOperations.size() != privilegesByCategory.getValue().size()) { + throw new IllegalArgumentException("Different privileges for the same category and operation are not permitted"); + } } } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + final Map> privilegesByCategoryMap = + this.privileges.stream().collect(Collectors.groupingBy(GlobalOperationPrivilege::getCategory)); builder.startObject(); - for (final String category : CATEGORIES) { - builder.startObject(category); - for (final GlobalOperationPrivilege privilege : privileges) { - builder.field(privilege.getScope(), privilege.getRaw()); + for (final Map.Entry> privilegesByCategory : privilegesByCategoryMap.entrySet()) { + builder.startObject(privilegesByCategory.getKey()); + for (final GlobalOperationPrivilege privilege : privilegesByCategory.getValue()) { + builder.field(privilege.getOperation(), privilege.getRaw()); } builder.endObject(); } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java index d83f8bcc56458..e693a4fea34fa 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/IndicesPrivileges.java @@ -49,21 +49,6 @@ */ public final class IndicesPrivileges implements ToXContentObject { - public static final String NONE_PRIVILEGE_NAME = "none"; - public static final String ALL_PRIVILEGE_NAME = "all"; - public static final String READ_PRIVILEGE_NAME = "read"; - public static final String READ_CROSS_CLUSTER_PRIVILEGE_NAME = "read_cross_cluster"; - public static final String CREATE_PRIVILEGE_NAME = "create"; - public static final String INDEX_PRIVILEGE_NAME = "index"; - public static final String DELETE_PRIVILEGE_NAME = "delete"; - public static final String WRITE_PRIVILEGE_NAME = "write"; - public static final String MONITOR_PRIVILEGE_NAME = "monitor"; - public static final String MANAGE_PRIVILEGE_NAME = "manage"; - public static final String DELETE_INDEX_PRIVILEGE_NAME = "delete_index"; - public static final String CREATE_INDEX_PRIVILEGE_NAME = "create_index"; - public static final String VIEW_INDEX_METADATA_PRIVILEGE_NAME = "view_index_metadata"; - public static final String MANAGE_FOLLOW_INDEX_PRIVILEGE_NAME = "manage_follow_index"; - public static final ParseField NAMES = new ParseField("names"); public static final ParseField PRIVILEGES = new ParseField("privileges"); public static final ParseField FIELD_PERMISSIONS = new ParseField("field_security"); @@ -234,7 +219,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field(PRIVILEGES.getPreferredName(), privileges); if (isUsingFieldLevelSecurity()) { builder.startObject(FIELD_PERMISSIONS.getPreferredName()); - if (limitsGrantedFields()) { + if (grantedFields != null) { builder.field(GRANT_FIELDS.getPreferredName(), grantedFields); } if (hasDeniedFields()) { diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java index 69d51a94a8f85..9356c2ef0e867 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/ManageApplicationPrivilege.java @@ -31,15 +31,26 @@ */ public class ManageApplicationPrivilege extends GlobalOperationPrivilege { - private static final String SCOPE = "manage"; - private static final String APPLICATIONS = "applications"; + private static final String CATEGORY = "application"; + private static final String OPERATION = "manage"; + private static final String KEY = "applications"; public ManageApplicationPrivilege(Collection applications) { - super(SCOPE, Collections.singletonMap(APPLICATIONS, new HashSet(Objects.requireNonNull(applications)))); + super(CATEGORY, OPERATION, Collections.singletonMap(KEY, new HashSet(Objects.requireNonNull(applications)))); } @SuppressWarnings("unchecked") - public Set getApplications() { - return (Set)getRaw().get(APPLICATIONS); + public Set getManagedApplications() { + return (Set)getRaw().get(KEY); + } + + @Override + public boolean equals(Object o) { + return super.equals(o); + } + + @Override + public int hashCode() { + return super.hashCode(); } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java index 659a1fa596de1..78265196ee819 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java @@ -47,25 +47,6 @@ */ public final class Role implements ToXContentObject { - public static final String NONE_CLUSTER_PRIVILEGE_NAME = "none"; - public static final String ALL_CLUSTER_PRIVILEGE_NAME = "all"; - public static final String MONITOR_CLUSTER_PRIVILEGE_NAME = "monitor"; - public static final String MONITOR_ML_CLUSTER_PRIVILEGE_NAME = "monitor_ml"; - public static final String MONITOR_WATCHER_CLUSTER_PRIVILEGE_NAME = "monitor_watcher"; - public static final String MONITOR_ROLLUP_CLUSTER_PRIVILEGE_NAME = "monitor_rollup"; - public static final String MANAGE_CLUSTER_PRIVILEGE_NAME = "manage"; - public static final String MANAGE_ML_CLUSTER_PRIVILEGE_NAME = "manage_ml"; - public static final String MANAGE_WATCHER_CLUSTER_PRIVILEGE_NAME = "manage_watcher"; - public static final String MANAGE_ROLLUP_CLUSTER_PRIVILEGE_NAME = "manage_rollup"; - public static final String MANAGE_INDEX_TEMPLATES_CLUSTER_PRIVILEGE_NAME = "manage_index_templates"; - public static final String MANAGE_INGEST_PIPELINES_CLUSTER_PRIVILEGE_NAME = "manage_ingest_pipelines"; - public static final String TRANSPORT_CLIENT_CLUSTER_PRIVILEGE_NAME = "transport_client"; - public static final String MANAGE_SECURITY_CLUSTER_PRIVILEGE_NAME = "manage_security"; - public static final String MANAGE_SAML_CLUSTER_PRIVILEGE_NAME = "manage_saml"; - public static final String MANAGE_PIPELINE_CLUSTER_PRIVILEGE_NAME = "manage_pipeline"; - public static final String MANAGE_CCR_CLUSTER_PRIVILEGE_NAME = "manage_ccr"; - public static final String READ_CCR_CLUSTER_PRIVILEGE_NAME = "read_ccr"; - public static final ParseField CLUSTER = new ParseField("cluster"); public static final ParseField GLOBAL = new ParseField("global"); public static final ParseField INDICES = new ParseField("indices"); @@ -233,7 +214,7 @@ public Builder clusterPrivileges(Collection clusterPrivileges) { return this; } - public Builder manageApplicationPrivileges(GlobalPrivileges globalApplicationPrivileges) { + public Builder glabalApplicationPrivileges(GlobalPrivileges globalApplicationPrivileges) { this.globalApplicationPrivileges = globalApplicationPrivileges; return this; } @@ -282,4 +263,48 @@ public Role build() { } } + /** + * Canonical cluster privilege names. There is no enforcement to only use these. + */ + public static class ClusterPrivilegeName { + public static final String NONE = "none"; + public static final String ALL = "all"; + public static final String MONITOR = "monitor"; + public static final String MONITOR_ML = "monitor_ml"; + public static final String MONITOR_WATCHER = "monitor_watcher"; + public static final String MONITOR_ROLLUP = "monitor_rollup"; + public static final String MANAGE = "manage"; + public static final String MANAGE_ML = "manage_ml"; + public static final String MANAGE_WATCHER = "manage_watcher"; + public static final String MANAGE_ROLLUP = "manage_rollup"; + public static final String MANAGE_INDEX_TEMPLATES = "manage_index_templates"; + public static final String MANAGE_INGEST_PIPELINES = "manage_ingest_pipelines"; + public static final String TRANSPORT_CLIENT = "transport_client"; + public static final String MANAGE_SECURITY = "manage_security"; + public static final String MANAGE_SAML = "manage_saml"; + public static final String MANAGE_PIPELINE = "manage_pipeline"; + public static final String MANAGE_CCR = "manage_ccr"; + public static final String READ_CCR = "read_ccr"; + } + + /** + * Canonical index privilege names. There is no enforcement to only use these. + */ + public static class IndexPrivilegeName { + public static final String NONE = "none"; + public static final String ALL = "all"; + public static final String READ = "read"; + public static final String READ_CROSS = "read_cross_cluster"; + public static final String CREATE = "create"; + public static final String INDEX = "index"; + public static final String DELETE = "delete"; + public static final String WRITE = "write"; + public static final String MONITOR = "monitor"; + public static final String MANAGE = "manage"; + public static final String DELETE_INDEX = "delete_index"; + public static final String CREATE_INDEX = "create_index"; + public static final String VIEW_INDEX_METADATA = "view_index_metadata"; + public static final String MANAGE_FOLLOW_INDEX = "manage_follow_index"; + } + } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java index 42b12386456dd..bb1e933089189 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/user/privileges/GlobalPrivilegesTests.java @@ -52,36 +52,36 @@ protected boolean supportsUnknownFields() { return false; // true really means inserting bogus privileges } - public void testEmptyOrNullGlobalScopedPrivilege() { + public void testEmptyOrNullGlobalOperationPrivilege() { final Map privilege = randomBoolean() ? null : Collections.emptyMap(); final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> new GlobalOperationPrivilege(randomAlphaOfLength(2), privilege)); + () -> new GlobalOperationPrivilege(randomAlphaOfLength(2), randomAlphaOfLength(2), privilege)); assertThat(e.getMessage(), is("Privileges cannot be empty or null")); } public void testEmptyOrNullGlobalPrivileges() { final List privileges = randomBoolean() ? null : Collections.emptyList(); final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new GlobalPrivileges(privileges)); - assertThat(e.getMessage(), is("Application privileges cannot be empty or null")); + assertThat(e.getMessage(), is("Privileges cannot be empty or null")); } - public void testDuplicateGlobalScopedPrivilege() { + public void testDuplicateGlobalOperationPrivilege() { final GlobalOperationPrivilege privilege = buildRandomGlobalScopedPrivilege(); // duplicate - final GlobalOperationPrivilege privilege2 = new GlobalOperationPrivilege(privilege.getScope(), new HashMap<>(privilege.getRaw())); + final GlobalOperationPrivilege privilege2 = new GlobalOperationPrivilege(privilege.getCategory(), privilege.getOperation(), + new HashMap<>(privilege.getRaw())); final GlobalPrivileges globalPrivilege = new GlobalPrivileges(Arrays.asList(privilege, privilege2)); assertThat(globalPrivilege.getPrivileges().size(), is(1)); assertThat(globalPrivilege.getPrivileges().iterator().next(), is(privilege)); } - public void testSameScopeGlobalScopedPrivilege() { + public void testSameScopeGlobalOperationPrivilege() { final GlobalOperationPrivilege privilege = buildRandomGlobalScopedPrivilege(); - final GlobalOperationPrivilege sameScopePrivilege = new GlobalOperationPrivilege(privilege.getScope(), - buildRandomGlobalScopedPrivilege().getRaw()); + final GlobalOperationPrivilege sameOperationPrivilege = new GlobalOperationPrivilege(privilege.getCategory(), + privilege.getOperation(), buildRandomGlobalScopedPrivilege().getRaw()); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> new GlobalPrivileges(Arrays.asList(privilege, sameScopePrivilege))); - assertThat(e.getMessage(), is( - "Application privileges have the same scope but the privileges differ. Only one privilege for any one scope is allowed.")); + () -> new GlobalPrivileges(Arrays.asList(privilege, sameOperationPrivilege))); + assertThat(e.getMessage(), is("Different privileges for the same category and operation are not permitted")); } private static GlobalOperationPrivilege buildRandomGlobalScopedPrivilege() { @@ -89,6 +89,6 @@ private static GlobalOperationPrivilege buildRandomGlobalScopedPrivilege() { for (int i = 0; i < randomIntBetween(1, 4); i++) { privilege.put(randomAlphaOfLength(2) + idCounter++, randomAlphaOfLengthBetween(1, 4)); } - return new GlobalOperationPrivilege(randomAlphaOfLength(2) + idCounter++, privilege); + return new GlobalOperationPrivilege("application", randomAlphaOfLength(2) + idCounter++, privilege); } } From 20f14f7a580c8487915cfc6d3eab100774d70fb5 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Fri, 9 Nov 2018 15:56:46 +0200 Subject: [PATCH 26/26] Javadocs and privilegesByCategoryMap as member --- .../privileges/GlobalOperationPrivilege.java | 18 ++++++++---------- .../user/privileges/GlobalPrivileges.java | 14 ++++++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalOperationPrivilege.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalOperationPrivilege.java index 6957e10011926..507d6a5a1956f 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalOperationPrivilege.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalOperationPrivilege.java @@ -28,10 +28,10 @@ /** * Represents generic global cluster privileges that can be scoped by categories - * and then by operations. The privilege definition, as well as the operation - * identifier, are outside of the Elasticsearch jurisdiction. Categories are - * predefined and enforced by Elasticsearch. It is not permitted to define - * different privileges for the same category and operation. + * and then further by operations. The privilege's syntactic and semantic + * meaning is specific to each category and operation; there is no general + * definition template. It is not permitted to define different privileges under + * the same category and operation. */ public class GlobalOperationPrivilege { @@ -40,18 +40,16 @@ public class GlobalOperationPrivilege { private final Map privilege; /** - * Constructs privileges under a certain {@code category} and for some - * {@code operation}. There is no constraint over the {@code operation} - * identifier, only the categories are predefined. The privilege definition is - * also out of Elasticsearch's control. + * Constructs privileges under a specific {@code category} and for some + * {@code operation}. The privilege definition is flexible, it is a {@code Map}, + * and the semantics is bound to the {@code category} and {@code operation}. * * @param category * The category of the privilege. * @param operation * The operation of the privilege. * @param privilege - * The privilege definition. This is out of the Elasticsearch's - * control. + * The privilege definition. */ public GlobalOperationPrivilege(String category, String operation, Map privilege) { this.category = Objects.requireNonNull(category); diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java index 5cff9206f362b..891980765427e 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/GlobalPrivileges.java @@ -68,6 +68,9 @@ public final class GlobalPrivileges implements ToXContentObject { } private final Set privileges; + // same data as in privileges but broken down by categories; internally, it is + // easier to work with this structure + private final Map> privilegesByCategoryMap; /** * Constructs global privileges by bundling the set of privileges. @@ -81,11 +84,12 @@ public GlobalPrivileges(Collection privilege } // duplicates are just ignored this.privileges = Collections.unmodifiableSet(new HashSet<>(Objects.requireNonNull(privileges))); - final Map> privilegesByCategoryMap = - this.privileges.stream().collect(Collectors.groupingBy(GlobalOperationPrivilege::getCategory)); + this.privilegesByCategoryMap = Collections + .unmodifiableMap(this.privileges.stream().collect(Collectors.groupingBy(GlobalOperationPrivilege::getCategory))); for (final Map.Entry> privilegesByCategory : privilegesByCategoryMap.entrySet()) { // all operations for a specific category - final Set allOperations = privilegesByCategory.getValue().stream().map(p -> p.getOperation()).collect(Collectors.toSet()); + final Set allOperations = privilegesByCategory.getValue().stream().map(p -> p.getOperation()) + .collect(Collectors.toSet()); if (allOperations.size() != privilegesByCategory.getValue().size()) { throw new IllegalArgumentException("Different privileges for the same category and operation are not permitted"); } @@ -94,10 +98,8 @@ public GlobalPrivileges(Collection privilege @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - final Map> privilegesByCategoryMap = - this.privileges.stream().collect(Collectors.groupingBy(GlobalOperationPrivilege::getCategory)); builder.startObject(); - for (final Map.Entry> privilegesByCategory : privilegesByCategoryMap.entrySet()) { + for (final Map.Entry> privilegesByCategory : this.privilegesByCategoryMap.entrySet()) { builder.startObject(privilegesByCategory.getKey()); for (final GlobalOperationPrivilege privilege : privilegesByCategory.getValue()) { builder.field(privilege.getOperation(), privilege.getRaw());