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 8afaa3551ad88..14023e5e39d07 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 @@ -20,6 +20,8 @@ package org.elasticsearch.client; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.client.security.PutRoleMappingRequest; +import org.elasticsearch.client.security.PutRoleMappingResponse; import org.elasticsearch.client.security.DisableUserRequest; import org.elasticsearch.client.security.EnableUserRequest; import org.elasticsearch.client.security.GetSslCertificatesRequest; @@ -75,6 +77,34 @@ public void putUserAsync(PutUserRequest request, RequestOptions options, ActionL PutUserResponse::fromXContent, listener, emptySet()); } + /** + * Create/Update a role mapping. + * See + * the docs for more. + * @param request the request with the role mapping information + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response from the put role mapping call + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public PutRoleMappingResponse putRoleMapping(final PutRoleMappingRequest request, final RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::putRoleMapping, options, + PutRoleMappingResponse::fromXContent, emptySet()); + } + + /** + * Asynchronously create/update a role mapping. + * See + * the docs for more. + * @param request the request with the role mapping information + * @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 putRoleMappingAsync(final PutRoleMappingRequest request, final RequestOptions options, + final ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::putRoleMapping, options, + PutRoleMappingResponse::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 3157abe63375e..0a1d9cb3530c4 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 @@ -21,6 +21,7 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; +import org.elasticsearch.client.security.PutRoleMappingRequest; import org.elasticsearch.client.security.DisableUserRequest; import org.elasticsearch.client.security.EnableUserRequest; import org.elasticsearch.client.security.ChangePasswordRequest; @@ -61,6 +62,18 @@ static Request putUser(PutUserRequest putUserRequest) throws IOException { return request; } + static Request putRoleMapping(final PutRoleMappingRequest putRoleMappingRequest) throws IOException { + final String endpoint = new RequestConverters.EndpointBuilder() + .addPathPartAsIs("_xpack/security/role_mapping") + .addPathPart(putRoleMappingRequest.getName()) + .build(); + final Request request = new Request(HttpPut.METHOD_NAME, endpoint); + request.setEntity(createEntity(putRoleMappingRequest, REQUEST_BODY_CONTENT_TYPE)); + final RequestConverters.Params params = new RequestConverters.Params(request); + params.withRefreshPolicy(putRoleMappingRequest.getRefreshPolicy()); + return request; + } + static Request enableUser(EnableUserRequest enableUserRequest) { return setUserEnabled(enableUserRequest); } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/PutRoleMappingRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/PutRoleMappingRequest.java new file mode 100644 index 0000000000000..b8da17da72dad --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/PutRoleMappingRequest.java @@ -0,0 +1,124 @@ +/* + * 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.client.security.support.expressiondsl.RoleMapperExpression; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Request object to create or update a role mapping. + */ +public final class PutRoleMappingRequest implements Validatable, ToXContentObject { + + private final String name; + private final boolean enabled; + private final List roles; + private final RoleMapperExpression rules; + + private final Map metadata; + private final RefreshPolicy refreshPolicy; + + public PutRoleMappingRequest(final String name, final boolean enabled, final List roles, final RoleMapperExpression rules, + @Nullable final Map metadata, @Nullable final RefreshPolicy refreshPolicy) { + if (Strings.hasText(name) == false) { + throw new IllegalArgumentException("role-mapping name is missing"); + } + this.name = name; + this.enabled = enabled; + if (roles == null || roles.isEmpty()) { + throw new IllegalArgumentException("role-mapping roles are missing"); + } + this.roles = Collections.unmodifiableList(roles); + this.rules = Objects.requireNonNull(rules, "role-mapping rules are missing"); + this.metadata = (metadata == null) ? Collections.emptyMap() : metadata; + this.refreshPolicy = (refreshPolicy == null) ? RefreshPolicy.getDefault() : refreshPolicy; + } + + public String getName() { + return name; + } + + public boolean isEnabled() { + return enabled; + } + + public List getRoles() { + return roles; + } + + public RoleMapperExpression getRules() { + return rules; + } + + public Map getMetadata() { + return metadata; + } + + public RefreshPolicy getRefreshPolicy() { + return refreshPolicy; + } + + @Override + public int hashCode() { + return Objects.hash(name, enabled, refreshPolicy, roles, rules, metadata); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final PutRoleMappingRequest other = (PutRoleMappingRequest) obj; + + return (enabled == other.enabled) && + (refreshPolicy == other.refreshPolicy) && + Objects.equals(name, other.name) && + Objects.equals(roles, other.roles) && + Objects.equals(rules, other.rules) && + Objects.equals(metadata, other.metadata); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field("enabled", enabled); + builder.field("roles", roles); + builder.field("rules", rules); + builder.field("metadata", metadata); + return builder.endObject(); + } + +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/PutRoleMappingResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/PutRoleMappingResponse.java new file mode 100644 index 0000000000000..04cdb14163e3e --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/PutRoleMappingResponse.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; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Objects; + +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; + +/** + * Response when adding/updating a role mapping. Returns a boolean field for + * whether the role mapping was created or updated. + */ +public final class PutRoleMappingResponse { + + private final boolean created; + + public PutRoleMappingResponse(boolean created) { + this.created = created; + } + + public boolean isCreated() { + return created; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final PutRoleMappingResponse that = (PutRoleMappingResponse) o; + return created == that.created; + } + + @Override + public int hashCode() { + return Objects.hash(created); + } + + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "put_role_mapping_response", true, args -> new PutRoleMappingResponse((boolean) args[0])); + static { + PARSER.declareBoolean(constructorArg(), new ParseField("created")); + // To parse the "created" field we declare "role_mapping" field object. + // Once the nested field "created" is found parser constructs the target object and + // ignores the role_mapping object. + PARSER.declareObject((a,b) -> {}, (parser, context) -> null, new ParseField("role_mapping")); + } + + public static PutRoleMappingResponse fromXContent(XContentParser parser) throws IOException { + return PARSER.parse(parser, null); + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/support/expressiondsl/expressions/CompositeRoleMapperExpression.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/support/expressiondsl/expressions/CompositeRoleMapperExpression.java index 2519c59b68846..3373f325bfcdb 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/support/expressiondsl/expressions/CompositeRoleMapperExpression.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/support/expressiondsl/expressions/CompositeRoleMapperExpression.java @@ -57,7 +57,7 @@ public abstract class CompositeRoleMapperExpression implements RoleMapperExpress } public String getName() { - return this.getName(); + return this.name; } public List getElements() { diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/support/expressiondsl/fields/FieldRoleMapperExpression.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/support/expressiondsl/fields/FieldRoleMapperExpression.java index c96ac3cc5b5ec..4111b9fa2a2c3 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/support/expressiondsl/fields/FieldRoleMapperExpression.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/support/expressiondsl/fields/FieldRoleMapperExpression.java @@ -50,7 +50,7 @@ public FieldRoleMapperExpression(final String field, final Object... values) { throw new IllegalArgumentException("null or empty field name (" + field + ")"); } if (values == null || values.length == 0) { - throw new IllegalArgumentException("null or empty values (" + values + ")"); + throw new IllegalArgumentException("null or empty values for field (" + field + ")"); } this.field = field; this.values = Collections.unmodifiableList(Arrays.asList(values)); 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 0741c6f72d90a..3c0e6dc4374c7 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 @@ -24,8 +24,12 @@ import org.elasticsearch.client.security.DisableUserRequest; import org.elasticsearch.client.security.EnableUserRequest; import org.elasticsearch.client.security.ChangePasswordRequest; +import org.elasticsearch.client.security.PutRoleMappingRequest; import org.elasticsearch.client.security.PutUserRequest; 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.test.ESTestCase; import java.io.IOException; @@ -67,6 +71,34 @@ public void testPutUser() throws IOException { assertToXContentBody(putUserRequest, request.getEntity()); } + public void testPutRoleMapping() throws IOException { + final String username = randomAlphaOfLengthBetween(4, 7); + final String rolename = randomAlphaOfLengthBetween(4, 7); + final String roleMappingName = randomAlphaOfLengthBetween(4, 7); + final String groupname = "cn="+randomAlphaOfLengthBetween(4, 7)+",dc=example,dc=com"; + final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); + final Map expectedParams; + if (refreshPolicy != RefreshPolicy.NONE) { + expectedParams = Collections.singletonMap("refresh", refreshPolicy.getValue()); + } else { + expectedParams = Collections.emptyMap(); + } + + final RoleMapperExpression rules = AnyRoleMapperExpression.builder() + .addExpression(FieldRoleMapperExpression.ofUsername(username)) + .addExpression(FieldRoleMapperExpression.ofGroups(groupname)) + .build(); + final PutRoleMappingRequest putRoleMappingRequest = new PutRoleMappingRequest(roleMappingName, true, Collections.singletonList( + rolename), rules, null, refreshPolicy); + + final Request request = SecurityRequestConverters.putRoleMapping(putRoleMappingRequest); + + assertEquals(HttpPut.METHOD_NAME, request.getMethod()); + assertEquals("/_xpack/security/role_mapping/" + roleMappingName, request.getEndpoint()); + assertEquals(expectedParams, request.getParameters()); + assertToXContentBody(putRoleMappingRequest, 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 ece1c1eacef4a..d3d12c4a90ef4 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 @@ -26,13 +26,18 @@ import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.security.ChangePasswordRequest; import org.elasticsearch.client.security.DisableUserRequest; +import org.elasticsearch.client.security.EmptyResponse; import org.elasticsearch.client.security.EnableUserRequest; import org.elasticsearch.client.security.GetSslCertificatesResponse; +import org.elasticsearch.client.security.PutRoleMappingRequest; +import org.elasticsearch.client.security.PutRoleMappingResponse; import org.elasticsearch.client.security.PutUserRequest; import org.elasticsearch.client.security.PutUserResponse; import org.elasticsearch.client.security.RefreshPolicy; -import org.elasticsearch.client.security.EmptyResponse; import org.elasticsearch.client.security.support.CertificateInfo; +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.hamcrest.Matchers; import java.util.Collections; @@ -91,6 +96,58 @@ public void onFailure(Exception e) { } } + public void testPutRoleMapping() throws Exception { + final RestHighLevelClient client = highLevelClient(); + + { + // tag::put-role-mapping-execute + final RoleMapperExpression rules = AnyRoleMapperExpression.builder() + .addExpression(FieldRoleMapperExpression.ofUsername("*")) + .addExpression(FieldRoleMapperExpression.ofGroups("cn=admins,dc=example,dc=com")) + .build(); + final PutRoleMappingRequest request = new PutRoleMappingRequest("mapping-example", true, Collections.singletonList("superuser"), + rules, null, RefreshPolicy.NONE); + final PutRoleMappingResponse response = client.security().putRoleMapping(request, RequestOptions.DEFAULT); + // end::put-role-mapping-execute + // tag::put-role-mapping-response + boolean isCreated = response.isCreated(); // <1> + // end::put-role-mapping-response + assertTrue(isCreated); + } + + { + final RoleMapperExpression rules = AnyRoleMapperExpression.builder() + .addExpression(FieldRoleMapperExpression.ofUsername("*")) + .addExpression(FieldRoleMapperExpression.ofGroups("cn=admins,dc=example,dc=com")) + .build(); + final PutRoleMappingRequest request = new PutRoleMappingRequest("mapping-example", true, Collections.singletonList("superuser"), + rules, null, RefreshPolicy.NONE); + // tag::put-role-mapping-execute-async + ActionListener listener = new ActionListener() { + @Override + public void onResponse(PutRoleMappingResponse response) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::put-role-mapping-execute-async + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::put-role-mapping-execute-listener + client.security().putRoleMappingAsync(request, RequestOptions.DEFAULT, listener); // <1> + // end::put-role-mapping-execute-listener + + 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/PutRoleMappingRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/PutRoleMappingRequestTests.java new file mode 100644 index 0000000000000..f0a3f7572ef3b --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/PutRoleMappingRequestTests.java @@ -0,0 +1,177 @@ +/* + * 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.fields.FieldRoleMapperExpression; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.EqualsHashCodeTestUtils; + +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 java.util.stream.Collectors; + +import static org.hamcrest.Matchers.equalTo; + +public class PutRoleMappingRequestTests extends ESTestCase { + + public void testPutRoleMappingRequest() { + final String name = randomAlphaOfLength(5); + final boolean enabled = randomBoolean(); + final List roles = Collections.singletonList("superuser"); + final RoleMapperExpression rules = FieldRoleMapperExpression.ofUsername("user"); + final Map metadata = new HashMap<>(); + metadata.put("k1", "v1"); + final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); + + PutRoleMappingRequest putRoleMappingRequest = new PutRoleMappingRequest(name, enabled, roles, rules, metadata, refreshPolicy); + assertNotNull(putRoleMappingRequest); + assertThat(putRoleMappingRequest.getName(), equalTo(name)); + assertThat(putRoleMappingRequest.isEnabled(), equalTo(enabled)); + assertThat(putRoleMappingRequest.getRefreshPolicy(), equalTo((refreshPolicy == null) ? RefreshPolicy.getDefault() : refreshPolicy)); + assertThat(putRoleMappingRequest.getRules(), equalTo(rules)); + assertThat(putRoleMappingRequest.getRoles(), equalTo(roles)); + assertThat(putRoleMappingRequest.getMetadata(), equalTo((metadata == null) ? Collections.emptyMap() : metadata)); + } + + public void testPutRoleMappingRequestThrowsExceptionForNullOrEmptyName() { + final String name = randomBoolean() ? null : ""; + final boolean enabled = randomBoolean(); + final List roles = Collections.singletonList("superuser"); + final RoleMapperExpression rules = FieldRoleMapperExpression.ofUsername("user"); + final Map metadata = new HashMap<>(); + metadata.put("k1", "v1"); + final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); + + final IllegalArgumentException ile = expectThrows(IllegalArgumentException.class, () -> new PutRoleMappingRequest(name, enabled, + roles, rules, metadata, refreshPolicy)); + assertThat(ile.getMessage(), equalTo("role-mapping name is missing")); + } + + public void testPutRoleMappingRequestThrowsExceptionForNullOrEmptyRoles() { + final String name = randomAlphaOfLength(5); + final boolean enabled = randomBoolean(); + final List roles = randomBoolean() ? null : Collections.emptyList(); + final RoleMapperExpression rules = FieldRoleMapperExpression.ofUsername("user"); + final Map metadata = new HashMap<>(); + metadata.put("k1", "v1"); + final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); + + final IllegalArgumentException ile = expectThrows(IllegalArgumentException.class, () -> new PutRoleMappingRequest(name, enabled, + roles, rules, metadata, refreshPolicy)); + assertThat(ile.getMessage(), equalTo("role-mapping roles are missing")); + } + + public void testPutRoleMappingRequestThrowsExceptionForNullRules() { + final String name = randomAlphaOfLength(5); + final boolean enabled = randomBoolean(); + final List roles = Collections.singletonList("superuser"); + final RoleMapperExpression rules = null; + final Map metadata = new HashMap<>(); + metadata.put("k1", "v1"); + final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); + + expectThrows(NullPointerException.class, () -> new PutRoleMappingRequest(name, enabled, roles, rules, metadata, refreshPolicy)); + } + + public void testPutRoleMappingRequestToXContent() throws IOException { + final String name = randomAlphaOfLength(5); + final boolean enabled = randomBoolean(); + final List roles = Collections.singletonList("superuser"); + final RoleMapperExpression rules = FieldRoleMapperExpression.ofUsername("user"); + final Map metadata = new HashMap<>(); + metadata.put("k1", "v1"); + final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); + + final PutRoleMappingRequest putRoleMappingRequest = new PutRoleMappingRequest(name, enabled, roles, rules, metadata, refreshPolicy); + + final XContentBuilder builder = XContentFactory.jsonBuilder(); + putRoleMappingRequest.toXContent(builder, ToXContent.EMPTY_PARAMS); + final String output = Strings.toString(builder); + final String expected = + "{"+ + "\"enabled\":" + enabled + "," + + "\"roles\":[\"superuser\"]," + + "\"rules\":{" + + "\"field\":{\"username\":[\"user\"]}" + + "}," + + "\"metadata\":{\"k1\":\"v1\"}" + + "}"; + + assertThat(output, equalTo(expected)); + } + + public void testEqualsHashCode() { + final String name = randomAlphaOfLength(5); + final boolean enabled = randomBoolean(); + final List roles = Collections.singletonList("superuser"); + final RoleMapperExpression rules = FieldRoleMapperExpression.ofUsername("user"); + final Map metadata = new HashMap<>(); + metadata.put("k1", "v1"); + final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); + + PutRoleMappingRequest putRoleMappingRequest = new PutRoleMappingRequest(name, enabled, roles, rules, metadata, refreshPolicy); + assertNotNull(putRoleMappingRequest); + + EqualsHashCodeTestUtils.checkEqualsAndHashCode(putRoleMappingRequest, (original) -> { + return new PutRoleMappingRequest(original.getName(), original.isEnabled(), original.getRoles(), original.getRules(), original + .getMetadata(), original.getRefreshPolicy()); + }); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(putRoleMappingRequest, (original) -> { + return new PutRoleMappingRequest(original.getName(), original.isEnabled(), original.getRoles(), original.getRules(), original + .getMetadata(), original.getRefreshPolicy()); + }, PutRoleMappingRequestTests::mutateTestItem); + } + + private static PutRoleMappingRequest mutateTestItem(PutRoleMappingRequest original) { + switch (randomIntBetween(0, 4)) { + case 0: + return new PutRoleMappingRequest(randomAlphaOfLength(5), original.isEnabled(), original.getRoles(), original.getRules(), + original.getMetadata(), original.getRefreshPolicy()); + case 1: + return new PutRoleMappingRequest(original.getName(), !original.isEnabled(), original.getRoles(), original.getRules(), + original.getMetadata(), original.getRefreshPolicy()); + case 2: + return new PutRoleMappingRequest(original.getName(), original.isEnabled(), original.getRoles(), + FieldRoleMapperExpression.ofGroups("group"), original.getMetadata(), original.getRefreshPolicy()); + case 3: + return new PutRoleMappingRequest(original.getName(), original.isEnabled(), original.getRoles(), original.getRules(), + Collections.emptyMap(), original.getRefreshPolicy()); + case 4: + List values = Arrays.stream(RefreshPolicy.values()) + .filter(rp -> rp != original.getRefreshPolicy()) + .collect(Collectors.toList()); + return new PutRoleMappingRequest(original.getName(), original.isEnabled(), original.getRoles(), original.getRules(), original + .getMetadata(), randomFrom(values)); + default: + return new PutRoleMappingRequest(randomAlphaOfLength(5), original.isEnabled(), original.getRoles(), original.getRules(), + original.getMetadata(), original.getRefreshPolicy()); + } + } + +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/support/expressiondsl/RoleMapperExpressionDslTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/support/expressiondsl/RoleMapperExpressionDslTests.java index df94640f172dd..6b067e08e0be2 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/support/expressiondsl/RoleMapperExpressionDslTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/support/expressiondsl/RoleMapperExpressionDslTests.java @@ -86,7 +86,7 @@ public void testRoleMapperExpressionToXContentType() throws IOException { "]"+ "}"; - assertThat(expected, equalTo(output)); + assertThat(output, equalTo(expected)); } public void testFieldRoleMapperExpressionThrowsExceptionForMissingMetadataPrefix() { diff --git a/docs/java-rest/high-level/security/put-role-mapping.asciidoc b/docs/java-rest/high-level/security/put-role-mapping.asciidoc new file mode 100644 index 0000000000000..7f1bcb2839bde --- /dev/null +++ b/docs/java-rest/high-level/security/put-role-mapping.asciidoc @@ -0,0 +1,53 @@ +[[java-rest-high-security-put-role-mapping]] +=== Put Role Mapping API + +[[java-rest-high-security-put-role-mapping-execution]] +==== Execution + +Creating and updating a role mapping can be performed using the `security().putRoleMapping()` +method: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SecurityDocumentationIT.java[put-role-mapping-execute] +-------------------------------------------------- + +[[java-rest-high-security-put-role-mapping-response]] +==== Response + +The returned `PutRoleMappingResponse` contains a single field, `created`. This field +serves as an indication if a role mapping was created or if an existing entry was updated. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SecurityDocumentationIT.java[put-role-mapping-response] +-------------------------------------------------- +<1> `created` is a boolean indicating whether the role mapping was created or updated + +[[java-rest-high-security-put-role-mapping-async]] +==== Asynchronous Execution + +This request can be executed asynchronously using the `security().putRoleMappingAsync()` +method: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SecurityDocumentationIT.java[put-role-mapping-execute-async] +-------------------------------------------------- +<1> The `PutRoleMappingResponse` 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 `PutRoleMappingResponse` looks like: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SecurityDocumentationIT.java[put-role-mapping-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 e1b7cb2b9c10c..b58fcb228872e 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -308,12 +308,14 @@ The Java High Level REST Client supports the following Security APIs: * <> * <> * <> +* <> include::security/put-user.asciidoc[] include::security/enable-user.asciidoc[] include::security/disable-user.asciidoc[] include::security/change-password.asciidoc[] include::security/get-certificates.asciidoc[] +include::security/put-role-mapping.asciidoc[] == Watcher APIs