From 4b17055035daee9f6f569112fdffd9c93d4c650f Mon Sep 17 00:00:00 2001 From: Nick Knize Date: Thu, 13 Dec 2018 12:24:48 -0600 Subject: [PATCH] HLRC: Add get users action (#36332) This commit adds get user action to the high level rest client. --- .../elasticsearch/client/SecurityClient.java | 29 ++++ .../client/SecurityRequestConverters.java | 10 ++ .../client/security/GetUsersRequest.java | 58 ++++++++ .../client/security/GetUsersResponse.java | 134 ++++++++++++++++++ .../client/security/user/User.java | 1 - .../org/elasticsearch/client/SecurityIT.java | 28 ++++ .../SecurityRequestConvertersTests.java | 16 +++ .../SecurityDocumentationIT.java | 92 ++++++++++++ .../security/AuthenticateResponseTests.java | 22 +-- .../client/security/GetUsersRequestTests.java | 53 +++++++ .../security/GetUsersResponseTests.java | 126 ++++++++++++++++ .../high-level/security/get-users.asciidoc | 48 +++++++ .../high-level/supported-apis.asciidoc | 2 + 13 files changed, 607 insertions(+), 12 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetUsersRequest.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetUsersResponse.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetUsersRequestTests.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetUsersResponseTests.java create mode 100644 docs/java-rest/high-level/security/get-users.asciidoc diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java index d02c15b59706f..48a1cdb778243 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 @@ -50,6 +50,8 @@ import org.elasticsearch.client.security.GetSslCertificatesResponse; import org.elasticsearch.client.security.GetUserPrivilegesRequest; import org.elasticsearch.client.security.GetUserPrivilegesResponse; +import org.elasticsearch.client.security.GetUsersRequest; +import org.elasticsearch.client.security.GetUsersResponse; import org.elasticsearch.client.security.HasPrivilegesRequest; import org.elasticsearch.client.security.HasPrivilegesResponse; import org.elasticsearch.client.security.InvalidateTokenRequest; @@ -81,6 +83,33 @@ public final class SecurityClient { this.restHighLevelClient = restHighLevelClient; } + /** + * Get a user, or list of users, in the native realm synchronously. + * See + * the docs for more information. + * @param request the request with the user's name + * @param options the request options (e.g., headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response from the get users call + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public GetUsersResponse getUsers(GetUsersRequest request, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::getUsers, options, + GetUsersResponse::fromXContent, emptySet()); + } + + /** + * Get a user, or list of users, in the native realm asynchronously. + * See + * the docs for more information. + * @param request the request with the user's name + * @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 getUsersAsync(GetUsersRequest request, RequestOptions options, ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::getUsers, options, + GetUsersResponse::fromXContent, listener, emptySet()); + } + /** * Create/update a user in the native realm 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 aa09b9596a83f..9e9698ded1cd8 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 @@ -36,6 +36,7 @@ import org.elasticsearch.client.security.GetPrivilegesRequest; import org.elasticsearch.client.security.GetRoleMappingsRequest; import org.elasticsearch.client.security.GetRolesRequest; +import org.elasticsearch.client.security.GetUsersRequest; import org.elasticsearch.client.security.HasPrivilegesRequest; import org.elasticsearch.client.security.InvalidateTokenRequest; import org.elasticsearch.client.security.PutPrivilegesRequest; @@ -67,6 +68,15 @@ static Request changePassword(ChangePasswordRequest changePasswordRequest) throw return request; } + static Request getUsers(GetUsersRequest getUsersRequest) { + RequestConverters.EndpointBuilder builder = new RequestConverters.EndpointBuilder() + .addPathPartAsIs("_security/user"); + if (getUsersRequest.getUsernames().size() > 0) { + builder.addPathPart(Strings.collectionToCommaDelimitedString(getUsersRequest.getUsernames())); + } + return new Request(HttpGet.METHOD_NAME, builder.build()); + } + static Request putUser(PutUserRequest putUserRequest) throws IOException { String endpoint = new RequestConverters.EndpointBuilder() .addPathPartAsIs("_security/user") diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetUsersRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetUsersRequest.java new file mode 100644 index 0000000000000..0a6b5e9bb2578 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetUsersRequest.java @@ -0,0 +1,58 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.client.security; + +import org.elasticsearch.client.Validatable; +import org.elasticsearch.common.util.set.Sets; + +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +/** + * Request object to retrieve users from the native realm + */ +public class GetUsersRequest implements Validatable { + private final Set usernames; + + public GetUsersRequest(final String... usernames) { + if (usernames != null) { + this.usernames = Collections.unmodifiableSet(Sets.newHashSet(usernames)); + } else { + this.usernames = Collections.emptySet(); + } + } + + public Set getUsernames() { + return usernames; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof GetUsersRequest)) return false; + GetUsersRequest that = (GetUsersRequest) o; + return Objects.equals(usernames, that.usernames); + } + + @Override + public int hashCode() { + return Objects.hash(usernames); + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetUsersResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetUsersResponse.java new file mode 100644 index 0000000000000..107b93afe7ce4 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetUsersResponse.java @@ -0,0 +1,134 @@ +/* + * 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.user.User; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParser.Token; +import org.elasticsearch.common.xcontent.XContentParserUtils; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; + +/** + * Response when requesting zero or more users. + * Returns a List of {@link User} objects + */ +public class GetUsersResponse { + private final Set users; + private final Set enabledUsers; + + public GetUsersResponse(Set users, Set enabledUsers) { + this.users = Collections.unmodifiableSet(users); + this.enabledUsers = Collections.unmodifiableSet(enabledUsers); + } + + public Set getUsers() { + return users; + } + + public Set getEnabledUsers() { + return enabledUsers; + } + + public static GetUsersResponse fromXContent(XContentParser parser) throws IOException { + XContentParserUtils.ensureExpectedToken(Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + final Set users = new HashSet<>(); + final Set enabledUsers = new HashSet<>(); + Token token; + while ((token = parser.nextToken()) != Token.END_OBJECT) { + XContentParserUtils.ensureExpectedToken(Token.FIELD_NAME, token, parser::getTokenLocation); + ParsedUser parsedUser = USER_PARSER.parse(parser, parser.currentName()); + users.add(parsedUser.user); + if (parsedUser.enabled) { + enabledUsers.add(parsedUser.user); + } + } + return new GetUsersResponse(users, enabledUsers); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof GetUsersResponse)) return false; + GetUsersResponse that = (GetUsersResponse) o; + return Objects.equals(users, that.users); + } + + @Override + public int hashCode() { + return Objects.hash(users); + } + + public static final ParseField USERNAME = new ParseField("username"); + public static final ParseField ROLES = new ParseField("roles"); + public static final ParseField FULL_NAME = new ParseField("full_name"); + public static final ParseField EMAIL = new ParseField("email"); + public static final ParseField METADATA = new ParseField("metadata"); + public static final ParseField ENABLED = new ParseField("enabled"); + + @SuppressWarnings("unchecked") + public static final ConstructingObjectParser USER_PARSER = new ConstructingObjectParser<>("user_info", + (constructorObjects) -> { + int i = 0; + final String username = (String) constructorObjects[i++]; + final Collection roles = (Collection) constructorObjects[i++]; + final Map metadata = (Map) constructorObjects[i++]; + final Boolean enabled = (Boolean) constructorObjects[i++]; + final String fullName = (String) constructorObjects[i++]; + final String email = (String) constructorObjects[i++]; + return new ParsedUser(username, roles, metadata, enabled, fullName, email); + }); + + static { + USER_PARSER.declareString(constructorArg(), USERNAME); + USER_PARSER.declareStringArray(constructorArg(), ROLES); + USER_PARSER.declareObject(constructorArg(), (parser, c) -> parser.map(), METADATA); + USER_PARSER.declareBoolean(constructorArg(), ENABLED); + USER_PARSER.declareStringOrNull(optionalConstructorArg(), FULL_NAME); + USER_PARSER.declareStringOrNull(optionalConstructorArg(), EMAIL); + } + + protected static final class ParsedUser { + protected User user; + protected boolean enabled; + + public ParsedUser(String username, Collection roles, Map metadata, Boolean enabled, + @Nullable String fullName, @Nullable String email) { + String checkedUsername = username = Objects.requireNonNull(username, "`username` is required, cannot be null"); + Collection checkedRoles = Collections.unmodifiableSet(new HashSet<>( + Objects.requireNonNull(roles, "`roles` is required, cannot be null. Pass an empty Collection instead."))); + Map checkedMetadata = Collections + .unmodifiableMap(Objects.requireNonNull(metadata, "`metadata` is required, cannot be null. Pass an empty map instead.")); + this.user = new User(checkedUsername, checkedRoles, checkedMetadata, fullName, email); + this.enabled = enabled; + } + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/User.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/User.java index ba6cd5f2f8ef5..4ac8f54c4741b 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/User.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/User.java @@ -29,7 +29,6 @@ import java.util.Objects; import java.util.Set; - /** * A user to be utilized with security APIs. * Can be an existing authenticated user or it can be a new user to be enrolled to the native realm. diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityIT.java index 05a854299a6bb..abf65d19df3b7 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityIT.java @@ -28,6 +28,8 @@ import org.elasticsearch.client.security.DeleteUserResponse; import org.elasticsearch.client.security.GetRolesRequest; import org.elasticsearch.client.security.GetRolesResponse; +import org.elasticsearch.client.security.GetUsersRequest; +import org.elasticsearch.client.security.GetUsersResponse; import org.elasticsearch.client.security.PutRoleRequest; import org.elasticsearch.client.security.PutRoleResponse; import org.elasticsearch.client.security.PutUserRequest; @@ -42,6 +44,7 @@ import org.elasticsearch.client.security.user.privileges.Role; import org.elasticsearch.common.CharArrays; +import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.HashMap; @@ -74,6 +77,22 @@ public void testPutUser() throws Exception { highLevelClient().getLowLevelClient().performRequest(deleteUserRequest); } + public void testGetUser() throws Exception { + final SecurityClient securityClient = highLevelClient().security(); + // create user + final PutUserRequest putUserRequest = randomPutUserRequest(randomBoolean()); + final PutUserResponse putUserResponse = execute(putUserRequest, securityClient::putUser, securityClient::putUserAsync); + // assert user created + assertThat(putUserResponse.isCreated(), is(true)); + // get user + final GetUsersRequest getUsersRequest = new GetUsersRequest(putUserRequest.getUser().getUsername()); + final GetUsersResponse getUsersResponse = execute(getUsersRequest, securityClient::getUsers, securityClient::getUsersAsync); + // assert user was correctly retrieved + ArrayList users = new ArrayList<>(); + users.addAll(getUsersResponse.getUsers()); + assertThat(users.get(0), is(putUserRequest.getUser())); + } + public void testAuthenticate() throws Exception { final SecurityClient securityClient = highLevelClient().security(); // test fixture: put enabled user @@ -89,6 +108,15 @@ public void testAuthenticate() throws Exception { assertThat(authenticateResponse.getUser(), is(putUserRequest.getUser())); assertThat(authenticateResponse.enabled(), is(true)); + // get user + final GetUsersRequest getUsersRequest = + new GetUsersRequest(putUserRequest.getUser().getUsername()); + final GetUsersResponse getUsersResponse = + execute(getUsersRequest, securityClient::getUsers, securityClient::getUsersAsync); + ArrayList users = new ArrayList<>(); + users.addAll(getUsersResponse.getUsers()); + assertThat(users.get(0), is(putUserRequest.getUser())); + // delete user final DeleteUserRequest deleteUserRequest = new DeleteUserRequest(putUserRequest.getUser().getUsername(), putUserRequest.getRefreshPolicy()); 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 87c692d9f2a3b..900f4210a9952 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 @@ -34,6 +34,7 @@ import org.elasticsearch.client.security.GetPrivilegesRequest; import org.elasticsearch.client.security.GetRoleMappingsRequest; import org.elasticsearch.client.security.GetRolesRequest; +import org.elasticsearch.client.security.GetUsersRequest; import org.elasticsearch.client.security.PutPrivilegesRequest; import org.elasticsearch.client.security.PutRoleMappingRequest; import org.elasticsearch.client.security.PutRoleRequest; @@ -101,6 +102,21 @@ public void testDeleteUser() { assertNull(request.getEntity()); } + public void testGetUsers() { + final String[] users = randomArray(0, 5, String[]::new, () -> randomAlphaOfLength(5)); + GetUsersRequest getUsersRequest = new GetUsersRequest(users); + Request request = SecurityRequestConverters.getUsers(getUsersRequest); + assertEquals(HttpGet.METHOD_NAME, request.getMethod()); + if (users.length == 0) { + assertEquals("/_security/user", request.getEndpoint()); + } else { + assertEquals("/_security/user/" + Strings.collectionToCommaDelimitedString(getUsersRequest.getUsernames()), + request.getEndpoint()); + } + assertNull(request.getEntity()); + assertEquals(Collections.emptyMap(), request.getParameters()); + } + public void testPutRoleMapping() throws IOException { final String username = randomAlphaOfLengthBetween(4, 7); final String rolename = randomAlphaOfLengthBetween(4, 7); 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 7d0438238e50c..c225685ad646e 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 @@ -54,6 +54,8 @@ import org.elasticsearch.client.security.GetRolesResponse; import org.elasticsearch.client.security.GetSslCertificatesResponse; import org.elasticsearch.client.security.GetUserPrivilegesResponse; +import org.elasticsearch.client.security.GetUsersRequest; +import org.elasticsearch.client.security.GetUsersResponse; import org.elasticsearch.client.security.HasPrivilegesRequest; import org.elasticsearch.client.security.HasPrivilegesResponse; import org.elasticsearch.client.security.InvalidateTokenRequest; @@ -109,6 +111,96 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase { + public void testGetUsers() throws Exception { + final RestHighLevelClient client = highLevelClient(); + String[] usernames = new String[] {"user1", "user2", "user3"}; + addUser(client, usernames[0], randomAlphaOfLength(4)); + addUser(client, usernames[1], randomAlphaOfLength(4)); + addUser(client, usernames[2], randomAlphaOfLength(4)); + { + //tag::get-users-request + GetUsersRequest request = new GetUsersRequest(usernames[0]); + //end::get-users-request + //tag::get-users-execute + GetUsersResponse response = client.security().getUsers(request, RequestOptions.DEFAULT); + //end::get-users-execute + //tag::get-users-response + List users = new ArrayList<>(1); + users.addAll(response.getUsers()); + //end::get-users-response + + assertNotNull(response); + assertThat(users.size(), equalTo(1)); + assertThat(users.get(0), is(usernames[0])); + } + + { + //tag::get-users-list-request + GetUsersRequest request = new GetUsersRequest(usernames); + GetUsersResponse response = client.security().getUsers(request, RequestOptions.DEFAULT); + //end::get-users-list-request + + List users = new ArrayList<>(3); + users.addAll(response.getUsers()); + assertNotNull(response); + assertThat(users.size(), equalTo(3)); + assertThat(users.get(0).getUsername(), equalTo(usernames[0])); + assertThat(users.get(1).getUsername(), equalTo(usernames[1])); + assertThat(users.get(2).getUsername(), equalTo(usernames[2])); + assertThat(users.size(), equalTo(3)); + } + + { + //tag::get-users-all-request + GetUsersRequest request = new GetUsersRequest(); + GetUsersResponse response = client.security().getUsers(request, RequestOptions.DEFAULT); + //end::get-users-all-request + + List users = new ArrayList<>(3); + users.addAll(response.getUsers()); + assertNotNull(response); + // 4 system users plus the three we created + assertThat(users.size(), equalTo(7)); + } + + { + GetUsersRequest request = new GetUsersRequest(usernames[0]); + ActionListener listener; + + //tag::get-roles-execute-listener + listener = new ActionListener() { + @Override + public void onResponse(GetUsersResponse getRolesResponse) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + //end::get-users-execute-listener + + assertNotNull(listener); + + // Replace the empty listener by a blocking listener in test + final PlainActionFuture future = new PlainActionFuture<>(); + listener = future; + + //tag::get-users-execute-async + client.security().getUsersAsync(request, RequestOptions.DEFAULT, listener); // <1> + //end::get-users-execute-async + + final GetUsersResponse response = future.get(30, TimeUnit.SECONDS); + List users = new ArrayList<>(1); + users.addAll(response.getUsers()); + assertNotNull(response); + assertThat(users.size(), equalTo(1)); + assertThat(users.get(0).getUsername(), equalTo(usernames[0])); + } + } + + public void testPutUser() throws Exception { RestHighLevelClient client = highLevelClient(); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/AuthenticateResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/AuthenticateResponseTests.java index f09340fa09ffd..f59038af55af7 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/AuthenticateResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/AuthenticateResponseTests.java @@ -38,12 +38,12 @@ public class AuthenticateResponseTests extends ESTestCase { public void testFromXContent() throws IOException { xContentTester( - this::createParser, - this::createTestInstance, - this::toXContent, - AuthenticateResponse::fromXContent) - .supportsUnknownFields(false) - .test(); + this::createParser, + this::createTestInstance, + this::toXContent, + AuthenticateResponse::fromXContent) + .supportsUnknownFields(false) + .test(); } public void testEqualsAndHashCode() { @@ -108,7 +108,7 @@ private void toXContent(AuthenticateResponse response, XContentBuilder builder) private AuthenticateResponse copy(AuthenticateResponse response) { final User originalUser = response.getUser(); final User copyUser = new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - originalUser.getFullName(), originalUser.getEmail()); + originalUser.getFullName(), originalUser.getEmail()); return new AuthenticateResponse(copyUser, response.enabled(), response.getAuthenticationRealm(), response.getLookupRealm()); } @@ -117,9 +117,9 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { final User originalUser = response.getUser(); switch (randomIntBetween(1, 8)) { case 1: - return new AuthenticateResponse(new User(originalUser.getUsername() + "wrong", originalUser.getRoles(), + return new AuthenticateResponse(new User(originalUser.getUsername() + "wrong", originalUser.getRoles(), originalUser.getMetadata(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(), - response.getAuthenticationRealm(), response.getLookupRealm()); + response.getAuthenticationRealm(), response.getLookupRealm()); case 2: final Collection wrongRoles = new ArrayList<>(originalUser.getRoles()); wrongRoles.add(randomAlphaOfLengthBetween(1, 4)); @@ -134,11 +134,11 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { response.getLookupRealm()); case 4: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - originalUser.getFullName() + "wrong", originalUser.getEmail()), response.enabled(), + originalUser.getFullName() + "wrong", originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(), response.getLookupRealm()); case 5: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - originalUser.getFullName(), originalUser.getEmail() + "wrong"), response.enabled(), + originalUser.getFullName(), originalUser.getEmail() + "wrong"), response.enabled(), response.getAuthenticationRealm(), response.getLookupRealm()); case 6: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetUsersRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetUsersRequestTests.java new file mode 100644 index 0000000000000..68b1751716e1f --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetUsersRequestTests.java @@ -0,0 +1,53 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.client.security; + +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.EqualsHashCodeTestUtils; + +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.equalTo; + +public class GetUsersRequestTests extends ESTestCase { + + public void testGetUsersRequest() { + final String[] users = randomArray(0, 5, String[]::new, () -> randomAlphaOfLength(5)); + GetUsersRequest getUsersRequest = new GetUsersRequest(users); + assertThat(getUsersRequest.getUsernames().size(), equalTo(users.length)); + assertThat(getUsersRequest.getUsernames(), containsInAnyOrder(users)); + } + + public void testEqualsHashCode() { + final String[] users = randomArray(0, 5, String[]::new, () -> randomAlphaOfLength(5)); + final GetUsersRequest getUsersRequest = new GetUsersRequest(users); + assertNotNull(getUsersRequest); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(getUsersRequest, (original) -> { + return new GetUsersRequest(original.getUsernames().toArray(new String[0])); + }); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(getUsersRequest, (original) -> { + return new GetUsersRequest(original.getUsernames().toArray(new String[0])); + }, GetUsersRequestTests::mutateTestItem); + } + + private static GetUsersRequest mutateTestItem(GetUsersRequest original) { + final int minRoles = original.getUsernames().isEmpty() ? 1 : 0; + return new GetUsersRequest(randomArray(minRoles, 5, String[]::new, () -> randomAlphaOfLength(6))); + } + +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetUsersResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetUsersResponseTests.java new file mode 100644 index 0000000000000..3025241bb3909 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetUsersResponseTests.java @@ -0,0 +1,126 @@ +/* + * 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.user.User; +import org.elasticsearch.common.xcontent.DeprecationHandler; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.EqualsHashCodeTestUtils; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.hamcrest.Matchers.equalTo; + +/** tests the Response for getting users from the security HLRC */ +public class GetUsersResponseTests extends ESTestCase { + public void testFromXContent() throws IOException { + String json = + "{\n" + + " \"jacknich\": {\n" + + " \"username\": \"jacknich\",\n" + + " \"roles\": [\n" + + " \"admin\", \"other_role1\"\n" + + " ],\n" + + " \"full_name\": \"Jack Nicholson\",\n" + + " \"email\": \"jacknich@example.com\",\n" + + " \"metadata\": { \"intelligence\" : 7 },\n" + + " \"enabled\": true\n" + + " }\n" + + "}"; + final GetUsersResponse response = GetUsersResponse.fromXContent((XContentType.JSON.xContent().createParser( + new NamedXContentRegistry(Collections.emptyList()), new DeprecationHandler() { + @Override + public void usedDeprecatedName(String usedName, String modernName) { + } + + @Override + public void usedDeprecatedField(String usedName, String replacedWith) { + } + }, json))); + assertThat(response.getUsers().size(), equalTo(1)); + final User user = response.getUsers().iterator().next(); + assertThat(user.getUsername(), equalTo("jacknich")); + assertThat(user.getRoles().size(), equalTo(2)); + assertThat(user.getFullName(), equalTo("Jack Nicholson")); + assertThat(user.getEmail(), equalTo("jacknich@example.com")); + final Map metadata = new HashMap<>(); + metadata.put("intelligence", 7); + assertThat(metadata, equalTo(user.getMetadata())); + } + + public void testEqualsHashCode() { + final Set users = new HashSet<>(); + final Set enabledUsers = new HashSet<>(); + Map metadata = new HashMap<>(); + metadata.put("intelligence", 1); + final User user1 = new User("testUser1", Arrays.asList(new String[] {"admin", "other_role1"}), + metadata, "Test User 1", null); + users.add(user1); + enabledUsers.add(user1); + Map metadata2 = new HashMap<>(); + metadata2.put("intelligence", 9); + metadata2.put("specialty", "geo"); + final User user2 = new User("testUser2", Arrays.asList(new String[] {"admin"}), + metadata, "Test User 2", "testuser2@example.com"); + users.add(user2); + enabledUsers.add(user2); + final GetUsersResponse getUsersResponse = new GetUsersResponse(users, enabledUsers); + assertNotNull(getUsersResponse); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(getUsersResponse, (original) -> { + return new GetUsersResponse(original.getUsers(), original.getEnabledUsers()); + }); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(getUsersResponse, (original) -> { + return new GetUsersResponse(original.getUsers(), original.getEnabledUsers()); + }, GetUsersResponseTests::mutateTestItem); + } + + private static GetUsersResponse mutateTestItem(GetUsersResponse original) { + if (randomBoolean()) { + final Set users = new HashSet<>(); + final Set enabledUsers = new HashSet<>(); + Map metadata = new HashMap<>(); + metadata.put("intelligence", 1); + final User user1 = new User("testUser1", Arrays.asList(new String[] {"admin", "other_role1"}), + metadata, "Test User 1", null); + users.add(user1); + enabledUsers.add(user1); + return new GetUsersResponse(users, enabledUsers); + } + Map metadata = new HashMap<>(); + metadata.put("intelligence", 5); // change intelligence + final User user1 = new User("testUser1", Arrays.asList(new String[] {"admin", "other_role1"}), + metadata, "Test User 1", null); + Set newUsers = original.getUsers().stream().collect(Collectors.toSet()); + Set enabledUsers = original.getEnabledUsers().stream().collect(Collectors.toSet()); + newUsers.clear(); + enabledUsers.clear(); + newUsers.add(user1); + enabledUsers.add(user1); + return new GetUsersResponse(newUsers, enabledUsers); + } +} diff --git a/docs/java-rest/high-level/security/get-users.asciidoc b/docs/java-rest/high-level/security/get-users.asciidoc new file mode 100644 index 0000000000000..e9e4a0d94911b --- /dev/null +++ b/docs/java-rest/high-level/security/get-users.asciidoc @@ -0,0 +1,48 @@ + +-- +:api: get-users +:request: GetUsersRequest +:respnse: GetUsersResponse +-- + +[id="{upid}-{api}"] +=== Get Users API + +[id="{upid}-{api}-request"] +==== Get Users Request + +Retrieving a user can be performed using the `security().getUsers()` +method and by setting the username on +{request}+: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-request] +-------------------------------------------------- + +Retrieving multiple users can be performed using the `security().getUsers()` +method and by setting multiple usernames on +{request}+: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-list-request] +-------------------------------------------------- + +Retrieving all users can be performed using the `security().getUsers()` +method without specifying any usernames on +{request}+: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-all-request] +-------------------------------------------------- + +include::../execution.asciidoc[] + +[id="{upid}-{api}-response"] +==== Get Users Response + +The returned +{response}+ allows getting information about the retrieved users as follows. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-response] +-------------------------------------------------- \ 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 70b66074aadba..0b4a2570c896d 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -387,6 +387,7 @@ include::rollup/get_rollup_index_caps.asciidoc[] The Java High Level REST Client supports the following Security APIs: * <> +* <<{upid}-get-users>> * <<{upid}-delete-user>> * <> * <> @@ -410,6 +411,7 @@ The Java High Level REST Client supports the following Security APIs: * <<{upid}-delete-privileges>> include::security/put-user.asciidoc[] +include::security/get-users.asciidoc[] include::security/delete-user.asciidoc[] include::security/enable-user.asciidoc[] include::security/disable-user.asciidoc[]