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 8afaa3551ad..14023e5e39d 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 final class SecurityClient {
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 3157abe6337..0a1d9cb3530 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 @@ package org.elasticsearch.client;
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 @@ final class SecurityRequestConverters {
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 00000000000..b8da17da72d
--- /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 00000000000..04cdb14163e
--- /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 2519c59b688..3373f325bfc 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 c96ac3cc5b5..4111b9fa2a2 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 class FieldRoleMapperExpression implements RoleMapperExpression {
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 0741c6f72d9..3c0e6dc4374 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.apache.http.client.methods.HttpPut;
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 class SecurityRequestConvertersTests extends ESTestCase {
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 ece1c1eacef..d3d12c4a90e 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.RequestOptions;
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 class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
}
}
+ 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 00000000000..f0a3f7572ef
--- /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 df94640f172..6b067e08e0b 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 class RoleMapperExpressionDslTests extends ESTestCase {
"]"+
"}";
- 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 00000000000..7f1bcb2839b
--- /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 b5bb41e981d..850a9049fb6 100644
--- a/docs/java-rest/high-level/supported-apis.asciidoc
+++ b/docs/java-rest/high-level/supported-apis.asciidoc
@@ -306,12 +306,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