Skip to content

Commit

Permalink
HLRC: add change password API support (#33509)
Browse files Browse the repository at this point in the history
This change adds support for the change password APIs to the high
level rest client.

Relates #29827
  • Loading branch information
jkakavas authored Oct 2, 2018
1 parent a8a2a83 commit 300896d
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -480,9 +480,9 @@ public final BulkByScrollResponse updateByQuery(UpdateByQueryRequest updateByQue
* @param listener the listener to be notified upon request completion
*/
public final void updateByQueryAsync(UpdateByQueryRequest updateByQueryRequest, RequestOptions options,
ActionListener<BulkByScrollResponse> listener) {
ActionListener<BulkByScrollResponse> listener) {
performRequestAsyncAndParseEntity(
updateByQueryRequest, RequestConverters::updateByQuery, options, BulkByScrollResponse::fromXContent, listener, emptySet()
updateByQueryRequest, RequestConverters::updateByQuery, options, BulkByScrollResponse::fromXContent, listener, emptySet()
);
}

Expand Down Expand Up @@ -512,36 +512,38 @@ public final BulkByScrollResponse deleteByQuery(DeleteByQueryRequest deleteByQue
public final void deleteByQueryAsync(DeleteByQueryRequest deleteByQueryRequest, RequestOptions options,
ActionListener<BulkByScrollResponse> listener) {
performRequestAsyncAndParseEntity(
deleteByQueryRequest, RequestConverters::deleteByQuery, options, BulkByScrollResponse::fromXContent, listener, emptySet()
deleteByQueryRequest, RequestConverters::deleteByQuery, options, BulkByScrollResponse::fromXContent, listener, emptySet()
);
}

/**
* 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>
* 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 options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public final ListTasksResponse reindexRethrottle(RethrottleRequest rethrottleRequest, RequestOptions options) throws IOException {
return performRequestAndParseEntity(rethrottleRequest, RequestConverters::rethrottle, options, ListTasksResponse::fromXContent,
emptySet());
emptySet());
}

/**
* 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>
* 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
* @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 final void reindexRethrottleAsync(RethrottleRequest rethrottleRequest, RequestOptions options,
ActionListener<ListTasksResponse> listener) {
ActionListener<ListTasksResponse> listener) {
performRequestAsyncAndParseEntity(rethrottleRequest, RequestConverters::rethrottle, options, ListTasksResponse::fromXContent,
listener, emptySet());
listener, emptySet());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
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;

Expand All @@ -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
Expand All @@ -61,8 +63,9 @@ public PutUserResponse putUser(PutUserRequest request, RequestOptions options) t
* 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 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
*/
public void putUserAsync(PutUserRequest request, RequestOptions options, ActionListener<PutUserResponse> listener) {
Expand All @@ -74,6 +77,7 @@ public void putUserAsync(PutUserRequest request, RequestOptions options, ActionL
* 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
Expand All @@ -88,12 +92,13 @@ public EmptyResponse enableUser(EnableUserRequest request, RequestOptions option
* 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 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
*/
public void enableUserAsync(EnableUserRequest request, RequestOptions options,
ActionListener<EmptyResponse> listener) {
ActionListener<EmptyResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::enableUser, options,
EmptyResponse::fromXContent, listener, emptySet());
}
Expand All @@ -102,6 +107,7 @@ public void enableUserAsync(EnableUserRequest request, RequestOptions options,
* 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
Expand All @@ -116,13 +122,44 @@ public EmptyResponse disableUser(DisableUserRequest request, RequestOptions opti
* 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 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
*/
public void disableUserAsync(DisableUserRequest request, RequestOptions options,
ActionListener<EmptyResponse> listener) {
ActionListener<EmptyResponse> listener) {
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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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")
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -91,9 +93,34 @@ public void testDisableUser() {

private static Map<String, String> getExpectedParamsFromRefreshPolicy(RefreshPolicy refreshPolicy) {
if (refreshPolicy != RefreshPolicy.NONE) {
return Collections.singletonMap("refresh", refreshPolicy.getValue());
return Collections.singletonMap("refresh", refreshPolicy.getValue());
} else {
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());
}
}
Loading

0 comments on commit 300896d

Please sign in to comment.