HLRC: add change password API support (#33509)
This change adds support for the change password APIs to the high level rest client. Relates #29827
This commit is contained in:
parent
a8a2a83d45
commit
300896d401
|
@ -520,6 +520,7 @@ public class RestHighLevelClient implements Closeable {
|
|||
* Executes a reindex rethrottling request.
|
||||
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-reindex.html#docs-reindex-rethrottle">
|
||||
* Reindex rethrottling API on elastic.co</a>
|
||||
*
|
||||
* @param rethrottleRequest the request
|
||||
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||
* @return the response
|
||||
|
@ -534,6 +535,7 @@ public class RestHighLevelClient implements Closeable {
|
|||
* Executes a reindex rethrottling request.
|
||||
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-reindex.html#docs-reindex-rethrottle">
|
||||
* Reindex rethrottling API on elastic.co</a>
|
||||
*
|
||||
* @param rethrottleRequest the request
|
||||
* @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
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.elasticsearch.client.security.EnableUserRequest;
|
|||
import org.elasticsearch.client.security.PutUserRequest;
|
||||
import org.elasticsearch.client.security.PutUserResponse;
|
||||
import org.elasticsearch.client.security.EmptyResponse;
|
||||
import org.elasticsearch.client.security.ChangePasswordRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -47,6 +48,7 @@ public final class SecurityClient {
|
|||
* Create/update a user in the native realm synchronously.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-users.html">
|
||||
* the docs</a> for more.
|
||||
*
|
||||
* @param request the request with the user's 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 user call
|
||||
|
@ -61,6 +63,7 @@ public final class SecurityClient {
|
|||
* Asynchronously create/update a user in the native realm.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-users.html">
|
||||
* the docs</a> for more.
|
||||
*
|
||||
* @param request the request with the user's 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
|
||||
|
@ -74,6 +77,7 @@ public final class SecurityClient {
|
|||
* Enable a native realm or built-in user synchronously.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-enable-user.html">
|
||||
* the docs</a> for more.
|
||||
*
|
||||
* @param request the request with the user to enable
|
||||
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||
* @return the response from the enable user call
|
||||
|
@ -88,6 +92,7 @@ public final class SecurityClient {
|
|||
* Enable a native realm or built-in user asynchronously.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-enable-user.html">
|
||||
* the docs</a> for more.
|
||||
*
|
||||
* @param request the request with the user to enable
|
||||
* @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
|
||||
|
@ -102,6 +107,7 @@ public final class SecurityClient {
|
|||
* Disable a native realm or built-in user synchronously.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-disable-user.html">
|
||||
* the docs</a> for more.
|
||||
*
|
||||
* @param request the request with the user to disable
|
||||
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||
* @return the response from the enable user call
|
||||
|
@ -116,6 +122,7 @@ public final class SecurityClient {
|
|||
* Disable a native realm or built-in user asynchronously.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-disable-user.html">
|
||||
* the docs</a> for more.
|
||||
*
|
||||
* @param request the request with the user to disable
|
||||
* @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
|
||||
|
@ -125,4 +132,34 @@ public final class SecurityClient {
|
|||
restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::disableUser, options,
|
||||
EmptyResponse::fromXContent, listener, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the password of a user of a native realm or built-in user synchronously.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-change-password.html">
|
||||
* the docs</a> for more.
|
||||
*
|
||||
* @param request the request with the user's new password
|
||||
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||
* @return the response from the change user password call
|
||||
* @throws IOException in case there is a problem sending the request or parsing back the response
|
||||
*/
|
||||
public EmptyResponse changePassword(ChangePasswordRequest request, RequestOptions options) throws IOException {
|
||||
return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::changePassword, options,
|
||||
EmptyResponse::fromXContent, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the password of a user of a native realm or built-in user asynchronously.
|
||||
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-change-password.html">
|
||||
* the docs</a> for more.
|
||||
*
|
||||
* @param request the request with the user's new password
|
||||
* @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 changePasswordAsync(ChangePasswordRequest request, RequestOptions options,
|
||||
ActionListener<EmptyResponse> listener) {
|
||||
restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::changePassword, options,
|
||||
EmptyResponse::fromXContent, listener, emptySet());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,11 @@
|
|||
|
||||
package org.elasticsearch.client;
|
||||
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
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.PutUserRequest;
|
||||
import org.elasticsearch.client.security.SetUserEnabledRequest;
|
||||
|
||||
|
@ -34,6 +36,19 @@ final class SecurityRequestConverters {
|
|||
|
||||
private SecurityRequestConverters() {}
|
||||
|
||||
static Request changePassword(ChangePasswordRequest changePasswordRequest) throws IOException {
|
||||
String endpoint = new RequestConverters.EndpointBuilder()
|
||||
.addPathPartAsIs("_xpack/security/user")
|
||||
.addPathPart(changePasswordRequest.getUsername())
|
||||
.addPathPartAsIs("_password")
|
||||
.build();
|
||||
Request request = new Request(HttpPost.METHOD_NAME, endpoint);
|
||||
request.setEntity(createEntity(changePasswordRequest, REQUEST_BODY_CONTENT_TYPE));
|
||||
RequestConverters.Params params = new RequestConverters.Params(request);
|
||||
params.withRefreshPolicy(changePasswordRequest.getRefreshPolicy());
|
||||
return request;
|
||||
}
|
||||
|
||||
static Request putUser(PutUserRequest putUserRequest) throws IOException {
|
||||
String endpoint = new RequestConverters.EndpointBuilder()
|
||||
.addPathPartAsIs("_xpack/security/user")
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.CharArrays;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Request object to change the password of a user of a native realm or a built-in user.
|
||||
*/
|
||||
public final class ChangePasswordRequest implements Validatable, ToXContentObject {
|
||||
|
||||
private final String username;
|
||||
private final char[] password;
|
||||
private final RefreshPolicy refreshPolicy;
|
||||
|
||||
/**
|
||||
* @param username The username of the user whose password should be changed or null for the current user.
|
||||
* @param password The new password. The password array is not cleared by the {@link ChangePasswordRequest} object so the
|
||||
* calling code must clear it after receiving the response.
|
||||
* @param refreshPolicy The refresh policy for the request.
|
||||
*/
|
||||
public ChangePasswordRequest(@Nullable String username, char[] password, RefreshPolicy refreshPolicy) {
|
||||
this.username = username;
|
||||
this.password = Objects.requireNonNull(password, "password is required");
|
||||
this.refreshPolicy = refreshPolicy == null ? RefreshPolicy.getDefault() : refreshPolicy;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public char[] getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public RefreshPolicy getRefreshPolicy() {
|
||||
return refreshPolicy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
byte[] charBytes = CharArrays.toUtf8Bytes(password);
|
||||
try {
|
||||
return builder.startObject()
|
||||
.field("password").utf8Value(charBytes, 0, charBytes.length)
|
||||
.endObject();
|
||||
} finally {
|
||||
Arrays.fill(charBytes, (byte) 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,9 +19,11 @@
|
|||
|
||||
package org.elasticsearch.client;
|
||||
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
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.PutUserRequest;
|
||||
import org.elasticsearch.client.security.RefreshPolicy;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
@ -96,4 +98,29 @@ public class SecurityRequestConvertersTests extends ESTestCase {
|
|||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
public void testChangePassword() throws IOException {
|
||||
final String username = randomAlphaOfLengthBetween(4, 12);
|
||||
final char[] password = randomAlphaOfLengthBetween(8, 12).toCharArray();
|
||||
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
|
||||
final Map<String, String> expectedParams = getExpectedParamsFromRefreshPolicy(refreshPolicy);
|
||||
ChangePasswordRequest changePasswordRequest = new ChangePasswordRequest(username, password, refreshPolicy);
|
||||
Request request = SecurityRequestConverters.changePassword(changePasswordRequest);
|
||||
assertEquals(HttpPost.METHOD_NAME, request.getMethod());
|
||||
assertEquals("/_xpack/security/user/" + changePasswordRequest.getUsername() + "/_password", request.getEndpoint());
|
||||
assertEquals(expectedParams, request.getParameters());
|
||||
assertToXContentBody(changePasswordRequest, request.getEntity());
|
||||
}
|
||||
|
||||
public void testSelfChangePassword() throws IOException {
|
||||
final char[] password = randomAlphaOfLengthBetween(8, 12).toCharArray();
|
||||
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
|
||||
final Map<String, String> expectedParams = getExpectedParamsFromRefreshPolicy(refreshPolicy);
|
||||
ChangePasswordRequest changePasswordRequest = new ChangePasswordRequest(null, password, refreshPolicy);
|
||||
Request request = SecurityRequestConverters.changePassword(changePasswordRequest);
|
||||
assertEquals(HttpPost.METHOD_NAME, request.getMethod());
|
||||
assertEquals("/_xpack/security/user/_password", request.getEndpoint());
|
||||
assertEquals(expectedParams, request.getParameters());
|
||||
assertToXContentBody(changePasswordRequest, request.getEntity());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.elasticsearch.action.LatchedActionListener;
|
|||
import org.elasticsearch.client.ESRestHighLevelClientTestCase;
|
||||
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.EnableUserRequest;
|
||||
import org.elasticsearch.client.security.PutUserRequest;
|
||||
|
@ -42,7 +43,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
|
||||
{
|
||||
//tag::put-user-execute
|
||||
char[] password = new char[] { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' };
|
||||
char[] password = new char[]{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
|
||||
PutUserRequest request =
|
||||
new PutUserRequest("example", password, Collections.singletonList("superuser"), null, null, true, null, RefreshPolicy.NONE);
|
||||
PutUserResponse response = client.security().putUser(request, RequestOptions.DEFAULT);
|
||||
|
@ -56,7 +57,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
}
|
||||
|
||||
{
|
||||
char[] password = new char[] { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' };
|
||||
char[] password = new char[]{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
|
||||
PutUserRequest request = new PutUserRequest("example2", password, Collections.singletonList("superuser"), null, null, true,
|
||||
null, RefreshPolicy.NONE);
|
||||
// tag::put-user-execute-listener
|
||||
|
@ -173,4 +174,48 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
|
|||
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
||||
public void testChangePassword() throws Exception {
|
||||
RestHighLevelClient client = highLevelClient();
|
||||
char[] password = new char[]{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
|
||||
char[] newPassword = new char[]{'n', 'e', 'w', 'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
|
||||
PutUserRequest putUserRequest = new PutUserRequest("change_password_user", password, Collections.singletonList("superuser"),
|
||||
null, null, true, null, RefreshPolicy.NONE);
|
||||
PutUserResponse putUserResponse = client.security().putUser(putUserRequest, RequestOptions.DEFAULT);
|
||||
assertTrue(putUserResponse.isCreated());
|
||||
{
|
||||
//tag::change-password-execute
|
||||
ChangePasswordRequest request = new ChangePasswordRequest("change_password_user", newPassword, RefreshPolicy.NONE);
|
||||
EmptyResponse response = client.security().changePassword(request, RequestOptions.DEFAULT);
|
||||
//end::change-password-execute
|
||||
|
||||
assertNotNull(response);
|
||||
}
|
||||
{
|
||||
//tag::change-password-execute-listener
|
||||
ChangePasswordRequest request = new ChangePasswordRequest("change_password_user", password, RefreshPolicy.NONE);
|
||||
ActionListener<EmptyResponse> listener = new ActionListener<EmptyResponse>() {
|
||||
@Override
|
||||
public void onResponse(EmptyResponse emptyResponse) {
|
||||
// <1>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
// <2>
|
||||
}
|
||||
};
|
||||
//end::change-password-execute-listener
|
||||
|
||||
// Replace the empty listener by a blocking listener in test
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
listener = new LatchedActionListener<>(listener, latch);
|
||||
|
||||
//tag::change-password-execute-async
|
||||
client.security().changePasswordAsync(request, RequestOptions.DEFAULT, listener); // <1>
|
||||
//end::change-password-execute-async
|
||||
|
||||
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
[[java-rest-high-security-put-user]]
|
||||
=== Change Password User API
|
||||
|
||||
[[java-rest-high-security-put-user-execution]]
|
||||
==== Execution
|
||||
|
||||
A user's password can be changed using the `security().changePassword()`
|
||||
method:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/SecurityDocumentationIT.java[change-password-execute]
|
||||
--------------------------------------------------
|
||||
|
||||
[[java-rest-high-change-password-response]]
|
||||
==== Response
|
||||
|
||||
The returned `EmptyResponse` does not contain any fields. The return of this
|
||||
response indicates a successful request.
|
||||
|
||||
[[java-rest-high-x-pack-security-put-user-async]]
|
||||
==== Asynchronous Execution
|
||||
|
||||
This request can be executed asynchronously:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/SecurityDocumentationIT.java[change-password-execute-async]
|
||||
--------------------------------------------------
|
||||
<1> The `ChangePassword` request 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 `EmptyResponse` looks like:
|
||||
|
||||
["source","java",subs="attributes,callouts,macros"]
|
||||
--------------------------------------------------
|
||||
include-tagged::{doc-tests}/SecurityDocumentationIT.java[change-password-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.
|
|
@ -310,6 +310,16 @@ The Java High Level REST Client supports the following Watcher APIs:
|
|||
include::watcher/put-watch.asciidoc[]
|
||||
include::watcher/delete-watch.asciidoc[]
|
||||
|
||||
== Security APIs
|
||||
|
||||
The Java High Level REST Client supports the following Security APIs:
|
||||
|
||||
* <<java-rest-high-x-pack-security-put-user>>
|
||||
* <<java-rest-high-change-password-user>>
|
||||
|
||||
include::x-pack/security/put-user.asciidoc[]
|
||||
include::security/change-password.asciidoc[]
|
||||
|
||||
== Graph APIs
|
||||
|
||||
The Java High Level REST Client supports the following Graph APIs:
|
||||
|
|
Loading…
Reference in New Issue