From a5ee134c40dd01e2b07008e8ff0211bb76017d2e Mon Sep 17 00:00:00 2001 From: Yogesh Gaikwad <902768+bizybot@users.noreply.github.com> Date: Mon, 29 Oct 2018 10:12:13 +1100 Subject: [PATCH] [HLRC] Add support for get role mappings API (#34637) This commit adds support for get role mappings API in HLRC. --- .../elasticsearch/client/SecurityClient.java | 36 +++++ .../client/SecurityRequestConverters.java | 12 ++ .../security/ExpressionRoleMapping.java | 153 ++++++++++++++++++ .../security/GetRoleMappingsRequest.java | 68 ++++++++ .../security/GetRoleMappingsResponse.java | 70 ++++++++ .../parser/RoleMapperExpressionParser.java | 12 ++ .../SecurityRequestConvertersTests.java | 22 +++ .../SecurityDocumentationIT.java | 122 ++++++++++++++ .../security/ExpressionRoleMappingTests.java | 108 +++++++++++++ .../security/GetRoleMappingsRequestTests.java | 55 +++++++ .../GetRoleMappingsResponseTests.java | 118 ++++++++++++++ .../security/get-role-mappings.asciidoc | 67 ++++++++ .../high-level/supported-apis.asciidoc | 2 + 13 files changed, 845 insertions(+) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/ExpressionRoleMapping.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetRoleMappingsRequest.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetRoleMappingsResponse.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/security/ExpressionRoleMappingTests.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetRoleMappingsRequestTests.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetRoleMappingsResponseTests.java create mode 100644 docs/java-rest/high-level/security/get-role-mappings.asciidoc diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java index e528116da6c..4b94b07c315 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java @@ -29,6 +29,8 @@ import org.elasticsearch.client.security.PutRoleMappingResponse; import org.elasticsearch.client.security.DisableUserRequest; import org.elasticsearch.client.security.EmptyResponse; import org.elasticsearch.client.security.EnableUserRequest; +import org.elasticsearch.client.security.GetRoleMappingsRequest; +import org.elasticsearch.client.security.GetRoleMappingsResponse; import org.elasticsearch.client.security.GetSslCertificatesRequest; import org.elasticsearch.client.security.GetSslCertificatesResponse; import org.elasticsearch.client.security.PutUserRequest; @@ -112,6 +114,40 @@ public final class SecurityClient { PutRoleMappingResponse::fromXContent, listener, emptySet()); } + /** + * Synchronously get role mapping(s). + * See + * the docs for more. + * + * @param request {@link GetRoleMappingsRequest} with role mapping name(s). + * If no role mapping name is provided then retrieves all role mappings. + * @param options the request options (e.g. headers), use + * {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response from the get role mapping call + * @throws IOException in case there is a problem sending the request or + * parsing back the response + */ + public GetRoleMappingsResponse getRoleMappings(final GetRoleMappingsRequest request, final RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::getRoleMappings, + options, GetRoleMappingsResponse::fromXContent, emptySet()); + } + + /** + * Asynchronously get role mapping(s). + * See + * the docs for more. + * + * @param request {@link GetRoleMappingsRequest} with role mapping name(s). + * If no role mapping name is provided then retrieves all role mappings. + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + */ + public void getRoleMappingsAsync(final GetRoleMappingsRequest request, final RequestOptions options, + final ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::getRoleMappings, + options, GetRoleMappingsResponse::fromXContent, listener, emptySet()); + } + /** * Enable a native realm or built-in user synchronously. * See diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityRequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityRequestConverters.java index e3acda9313a..eb727e13612 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityRequestConverters.java @@ -19,6 +19,7 @@ package org.elasticsearch.client; +import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; @@ -28,9 +29,11 @@ import org.elasticsearch.client.security.DeleteRoleRequest; import org.elasticsearch.client.security.PutRoleMappingRequest; import org.elasticsearch.client.security.DisableUserRequest; import org.elasticsearch.client.security.EnableUserRequest; +import org.elasticsearch.client.security.GetRoleMappingsRequest; import org.elasticsearch.client.security.ChangePasswordRequest; import org.elasticsearch.client.security.PutUserRequest; import org.elasticsearch.client.security.SetUserEnabledRequest; +import org.elasticsearch.common.Strings; import java.io.IOException; @@ -78,6 +81,15 @@ final class SecurityRequestConverters { return request; } + static Request getRoleMappings(final GetRoleMappingsRequest getRoleMappingRequest) throws IOException { + RequestConverters.EndpointBuilder builder = new RequestConverters.EndpointBuilder(); + builder.addPathPartAsIs("_xpack/security/role_mapping"); + if (getRoleMappingRequest.getRoleMappingNames().size() > 0) { + builder.addPathPart(Strings.collectionToCommaDelimitedString(getRoleMappingRequest.getRoleMappingNames())); + } + return new Request(HttpGet.METHOD_NAME, builder.build()); + } + static Request enableUser(EnableUserRequest enableUserRequest) { return setUserEnabled(enableUserRequest); } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/ExpressionRoleMapping.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/ExpressionRoleMapping.java new file mode 100644 index 00000000000..9cb78dd9c83 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/ExpressionRoleMapping.java @@ -0,0 +1,153 @@ +/* + * 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; + +import org.elasticsearch.client.security.support.expressiondsl.RoleMapperExpression; +import org.elasticsearch.client.security.support.expressiondsl.parser.RoleMapperExpressionParser; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; + +/** + * A representation of a single role-mapping. + * + * @see RoleMapperExpression + * @see RoleMapperExpressionParser + */ +public final class ExpressionRoleMapping { + + @SuppressWarnings("unchecked") + static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("role-mapping", true, + (args, name) -> new ExpressionRoleMapping(name, (RoleMapperExpression) args[0], (List) args[1], + (Map) args[2], (boolean) args[3])); + + static { + PARSER.declareField(constructorArg(), (parser, context) -> RoleMapperExpressionParser.fromXContent(parser), Fields.RULES, + ObjectParser.ValueType.OBJECT); + PARSER.declareStringArray(constructorArg(), Fields.ROLES); + PARSER.declareField(constructorArg(), XContentParser::map, Fields.METADATA, ObjectParser.ValueType.OBJECT); + PARSER.declareBoolean(constructorArg(), Fields.ENABLED); + } + + private final String name; + private final RoleMapperExpression expression; + private final List roles; + private final Map metadata; + private final boolean enabled; + + /** + * Constructor for role mapping + * + * @param name role mapping name + * @param expr {@link RoleMapperExpression} Expression used for role mapping + * @param roles list of roles to be associated with the user + * @param metadata metadata that helps to identify which roles are assigned + * to the user + * @param enabled a flag when {@code true} signifies the role mapping is active + */ + public ExpressionRoleMapping(final String name, final RoleMapperExpression expr, final List roles, + final Map metadata, boolean enabled) { + this.name = name; + this.expression = expr; + this.roles = Collections.unmodifiableList(roles); + this.metadata = (metadata == null) ? Collections.emptyMap() : Collections.unmodifiableMap(metadata); + this.enabled = enabled; + } + + public String getName() { + return name; + } + + public RoleMapperExpression getExpression() { + return expression; + } + + public List getRoles() { + return roles; + } + + public Map getMetadata() { + return metadata; + } + + public boolean isEnabled() { + return enabled; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (enabled ? 1231 : 1237); + result = prime * result + ((expression == null) ? 0 : expression.hashCode()); + result = prime * result + ((metadata == null) ? 0 : metadata.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((roles == null) ? 0 : roles.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final ExpressionRoleMapping other = (ExpressionRoleMapping) obj; + if (enabled != other.enabled) + return false; + if (expression == null) { + if (other.expression != null) + return false; + } else if (!expression.equals(other.expression)) + return false; + if (metadata == null) { + if (other.metadata != null) + return false; + } else if (!metadata.equals(other.metadata)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (roles == null) { + if (other.roles != null) + return false; + } else if (!roles.equals(other.roles)) + return false; + return true; + } + + public interface Fields { + ParseField ROLES = new ParseField("roles"); + ParseField ENABLED = new ParseField("enabled"); + ParseField RULES = new ParseField("rules"); + ParseField METADATA = new ParseField("metadata"); + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetRoleMappingsRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetRoleMappingsRequest.java new file mode 100644 index 00000000000..ca9b85c724d --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetRoleMappingsRequest.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; + +import org.elasticsearch.client.Validatable; +import org.elasticsearch.common.util.set.Sets; + +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +/** + * Request object to get role mappings + */ +public final class GetRoleMappingsRequest implements Validatable { + private final Set roleMappingNames; + + public GetRoleMappingsRequest(final String... roleMappingNames) { + if (roleMappingNames != null) { + this.roleMappingNames = Collections.unmodifiableSet(Sets.newHashSet(roleMappingNames)); + } else { + this.roleMappingNames = Collections.emptySet(); + } + } + + public Set getRoleMappingNames() { + return roleMappingNames; + } + + @Override + public int hashCode() { + return Objects.hash(roleMappingNames); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final GetRoleMappingsRequest other = (GetRoleMappingsRequest) obj; + + return Objects.equals(roleMappingNames, other.roleMappingNames); + } + +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetRoleMappingsResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetRoleMappingsResponse.java new file mode 100644 index 00000000000..05e63cefbe5 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetRoleMappingsResponse.java @@ -0,0 +1,70 @@ +/* + * 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; + +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParserUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Get role mappings response + */ +public final class GetRoleMappingsResponse { + + private final List mappings; + + public GetRoleMappingsResponse(List mappings) { + this.mappings = Collections.unmodifiableList(mappings); + } + + public List getMappings() { + return mappings; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final GetRoleMappingsResponse that = (GetRoleMappingsResponse) o; + return this.mappings.equals(that.mappings); + } + + @Override + public int hashCode() { + return mappings.hashCode(); + } + + public static GetRoleMappingsResponse fromXContent(XContentParser parser) throws IOException { + final List roleMappings = new ArrayList<>(); + + XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + XContentParser.Token token; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + XContentParserUtils.ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation); + roleMappings.add(ExpressionRoleMapping.PARSER.parse(parser, parser.currentName())); + } + + return new GetRoleMappingsResponse(roleMappings); + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/support/expressiondsl/parser/RoleMapperExpressionParser.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/support/expressiondsl/parser/RoleMapperExpressionParser.java index 98de4f4c209..0f5aec7f596 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/support/expressiondsl/parser/RoleMapperExpressionParser.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/support/expressiondsl/parser/RoleMapperExpressionParser.java @@ -45,6 +45,18 @@ import java.util.List; public final class RoleMapperExpressionParser { public static final ParseField FIELD = new ParseField("field"); + public static RoleMapperExpression fromXContent(final XContentParser parser) throws IOException { + return new RoleMapperExpressionParser().parse("rules", parser); + } + + /** + * This function exists to be compatible with + * {@link org.elasticsearch.common.xcontent.ContextParser#parse(XContentParser, Object)} + */ + public static RoleMapperExpression parseObject(XContentParser parser, String id) throws IOException { + return new RoleMapperExpressionParser().parse(id, parser); + } + /** * @param name The name of the expression tree within its containing object. * Used to provide descriptive error messages. diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java index 4267e238580..0cc283029e7 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.client; +import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; @@ -26,6 +27,7 @@ import org.elasticsearch.client.security.DeleteRoleMappingRequest; import org.elasticsearch.client.security.DeleteRoleRequest; import org.elasticsearch.client.security.DisableUserRequest; import org.elasticsearch.client.security.EnableUserRequest; +import org.elasticsearch.client.security.GetRoleMappingsRequest; import org.elasticsearch.client.security.ChangePasswordRequest; import org.elasticsearch.client.security.PutRoleMappingRequest; import org.elasticsearch.client.security.PutUserRequest; @@ -33,6 +35,7 @@ import org.elasticsearch.client.security.RefreshPolicy; import org.elasticsearch.client.security.support.expressiondsl.RoleMapperExpression; import org.elasticsearch.client.security.support.expressiondsl.expressions.AnyRoleMapperExpression; import org.elasticsearch.client.security.support.expressiondsl.fields.FieldRoleMapperExpression; +import org.elasticsearch.common.Strings; import org.elasticsearch.test.ESTestCase; import java.io.IOException; @@ -102,6 +105,25 @@ public class SecurityRequestConvertersTests extends ESTestCase { assertToXContentBody(putRoleMappingRequest, request.getEntity()); } + public void testGetRoleMappings() throws IOException { + int noOfRoleMappingNames = randomIntBetween(0, 2); + final String[] roleMappingNames = + randomArray(noOfRoleMappingNames, noOfRoleMappingNames, String[]::new, () -> randomAlphaOfLength(5)); + final GetRoleMappingsRequest getRoleMappingsRequest = new GetRoleMappingsRequest(roleMappingNames); + + final Request request = SecurityRequestConverters.getRoleMappings(getRoleMappingsRequest); + + assertEquals(HttpGet.METHOD_NAME, request.getMethod()); + if (noOfRoleMappingNames == 0) { + assertEquals("/_xpack/security/role_mapping", request.getEndpoint()); + } else { + assertEquals("/_xpack/security/role_mapping/" + + Strings.collectionToCommaDelimitedString(getRoleMappingsRequest.getRoleMappingNames()), request.getEndpoint()); + } + assertEquals(Collections.emptyMap(), request.getParameters()); + assertNull(request.getEntity()); + } + public void testEnableUser() { final String username = randomAlphaOfLengthBetween(1, 12); final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java index 96b4b311490..e003251f89b 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java @@ -38,6 +38,9 @@ import org.elasticsearch.client.security.DeleteRoleResponse; import org.elasticsearch.client.security.DisableUserRequest; import org.elasticsearch.client.security.EmptyResponse; import org.elasticsearch.client.security.EnableUserRequest; +import org.elasticsearch.client.security.ExpressionRoleMapping; +import org.elasticsearch.client.security.GetRoleMappingsRequest; +import org.elasticsearch.client.security.GetRoleMappingsResponse; import org.elasticsearch.client.security.GetSslCertificatesResponse; import org.elasticsearch.client.security.PutRoleMappingRequest; import org.elasticsearch.client.security.PutRoleMappingResponse; @@ -54,14 +57,20 @@ import org.hamcrest.Matchers; import java.io.IOException; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.not; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isIn; public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase { @@ -165,6 +174,119 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase { } } + public void testGetRoleMappings() throws Exception { + final RestHighLevelClient client = highLevelClient(); + + final RoleMapperExpression rules1 = AnyRoleMapperExpression.builder().addExpression(FieldRoleMapperExpression.ofUsername("*")) + .addExpression(FieldRoleMapperExpression.ofGroups("cn=admins,dc=example,dc=com")).build(); + final PutRoleMappingRequest putRoleMappingRequest1 = new PutRoleMappingRequest("mapping-example-1", true, Collections.singletonList( + "superuser"), rules1, null, RefreshPolicy.NONE); + final PutRoleMappingResponse putRoleMappingResponse1 = client.security().putRoleMapping(putRoleMappingRequest1, + RequestOptions.DEFAULT); + boolean isCreated1 = putRoleMappingResponse1.isCreated(); + assertTrue(isCreated1); + final RoleMapperExpression rules2 = AnyRoleMapperExpression.builder().addExpression(FieldRoleMapperExpression.ofGroups( + "cn=admins,dc=example,dc=com")).build(); + final Map metadata2 = new HashMap<>(); + metadata2.put("k1", "v1"); + final PutRoleMappingRequest putRoleMappingRequest2 = new PutRoleMappingRequest("mapping-example-2", true, Collections.singletonList( + "monitoring"), rules2, metadata2, RefreshPolicy.NONE); + final PutRoleMappingResponse putRoleMappingResponse2 = client.security().putRoleMapping(putRoleMappingRequest2, + RequestOptions.DEFAULT); + boolean isCreated2 = putRoleMappingResponse2.isCreated(); + assertTrue(isCreated2); + + { + // tag::get-role-mappings-execute + final GetRoleMappingsRequest request = new GetRoleMappingsRequest("mapping-example-1"); + final GetRoleMappingsResponse response = client.security().getRoleMappings(request, RequestOptions.DEFAULT); + // end::get-role-mappings-execute + // tag::get-role-mappings-response + List mappings = response.getMappings(); + // end::get-role-mappings-response + assertNotNull(mappings); + assertThat(mappings.size(), is(1)); + assertThat(mappings.get(0).isEnabled(), is(true)); + assertThat(mappings.get(0).getName(), is("mapping-example-1")); + assertThat(mappings.get(0).getExpression(), equalTo(rules1)); + assertThat(mappings.get(0).getMetadata(), equalTo(Collections.emptyMap())); + assertThat(mappings.get(0).getRoles(), contains("superuser")); + } + + { + // tag::get-role-mappings-list-execute + final GetRoleMappingsRequest request = new GetRoleMappingsRequest("mapping-example-1", "mapping-example-2"); + final GetRoleMappingsResponse response = client.security().getRoleMappings(request, RequestOptions.DEFAULT); + // end::get-role-mappings-all-execute + List mappings = response.getMappings(); + assertNotNull(mappings); + assertThat(mappings.size(), is(2)); + for (ExpressionRoleMapping roleMapping : mappings) { + assertThat(roleMapping.isEnabled(), is(true)); + assertThat(roleMapping.getName(), isIn(new String[] { "mapping-example-1", "mapping-example-2" })); + if (roleMapping.getName().equals("mapping-example-1")) { + assertThat(roleMapping.getMetadata(), equalTo(Collections.emptyMap())); + assertThat(roleMapping.getExpression(), equalTo(rules1)); + assertThat(roleMapping.getRoles(), contains("superuser")); + } else { + assertThat(roleMapping.getMetadata(), equalTo(metadata2)); + assertThat(roleMapping.getExpression(), equalTo(rules2)); + assertThat(roleMapping.getRoles(), contains("monitoring")); + } + } + } + + { + // tag::get-role-mappings-all-execute + final GetRoleMappingsRequest request = new GetRoleMappingsRequest(); + final GetRoleMappingsResponse response = client.security().getRoleMappings(request, RequestOptions.DEFAULT); + // end::get-role-mappings-all-execute + List mappings = response.getMappings(); + assertNotNull(mappings); + assertThat(mappings.size(), is(2)); + for (ExpressionRoleMapping roleMapping : mappings) { + assertThat(roleMapping.isEnabled(), is(true)); + assertThat(roleMapping.getName(), isIn(new String[] { "mapping-example-1", "mapping-example-2" })); + if (roleMapping.getName().equals("mapping-example-1")) { + assertThat(roleMapping.getMetadata(), equalTo(Collections.emptyMap())); + assertThat(roleMapping.getExpression(), equalTo(rules1)); + assertThat(roleMapping.getRoles(), contains("superuser")); + } else { + assertThat(roleMapping.getMetadata(), equalTo(metadata2)); + assertThat(roleMapping.getExpression(), equalTo(rules2)); + assertThat(roleMapping.getRoles(), contains("monitoring")); + } + } + } + + { + final GetRoleMappingsRequest request = new GetRoleMappingsRequest(); + // tag::get-role-mappings-execute-listener + ActionListener listener = new ActionListener() { + @Override + public void onResponse(GetRoleMappingsResponse response) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::get-role-mappings-execute-listener + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::get-role-mappings-execute-async + client.security().getRoleMappingsAsync(request, RequestOptions.DEFAULT, listener); // <1> + // end::get-role-mappings-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } + public void testEnableUser() throws Exception { RestHighLevelClient client = highLevelClient(); char[] password = new char[]{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}; diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/ExpressionRoleMappingTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/ExpressionRoleMappingTests.java new file mode 100644 index 00000000000..29bc7812f5b --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/ExpressionRoleMappingTests.java @@ -0,0 +1,108 @@ +/* + * 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; + +import org.elasticsearch.client.security.support.expressiondsl.fields.FieldRoleMapperExpression; +import org.elasticsearch.common.xcontent.DeprecationHandler; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.EqualsHashCodeTestUtils; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.hamcrest.Matchers.equalTo; + +public class ExpressionRoleMappingTests extends ESTestCase { + + public void testExpressionRoleMappingParser() throws IOException { + final String json = + "{\n" + + " \"enabled\" : true,\n" + + " \"roles\" : [\n" + + " \"superuser\"\n" + + " ],\n" + + " \"rules\" : {\n" + + " \"field\" : {\n" + + " \"realm.name\" : \"kerb1\"\n" + + " }\n" + + " },\n" + + " \"metadata\" : { }\n" + + " }"; + final ExpressionRoleMapping expressionRoleMapping = ExpressionRoleMapping.PARSER.parse(XContentType.JSON.xContent().createParser( + new NamedXContentRegistry(Collections.emptyList()), new DeprecationHandler() { + @Override + public void usedDeprecatedName(String usedName, String modernName) { + } + + @Override + public void usedDeprecatedField(String usedName, String replacedWith) { + } + }, json), "example-role-mapping"); + final ExpressionRoleMapping expectedRoleMapping = new ExpressionRoleMapping("example-role-mapping", FieldRoleMapperExpression + .ofKeyValues("realm.name", "kerb1"), Collections.singletonList("superuser"), null, true); + assertThat(expressionRoleMapping, equalTo(expectedRoleMapping)); + } + + public void testEqualsHashCode() { + final ExpressionRoleMapping expressionRoleMapping = new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression + .ofKeyValues("realm.name", "kerb1"), Collections.singletonList("superuser"), null, true); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(expressionRoleMapping, (original) -> { + return new ExpressionRoleMapping(original.getName(), original.getExpression(), original.getRoles(), original.getMetadata(), + original.isEnabled()); + }); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(expressionRoleMapping, (original) -> { + return new ExpressionRoleMapping(original.getName(), original.getExpression(), original.getRoles(), original.getMetadata(), + original.isEnabled()); + }, ExpressionRoleMappingTests::mutateTestItem); + } + + private static ExpressionRoleMapping mutateTestItem(ExpressionRoleMapping original) { + ExpressionRoleMapping mutated = null; + switch (randomIntBetween(0, 4)) { + case 0: + mutated = new ExpressionRoleMapping("namechanged", FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"), Collections + .singletonList("superuser"), null, true); + break; + case 1: + mutated = new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("changed", "changed"), Collections + .singletonList("superuser"), null, true); + break; + case 2: + mutated = new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"), Collections + .singletonList("changed"), null, true); + break; + case 3: + Map metadata = new HashMap<>(); + metadata.put("a", "b"); + mutated = new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"), Collections + .singletonList("superuser"), metadata, true); + break; + case 4: + mutated = new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"), Collections + .singletonList("superuser"), null, false); + break; + } + return mutated; + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetRoleMappingsRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetRoleMappingsRequestTests.java new file mode 100644 index 00000000000..8acd8ba17de --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetRoleMappingsRequestTests.java @@ -0,0 +1,55 @@ +/* + * 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; + +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.EqualsHashCodeTestUtils; + +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; + +public class GetRoleMappingsRequestTests extends ESTestCase { + + public void testGetRoleMappingsRequest() { + int noOfRoleMappingNames = randomIntBetween(0, 2); + final String[] roleMappingNames = randomArray(noOfRoleMappingNames, noOfRoleMappingNames, String[]::new, () -> randomAlphaOfLength( + 5)); + final GetRoleMappingsRequest getRoleMappingsRequest = new GetRoleMappingsRequest(roleMappingNames); + assertThat(getRoleMappingsRequest.getRoleMappingNames().size(), is(noOfRoleMappingNames)); + assertThat(getRoleMappingsRequest.getRoleMappingNames(), containsInAnyOrder(roleMappingNames)); + } + + public void testEqualsHashCode() { + int noOfRoleMappingNames = randomIntBetween(0, 2); + final String[] roleMappingNames = randomArray(noOfRoleMappingNames, String[]::new, () -> randomAlphaOfLength(5)); + final GetRoleMappingsRequest getRoleMappingsRequest = new GetRoleMappingsRequest(roleMappingNames); + assertNotNull(getRoleMappingsRequest); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(getRoleMappingsRequest, (original) -> { + return new GetRoleMappingsRequest(original.getRoleMappingNames().toArray(new String[0])); + }); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(getRoleMappingsRequest, (original) -> { + return new GetRoleMappingsRequest(original.getRoleMappingNames().toArray(new String[0])); + }, GetRoleMappingsRequestTests::mutateTestItem); + } + + private static GetRoleMappingsRequest mutateTestItem(GetRoleMappingsRequest original) { + return new GetRoleMappingsRequest(randomAlphaOfLength(8)); + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetRoleMappingsResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetRoleMappingsResponseTests.java new file mode 100644 index 00000000000..b612c9ead28 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetRoleMappingsResponseTests.java @@ -0,0 +1,118 @@ +/* + * 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; + +import org.elasticsearch.client.security.support.expressiondsl.fields.FieldRoleMapperExpression; +import org.elasticsearch.common.xcontent.DeprecationHandler; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.EqualsHashCodeTestUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.Matchers.equalTo; + +public class GetRoleMappingsResponseTests extends ESTestCase { + + public void testFromXContent() throws IOException { + final String json = "{\n" + + " \"kerberosmapping\" : {\n" + + " \"enabled\" : true,\n" + + " \"roles\" : [\n" + + " \"superuser\"\n" + + " ],\n" + + " \"rules\" : {\n" + + " \"field\" : {\n" + + " \"realm.name\" : \"kerb1\"\n" + + " }\n" + + " },\n" + + " \"metadata\" : { }\n" + + " },\n" + + " \"ldapmapping\" : {\n" + + " \"enabled\" : false,\n" + + " \"roles\" : [\n" + + " \"monitoring\"\n" + + " ],\n" + + " \"rules\" : {\n" + + " \"field\" : {\n" + + " \"groups\" : \"cn=ipausers,cn=groups,cn=accounts,dc=ipademo,dc=local\"\n" + + " }\n" + + " },\n" + + " \"metadata\" : { }\n" + + " }\n" + + "}"; + final GetRoleMappingsResponse response = GetRoleMappingsResponse.fromXContent(XContentType.JSON.xContent().createParser( + new NamedXContentRegistry(Collections.emptyList()), new DeprecationHandler() { + @Override + public void usedDeprecatedName(String usedName, String modernName) { + } + + @Override + public void usedDeprecatedField(String usedName, String replacedWith) { + } + }, json)); + final List expectedRoleMappingsList = new ArrayList<>(); + expectedRoleMappingsList.add(new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("realm.name", + "kerb1"), Collections.singletonList("superuser"), null, true)); + expectedRoleMappingsList.add(new ExpressionRoleMapping("ldapmapping", FieldRoleMapperExpression.ofGroups( + "cn=ipausers,cn=groups,cn=accounts,dc=ipademo,dc=local"), Collections.singletonList("monitoring"), null, false)); + final GetRoleMappingsResponse expectedResponse = new GetRoleMappingsResponse(expectedRoleMappingsList); + assertThat(response, equalTo(expectedResponse)); + } + + public void testEqualsHashCode() { + final List roleMappingsList = new ArrayList<>(); + roleMappingsList.add(new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("realm.name", + "kerb1"), Collections.singletonList("superuser"), null, true)); + final GetRoleMappingsResponse response = new GetRoleMappingsResponse(roleMappingsList); + assertNotNull(response); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(response, (original) -> { + return new GetRoleMappingsResponse(original.getMappings()); + }); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(response, (original) -> { + return new GetRoleMappingsResponse(original.getMappings()); + }, GetRoleMappingsResponseTests::mutateTestItem); + } + + private static GetRoleMappingsResponse mutateTestItem(GetRoleMappingsResponse original) { + GetRoleMappingsResponse mutated = null; + switch(randomIntBetween(0, 1)) { + case 0: + final List roleMappingsList1 = new ArrayList<>(); + roleMappingsList1.add(new ExpressionRoleMapping("ldapmapping", FieldRoleMapperExpression.ofGroups( + "cn=ipausers,cn=groups,cn=accounts,dc=ipademo,dc=local"), Collections.singletonList("monitoring"), null, false)); + mutated = new GetRoleMappingsResponse(roleMappingsList1); + break; + case 1: + final List roleMappingsList2 = new ArrayList<>(); + ExpressionRoleMapping orginialRoleMapping = original.getMappings().get(0); + roleMappingsList2.add(new ExpressionRoleMapping(orginialRoleMapping.getName(), FieldRoleMapperExpression.ofGroups( + "cn=ipausers,cn=groups,cn=accounts,dc=ipademo,dc=local"), + orginialRoleMapping.getRoles(), orginialRoleMapping.getMetadata(), !orginialRoleMapping.isEnabled())); + mutated = new GetRoleMappingsResponse(roleMappingsList2); + break; + } + return mutated; + } +} diff --git a/docs/java-rest/high-level/security/get-role-mappings.asciidoc b/docs/java-rest/high-level/security/get-role-mappings.asciidoc new file mode 100644 index 00000000000..cc58d0980c3 --- /dev/null +++ b/docs/java-rest/high-level/security/get-role-mappings.asciidoc @@ -0,0 +1,67 @@ +[[java-rest-high-security-get-role-mappings]] +=== Get Role Mappings API + +[[java-rest-high-security-get-role-mappings-execution]] +==== Execution + +Retrieving a role mapping can be performed using the `security().getRoleMappings()` +method and by setting role mapping name on `GetRoleMappingsRequest`: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SecurityDocumentationIT.java[get-role-mappings-execute] +-------------------------------------------------- + +Retrieving multiple role mappings can be performed using the `security.getRoleMappings()` +method and by setting role mapping names on `GetRoleMappingsRequest`: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SecurityDocumentationIT.java[get-role-mappings-list-execute] +-------------------------------------------------- + +Retrieving all role mappings can be performed using the `security.getRoleMappings()` +method and with no role mapping name on `GetRoleMappingsRequest`: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SecurityDocumentationIT.java[get-role-mappings-all-execute] +-------------------------------------------------- + +[[java-rest-high-security-get-role-mappings-response]] +==== Response + +The returned `GetRoleMappingsResponse` contains the list of role mapping(s). + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SecurityDocumentationIT.java[get-role-mappings-response] +-------------------------------------------------- + +[[java-rest-high-security-get-role-mappings-async]] +==== Asynchronous Execution + +This request can be executed asynchronously using the `security().getRoleMappingsAsync()` +method: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SecurityDocumentationIT.java[get-role-mappings-execute-async] +-------------------------------------------------- +<1> The `GetRoleMappingsRequest` to execute and the `ActionListener` to use when +the execution completes + +The asynchronous method does not block and returns immediately. Once the request +has completed the `ActionListener` is called back using the `onResponse` method +if the execution successfully completed or using the `onFailure` method if +it failed. + +A typical listener for a `GetRoleMappingsResponse` looks like: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SecurityDocumentationIT.java[get-role-mappings-execute-listener] +-------------------------------------------------- +<1> Called when the execution is successfully completed. The response is +provided as an argument +<2> Called in case of failure. The raised exception is provided as an argument \ No newline at end of file diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index 6e233bec181..5eb68701059 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -327,6 +327,7 @@ The Java High Level REST Client supports the following Security APIs: * <<{upid}-clear-roles-cache>> * <> * <> +* <> * <> include::security/put-user.asciidoc[] @@ -337,6 +338,7 @@ include::security/delete-role.asciidoc[] include::security/clear-roles-cache.asciidoc[] include::security/get-certificates.asciidoc[] include::security/put-role-mapping.asciidoc[] +include::security/get-role-mapping.asciidoc[] include::security/delete-role-mapping.asciidoc[] == Watcher APIs