From 1beb95d45a68dc3bde0de5e35f180bc26e5f59ae Mon Sep 17 00:00:00 2001 From: Nicholas Knize Date: Thu, 29 Nov 2018 11:43:56 -0600 Subject: [PATCH 01/11] HLRC: Add get users action This commit adds get user action to the high level rest client. --- .../elasticsearch/client/SecurityClient.java | 30 +++++ .../client/SecurityRequestConverters.java | 10 ++ .../client/security/AuthenticateResponse.java | 2 +- .../client/security/GetUsersRequest.java | 58 +++++++++ .../client/security/GetUsersResponse.java | 70 +++++++++++ .../client/security/user/User.java | 48 ++++++- .../org/elasticsearch/client/SecurityIT.java | 13 +- .../SecurityRequestConvertersTests.java | 16 ++- .../SecurityDocumentationIT.java | 15 ++- .../security/AuthenticateResponseTests.java | 38 +++--- .../client/security/GetUsersRequestTests.java | 53 ++++++++ .../security/GetUsersResponseTests.java | 119 ++++++++++++++++++ .../client/security/PutUserRequestTests.java | 6 +- 13 files changed, 450 insertions(+), 28 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 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 a033ee61f79dc..8c888707141c1 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 @@ -48,6 +48,8 @@ import org.elasticsearch.client.security.GetRolesResponse; import org.elasticsearch.client.security.GetSslCertificatesRequest; import org.elasticsearch.client.security.GetSslCertificatesResponse; +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; @@ -75,6 +77,34 @@ 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 nuser'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 nuser'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 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 6485899acf947..432b9896c3065 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 @@ -32,6 +32,7 @@ import org.elasticsearch.client.security.DeleteRoleMappingRequest; import org.elasticsearch.client.security.DeleteRoleRequest; import org.elasticsearch.client.security.DeleteUserRequest; +import org.elasticsearch.client.security.GetUsersRequest; import org.elasticsearch.client.security.InvalidateTokenRequest; import org.elasticsearch.client.security.GetRolesRequest; import org.elasticsearch.client.security.PutRoleMappingRequest; @@ -65,6 +66,15 @@ static Request changePassword(ChangePasswordRequest changePasswordRequest) throw return request; } + static Request getUsers(GetUsersRequest getUsersRequest) { + RequestConverters.EndpointBuilder builder = new RequestConverters.EndpointBuilder() + .addPathPartAsIs("_xpack/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("_xpack/security/user") diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/AuthenticateResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/AuthenticateResponse.java index b3b8fc2c23591..9414b897ad5e4 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/AuthenticateResponse.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/AuthenticateResponse.java @@ -55,7 +55,7 @@ public final class AuthenticateResponse { private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( "client_security_authenticate_response", a -> new AuthenticateResponse(new User((String) a[0], ((List) a[1]), (Map) a[2], - (String) a[3], (String) a[4]), (Boolean) a[5], (RealmInfo) a[6], (RealmInfo) a[7])); + (Boolean) a[5], (String) a[3], (String) a[4]), (Boolean) a[5], (RealmInfo) a[6], (RealmInfo) a[7])); static { final ConstructingObjectParser realmInfoParser = new ConstructingObjectParser<>("realm_info", a -> new RealmInfo((String) a[0], (String) a[1])); 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..e14cbd18a9da2 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetUsersResponse.java @@ -0,0 +1,70 @@ +/* + * 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.XContentParser; +import org.elasticsearch.common.xcontent.XContentParser.Token; +import org.elasticsearch.common.xcontent.XContentParserUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Response when requesting zero or more users. + * Returns a List of {@link User} objects + */ +public class GetUsersResponse { + private final List users; + + public GetUsersResponse(List users) { + this.users = Collections.unmodifiableList(users); + } + + public List getUsers() { + return users; + } + + public static GetUsersResponse fromXContent(XContentParser parser) throws IOException { + XContentParserUtils.ensureExpectedToken(Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + final List users = new ArrayList<>(); + Token token; + while ((token = parser.nextToken()) != Token.END_OBJECT) { + XContentParserUtils.ensureExpectedToken(Token.FIELD_NAME, token, parser::getTokenLocation); + users.add(User.PARSER.parse(parser, parser.currentName())); + } + return new GetUsersResponse(users); + } + + @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); + } +} 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..2d5d3664de755 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 @@ -20,7 +20,9 @@ package org.elasticsearch.client.security.user; import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; import java.util.Collection; import java.util.Collections; @@ -29,6 +31,8 @@ import java.util.Objects; import java.util.Set; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; /** * A user to be utilized with security APIs. @@ -36,9 +40,39 @@ */ public final class User { + 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 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 User(username, roles, metadata, enabled, fullName, email); + }); + + static { + PARSER.declareString(constructorArg(), USERNAME); + PARSER.declareStringArray(constructorArg(), ROLES); + PARSER.declareObject(constructorArg(), (parser, c) -> parser.map(), METADATA); + PARSER.declareBoolean(constructorArg(), ENABLED); + PARSER.declareStringOrNull(optionalConstructorArg(), FULL_NAME); + PARSER.declareStringOrNull(optionalConstructorArg(), EMAIL); + } + private final String username; private final Set roles; private final Map metadata; + private final boolean enabled; @Nullable private final String fullName; @Nullable private final String email; @@ -51,13 +85,14 @@ public final class User { * @param fullName the full name of the user that may be used for display purposes * @param email the email address of the user */ - public User(String username, Collection roles, Map metadata, @Nullable String fullName, + public User(String username, Collection roles, Map metadata, Boolean enabled, @Nullable String fullName, @Nullable String email) { this.username = username = Objects.requireNonNull(username, "`username` is required, cannot be null"); this.roles = Collections.unmodifiableSet(new HashSet<>( Objects.requireNonNull(roles, "`roles` is required, cannot be null. Pass an empty Collection instead."))); this.metadata = Collections .unmodifiableMap(Objects.requireNonNull(metadata, "`metadata` is required, cannot be null. Pass an empty map instead.")); + this.enabled = enabled.booleanValue(); this.fullName = fullName; this.email = email; } @@ -69,7 +104,7 @@ public User(String username, Collection roles, Map metad * @param roles the roles that this user is assigned */ public User(String username, Collection roles) { - this(username, roles, Collections.emptyMap(), null, null); + this(username, roles, Collections.emptyMap(), true, null, null); } /** @@ -96,6 +131,11 @@ public Map getMetadata() { return metadata; } + /** @return Whether or not this user is enabled */ + public boolean getEnabled() { + return enabled; + } + /** * @return The full name of this user. May be {@code null}. */ @@ -116,6 +156,7 @@ public String toString() { sb.append("User[username=").append(username); sb.append(",roles=[").append(Strings.collectionToCommaDelimitedString(roles)).append("]"); sb.append(",metadata=").append(metadata); + sb.append(",enabled=").append(enabled); sb.append(",fullName=").append(fullName); sb.append(",email=").append(email); sb.append("]"); @@ -130,13 +171,14 @@ public boolean equals(Object o) { return Objects.equals(username, that.username) && Objects.equals(roles, that.roles) && Objects.equals(metadata, that.metadata) + && enabled == that.enabled && Objects.equals(fullName, that.fullName) && Objects.equals(email, that.email); } @Override public int hashCode() { - return Objects.hash(username, roles, metadata, fullName, email); + return Objects.hash(username, roles, metadata, enabled, fullName, email); } } 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 27b1d31e6d7d5..9b91b9c8615f1 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 @@ -24,6 +24,8 @@ import org.elasticsearch.client.security.AuthenticateResponse; import org.elasticsearch.client.security.DeleteUserRequest; import org.elasticsearch.client.security.DeleteUserResponse; +import org.elasticsearch.client.security.GetUsersRequest; +import org.elasticsearch.client.security.GetUsersResponse; import org.elasticsearch.client.security.PutUserRequest; import org.elasticsearch.client.security.PutUserResponse; import org.elasticsearch.client.security.RefreshPolicy; @@ -36,6 +38,7 @@ import java.util.List; import java.util.Map; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.containsString; @@ -75,6 +78,13 @@ 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); + assertThat(getUsersResponse.getUsers().get(0), is(putUserRequest.getUser())); + // delete user final DeleteUserRequest deleteUserRequest = new DeleteUserRequest(putUserRequest.getUser().getUsername(), putUserRequest.getRefreshPolicy()); @@ -103,6 +113,7 @@ private static User randomUser(String username) { final List roles = Arrays.asList(generateRandomStringArray(3, 3, false, true)); final String fullName = randomFrom(random(), null, randomAlphaOfLengthBetween(0, 3)); final String email = randomFrom(random(), null, randomAlphaOfLengthBetween(0, 3)); + final boolean enabled = randomBoolean(); final Map metadata; metadata = new HashMap<>(); if (randomBoolean()) { @@ -115,7 +126,7 @@ private static User randomUser(String username) { } else { metadata.put("string_list", Arrays.asList(generateRandomStringArray(4, 4, false, true))); } - return new User(username, roles, metadata, fullName, email); + return new User(username, roles, metadata, enabled, fullName, email); } private static PutUserRequest randomPutUserRequest(boolean enabled) { 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 110e0cc56c986..8b0b8fd19efea 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.GetRoleMappingsRequest; import org.elasticsearch.client.security.ChangePasswordRequest; import org.elasticsearch.client.security.GetRolesRequest; +import org.elasticsearch.client.security.GetUsersRequest; import org.elasticsearch.client.security.PutRoleMappingRequest; import org.elasticsearch.client.security.PutUserRequest; import org.elasticsearch.client.security.RefreshPolicy; @@ -68,7 +69,7 @@ public void testPutUser() throws IOException { metadata.put(String.valueOf(i), randomAlphaOfLengthBetween(1, 12)); } } - final User user = new User(username, roles, metadata, fullName, email); + final User user = new User(username, roles, metadata, true, fullName, email); final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); final Map expectedParams = getExpectedParamsFromRefreshPolicy(refreshPolicy); @@ -93,6 +94,19 @@ public void testDeleteUser() { assertNull(request.getEntity()); } + public void testGetUsers() { + final String[] users = new String[] {"test"}; + GetUsersRequest getUsersRequest = new GetUsersRequest(users); + final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); + final Map expectedParams = getExpectedParamsFromRefreshPolicy(refreshPolicy); + Request request = SecurityRequestConverters.getUsers(getUsersRequest); + assertEquals(HttpGet.METHOD_NAME, request.getMethod()); + assertEquals("/_xpack/security/user/test", request.getEndpoint()); + assertEquals(expectedParams, request.getParameters()); + assertNull(request.getEntity()); + + } + 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 79258b314510c..01d5e06443fef 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 @@ -58,6 +58,8 @@ import org.elasticsearch.client.security.GetRolesRequest; import org.elasticsearch.client.security.GetRolesResponse; import org.elasticsearch.client.security.GetSslCertificatesResponse; +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; @@ -110,6 +112,17 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase { + public void testGetUsers() throws Exception { + RestHighLevelClient client = highLevelClient(); + addUser(client, "testUser", "testPassword"); + + { + GetUsersRequest getUsersRequest = new GetUsersRequest(); + GetUsersResponse getUsersResponse = client.security().getUsers(getUsersRequest, RequestOptions.DEFAULT); + assertNotNull(getUsersResponse.getUsers()); + } + } + public void testPutUser() throws Exception { RestHighLevelClient client = highLevelClient(); @@ -876,7 +889,7 @@ 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'}; - User user = new User("change_password_user", Collections.singletonList("superuser"), Collections.emptyMap(), null, null); + User user = new User("change_password_user", Collections.singletonList("superuser"), Collections.emptyMap(), true, null, null); PutUserRequest putUserRequest = new PutUserRequest(user, password, true, RefreshPolicy.NONE); PutUserResponse putUserResponse = client.security().putUser(putUserRequest, RequestOptions.DEFAULT); assertTrue(putUserResponse.isCreated()); 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..388be7e41cba8 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 @@ -75,7 +75,7 @@ protected AuthenticateResponse createTestInstance() { final String lookupRealmName = randomAlphaOfLength(5); final String lookupRealmType = randomFrom("file", "native", "ldap", "active_directory", "saml", "kerberos"); return new AuthenticateResponse( - new User(username, roles, metadata, fullName, email), enabled, + new User(username, roles, metadata, enabled, fullName, email), enabled, new AuthenticateResponse.RealmInfo(authenticationRealmName, authenticationRealmType), new AuthenticateResponse.RealmInfo(lookupRealmName, lookupRealmType)); } @@ -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()); + response.enabled(), originalUser.getFullName(), originalUser.getEmail()); return new AuthenticateResponse(copyUser, response.enabled(), response.getAuthenticationRealm(), response.getLookupRealm()); } @@ -118,40 +118,42 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { switch (randomIntBetween(1, 8)) { case 1: return new AuthenticateResponse(new User(originalUser.getUsername() + "wrong", originalUser.getRoles(), - originalUser.getMetadata(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(), - response.getAuthenticationRealm(), response.getLookupRealm()); + originalUser.getMetadata(), response.enabled(), originalUser.getFullName(), originalUser.getEmail()), + response.enabled(), response.getAuthenticationRealm(), response.getLookupRealm()); case 2: final Collection wrongRoles = new ArrayList<>(originalUser.getRoles()); wrongRoles.add(randomAlphaOfLengthBetween(1, 4)); return new AuthenticateResponse(new User(originalUser.getUsername(), wrongRoles, originalUser.getMetadata(), - originalUser.getFullName(), originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(), - response.getLookupRealm()); + response.enabled(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(), + response.getAuthenticationRealm(), response.getLookupRealm()); case 3: final Map wrongMetadata = new HashMap<>(originalUser.getMetadata()); wrongMetadata.put("wrong_string", randomAlphaOfLengthBetween(0, 4)); return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), wrongMetadata, - originalUser.getFullName(), originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(), - response.getLookupRealm()); + response.enabled(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(), + response.getAuthenticationRealm(), response.getLookupRealm()); case 4: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - originalUser.getFullName() + "wrong", originalUser.getEmail()), response.enabled(), - response.getAuthenticationRealm(), response.getLookupRealm()); + 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(), - response.getAuthenticationRealm(), response.getLookupRealm()); + 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(), - originalUser.getFullName(), originalUser.getEmail()), !response.enabled(), response.getAuthenticationRealm(), - response.getLookupRealm()); + response.enabled(), originalUser.getFullName(), originalUser.getEmail()), !response.enabled(), + response.getAuthenticationRealm(), response.getLookupRealm()); case 7: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - originalUser.getFullName(), originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(), - new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5))); + response.enabled(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(), + response.getAuthenticationRealm(), new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), + randomAlphaOfLength(5))); case 8: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - originalUser.getFullName(), originalUser.getEmail()), response.enabled(), - new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)), response.getLookupRealm()); + response.enabled(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(), + new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)), + response.getLookupRealm()); } throw new IllegalStateException("Bad random number"); } 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..03b2d8b024472 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetUsersResponseTests.java @@ -0,0 +1,119 @@ +/* + * 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.ArrayList; +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; + +/** 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().get(0); + assertThat(user.getUsername(), equalTo("jacknich")); + assertThat(user.getRoles().size(), equalTo(2)); + assertThat(user.getFullName(), equalTo("Jack Nicholson")); + assertThat(user.getEmail(), equalTo("jacknich@example.com")); + assertTrue(user.getEnabled()); + final Map metadata = new HashMap<>(); + metadata.put("intelligence", 7); + assertThat(metadata, equalTo(user.getMetadata())); + } + + public void testEqualsHashCode() { + final List users = new ArrayList<>(); + Map metadata = new HashMap<>(); + metadata.put("intelligence", 1); + final User user1 = new User("testUser1", Arrays.asList(new String[] {"admin", "other_role1"}), + metadata, true, "Test User 1", null); + users.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, true, "Test User 2", "testuser2@example.com"); + users.add(user2); + final GetUsersResponse getUsersResponse = new GetUsersResponse(users); + assertNotNull(getUsersResponse); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(getUsersResponse, (original) -> { + return new GetUsersResponse(original.getUsers()); + }); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(getUsersResponse, (original) -> { + return new GetUsersResponse(original.getUsers()); + }, GetUsersResponseTests::mutateTestItem); + } + + private static GetUsersResponse mutateTestItem(GetUsersResponse original) { + if (randomBoolean()) { + final List users = new ArrayList<>(); + Map metadata = new HashMap<>(); + metadata.put("intelligence", 1); + final User user1 = new User("testUser1", Arrays.asList(new String[] {"admin", "other_role1"}), + metadata, true, "Test User 1", null); + users.add(user1); + return new GetUsersResponse(users); + } + Map metadata = new HashMap<>(); + metadata.put("intelligence", 5); // change intelligence + final User user1 = new User("testUser1", Arrays.asList(new String[] {"admin", "other_role1"}), + metadata, true, "Test User 1", null); + List newUsers = original.getUsers().stream().collect(Collectors.toList()); + newUsers.remove(0); + newUsers.add(user1); + return new GetUsersResponse(newUsers); + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/PutUserRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/PutUserRequestTests.java index 76d3b283b0d90..b01729eadf093 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/PutUserRequestTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/PutUserRequestTests.java @@ -37,7 +37,7 @@ public class PutUserRequestTests extends ESTestCase { public void testBuildRequestWithPassword() throws Exception { final User user = new User("hawkeye", Arrays.asList("kibana_user", "avengers"), - Collections.singletonMap("status", "active"), "Clinton Barton", null); + Collections.singletonMap("status", "active"), true, "Clinton Barton", null); final char[] password = "f@rmb0y".toCharArray(); final PutUserRequest request = PutUserRequest.withPassword(user, password, true, RefreshPolicy.IMMEDIATE); String json = Strings.toString(request); @@ -58,7 +58,7 @@ public void testBuildRequestWithPassword() throws Exception { public void testBuildRequestWithPasswordHash() throws Exception { final User user = new User("hawkeye", Arrays.asList("kibana_user", "avengers"), - Collections.singletonMap("status", "active"), "Clinton Barton", null); + Collections.singletonMap("status", "active"), true, "Clinton Barton", null); final char[] passwordHash = "$2a$04$iu1G4x3ZKVDNi6egZIjkFuIPja6elQXiBF1LdRVauV4TGog6FYOpi".toCharArray(); final PutUserRequest request = PutUserRequest.withPasswordHash(user, passwordHash, true, RefreshPolicy.IMMEDIATE); String json = Strings.toString(request); @@ -79,7 +79,7 @@ public void testBuildRequestWithPasswordHash() throws Exception { public void testBuildRequestForUpdateOnly() throws Exception { final User user = new User("hawkeye", Arrays.asList("kibana_user", "avengers"), - Collections.singletonMap("status", "active"), "Clinton Barton", null); + Collections.singletonMap("status", "active"), true, "Clinton Barton", null); final char[] passwordHash = "$2a$04$iu1G4x3ZKVDNi6egZIjkFuIPja6elQXiBF1LdRVauV4TGog6FYOpi".toCharArray(); final PutUserRequest request = PutUserRequest.updateUser(user, true, RefreshPolicy.IMMEDIATE); String json = Strings.toString(request); From e9e73b920137c302a4e559997a76888ff4a4977b Mon Sep 17 00:00:00 2001 From: Nicholas Knize Date: Tue, 11 Dec 2018 12:44:20 -0600 Subject: [PATCH 02/11] convert users to a Set, add enabledUsers set to GetUsersResponse, move User parser to GetUsersResponse, and remove enabled flag from user --- .../elasticsearch/client/SecurityClient.java | 7 +- .../client/security/AuthenticateResponse.java | 2 +- .../client/security/GetUsersResponse.java | 82 +++++++++++++++++-- .../client/security/user/User.java | 44 +--------- .../org/elasticsearch/client/SecurityIT.java | 5 +- .../SecurityRequestConvertersTests.java | 2 +- .../SecurityDocumentationIT.java | 2 +- .../security/AuthenticateResponseTests.java | 20 ++--- .../security/GetUsersResponseTests.java | 40 +++++---- .../client/security/PutUserRequestTests.java | 6 +- 10 files changed, 122 insertions(+), 88 deletions(-) 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 8c888707141c1..b912365768d7a 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 @@ -81,7 +81,7 @@ public final class SecurityClient { * Get a user, or list of users, in the native realm synchronously. * See * the docs for more information. - * @param request the request with the nuser's name + * @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 @@ -95,10 +95,9 @@ public GetUsersResponse getUsers(GetUsersRequest request, RequestOptions options * Get a user, or list of users, in the native realm asynchronously. * See * the docs for more information. - * @param request the request with the nuser's name + * @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 + * @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, diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/AuthenticateResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/AuthenticateResponse.java index 9414b897ad5e4..b3b8fc2c23591 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/AuthenticateResponse.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/AuthenticateResponse.java @@ -55,7 +55,7 @@ public final class AuthenticateResponse { private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( "client_security_authenticate_response", a -> new AuthenticateResponse(new User((String) a[0], ((List) a[1]), (Map) a[2], - (Boolean) a[5], (String) a[3], (String) a[4]), (Boolean) a[5], (RealmInfo) a[6], (RealmInfo) a[7])); + (String) a[3], (String) a[4]), (Boolean) a[5], (RealmInfo) a[6], (RealmInfo) a[7])); static { final ConstructingObjectParser realmInfoParser = new ConstructingObjectParser<>("realm_info", a -> new RealmInfo((String) a[0], (String) a[1])); 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 index e14cbd18a9da2..15f01e63d39f5 100644 --- 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 @@ -19,40 +19,59 @@ 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.ArrayList; +import java.util.Collection; import java.util.Collections; -import java.util.List; +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 List users; + private final Set users; + private final Set enabledUsers; - public GetUsersResponse(List users) { - this.users = Collections.unmodifiableList(users); + public GetUsersResponse(Set users, Set enabledUsers) { + this.users = Collections.unmodifiableSet(users); + this.enabledUsers = Collections.unmodifiableSet(enabledUsers); } - public List getUsers() { + 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 List users = new ArrayList<>(); + 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); - users.add(User.PARSER.parse(parser, parser.currentName())); + ParsedUser parsedUser = USER_PARSER.parse(parser, parser.currentName()); + users.add(parsedUser.user); + if (parsedUser.enabled) { + enabledUsers.add(parsedUser.user); + } } - return new GetUsersResponse(users); + return new GetUsersResponse(users, enabledUsers); } @Override @@ -67,4 +86,49 @@ public boolean equals(Object o) { 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 2d5d3664de755..f18452d77caef 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 @@ -40,39 +40,9 @@ */ public final class User { - 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 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 User(username, roles, metadata, enabled, fullName, email); - }); - - static { - PARSER.declareString(constructorArg(), USERNAME); - PARSER.declareStringArray(constructorArg(), ROLES); - PARSER.declareObject(constructorArg(), (parser, c) -> parser.map(), METADATA); - PARSER.declareBoolean(constructorArg(), ENABLED); - PARSER.declareStringOrNull(optionalConstructorArg(), FULL_NAME); - PARSER.declareStringOrNull(optionalConstructorArg(), EMAIL); - } - private final String username; private final Set roles; private final Map metadata; - private final boolean enabled; @Nullable private final String fullName; @Nullable private final String email; @@ -85,14 +55,13 @@ public final class User { * @param fullName the full name of the user that may be used for display purposes * @param email the email address of the user */ - public User(String username, Collection roles, Map metadata, Boolean enabled, @Nullable String fullName, + public User(String username, Collection roles, Map metadata, @Nullable String fullName, @Nullable String email) { this.username = username = Objects.requireNonNull(username, "`username` is required, cannot be null"); this.roles = Collections.unmodifiableSet(new HashSet<>( Objects.requireNonNull(roles, "`roles` is required, cannot be null. Pass an empty Collection instead."))); this.metadata = Collections .unmodifiableMap(Objects.requireNonNull(metadata, "`metadata` is required, cannot be null. Pass an empty map instead.")); - this.enabled = enabled.booleanValue(); this.fullName = fullName; this.email = email; } @@ -104,7 +73,7 @@ public User(String username, Collection roles, Map metad * @param roles the roles that this user is assigned */ public User(String username, Collection roles) { - this(username, roles, Collections.emptyMap(), true, null, null); + this(username, roles, Collections.emptyMap(), null, null); } /** @@ -131,11 +100,6 @@ public Map getMetadata() { return metadata; } - /** @return Whether or not this user is enabled */ - public boolean getEnabled() { - return enabled; - } - /** * @return The full name of this user. May be {@code null}. */ @@ -156,7 +120,6 @@ public String toString() { sb.append("User[username=").append(username); sb.append(",roles=[").append(Strings.collectionToCommaDelimitedString(roles)).append("]"); sb.append(",metadata=").append(metadata); - sb.append(",enabled=").append(enabled); sb.append(",fullName=").append(fullName); sb.append(",email=").append(email); sb.append("]"); @@ -171,14 +134,13 @@ public boolean equals(Object o) { return Objects.equals(username, that.username) && Objects.equals(roles, that.roles) && Objects.equals(metadata, that.metadata) - && enabled == that.enabled && Objects.equals(fullName, that.fullName) && Objects.equals(email, that.email); } @Override public int hashCode() { - return Objects.hash(username, roles, metadata, enabled, fullName, email); + return Objects.hash(username, roles, metadata, fullName, email); } } 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 9b91b9c8615f1..ac13ba0593ace 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 @@ -83,7 +83,7 @@ public void testAuthenticate() throws Exception { new GetUsersRequest(putUserRequest.getUser().getUsername()); final GetUsersResponse getUsersResponse = execute(getUsersRequest, securityClient::getUsers, securityClient::getUsersAsync); - assertThat(getUsersResponse.getUsers().get(0), is(putUserRequest.getUser())); + assertThat(Arrays.asList(getUsersResponse.getUsers()).get(0), is(putUserRequest.getUser())); // delete user final DeleteUserRequest deleteUserRequest = @@ -113,7 +113,6 @@ private static User randomUser(String username) { final List roles = Arrays.asList(generateRandomStringArray(3, 3, false, true)); final String fullName = randomFrom(random(), null, randomAlphaOfLengthBetween(0, 3)); final String email = randomFrom(random(), null, randomAlphaOfLengthBetween(0, 3)); - final boolean enabled = randomBoolean(); final Map metadata; metadata = new HashMap<>(); if (randomBoolean()) { @@ -126,7 +125,7 @@ private static User randomUser(String username) { } else { metadata.put("string_list", Arrays.asList(generateRandomStringArray(4, 4, false, true))); } - return new User(username, roles, metadata, enabled, fullName, email); + return new User(username, roles, metadata, fullName, email); } private static PutUserRequest randomPutUserRequest(boolean enabled) { 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 8b0b8fd19efea..720963b9d0d0c 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 @@ -69,7 +69,7 @@ public void testPutUser() throws IOException { metadata.put(String.valueOf(i), randomAlphaOfLengthBetween(1, 12)); } } - final User user = new User(username, roles, metadata, true, fullName, email); + final User user = new User(username, roles, metadata, fullName, email); final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); final Map expectedParams = getExpectedParamsFromRefreshPolicy(refreshPolicy); 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 01d5e06443fef..4271255b7479b 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 @@ -889,7 +889,7 @@ 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'}; - User user = new User("change_password_user", Collections.singletonList("superuser"), Collections.emptyMap(), true, null, null); + User user = new User("change_password_user", Collections.singletonList("superuser"), Collections.emptyMap(), null, null); PutUserRequest putUserRequest = new PutUserRequest(user, password, true, RefreshPolicy.NONE); PutUserResponse putUserResponse = client.security().putUser(putUserRequest, RequestOptions.DEFAULT); assertTrue(putUserResponse.isCreated()); 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 388be7e41cba8..2dc8b37f20ca9 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 @@ -75,7 +75,7 @@ protected AuthenticateResponse createTestInstance() { final String lookupRealmName = randomAlphaOfLength(5); final String lookupRealmType = randomFrom("file", "native", "ldap", "active_directory", "saml", "kerberos"); return new AuthenticateResponse( - new User(username, roles, metadata, enabled, fullName, email), enabled, + new User(username, roles, metadata, fullName, email), enabled, new AuthenticateResponse.RealmInfo(authenticationRealmName, authenticationRealmType), new AuthenticateResponse.RealmInfo(lookupRealmName, lookupRealmType)); } @@ -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(), - response.enabled(), originalUser.getFullName(), originalUser.getEmail()); + originalUser.getFullName(), originalUser.getEmail()); return new AuthenticateResponse(copyUser, response.enabled(), response.getAuthenticationRealm(), response.getLookupRealm()); } @@ -118,40 +118,40 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { switch (randomIntBetween(1, 8)) { case 1: return new AuthenticateResponse(new User(originalUser.getUsername() + "wrong", originalUser.getRoles(), - originalUser.getMetadata(), response.enabled(), originalUser.getFullName(), originalUser.getEmail()), + originalUser.getMetadata(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(), response.getLookupRealm()); case 2: final Collection wrongRoles = new ArrayList<>(originalUser.getRoles()); wrongRoles.add(randomAlphaOfLengthBetween(1, 4)); return new AuthenticateResponse(new User(originalUser.getUsername(), wrongRoles, originalUser.getMetadata(), - response.enabled(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(), + originalUser.getFullName(), originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(), response.getLookupRealm()); case 3: final Map wrongMetadata = new HashMap<>(originalUser.getMetadata()); wrongMetadata.put("wrong_string", randomAlphaOfLengthBetween(0, 4)); return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), wrongMetadata, - response.enabled(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(), + originalUser.getFullName(), originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(), response.getLookupRealm()); case 4: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - response.enabled(), 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(), - response.enabled(), 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(), - response.enabled(), originalUser.getFullName(), originalUser.getEmail()), !response.enabled(), + originalUser.getFullName(), originalUser.getEmail()), !response.enabled(), response.getAuthenticationRealm(), response.getLookupRealm()); case 7: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - response.enabled(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(), + originalUser.getFullName(), originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(), new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5))); case 8: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - response.enabled(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(), + originalUser.getFullName(), originalUser.getEmail()), response.enabled(), new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)), response.getLookupRealm()); } 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 index 03b2d8b024472..427405b5fd211 100644 --- 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 @@ -30,8 +30,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import static org.hamcrest.Matchers.equalTo; @@ -63,57 +66,64 @@ public void usedDeprecatedField(String usedName, String replacedWith) { } }, json))); assertThat(response.getUsers().size(), equalTo(1)); - final User user = response.getUsers().get(0); + 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")); - assertTrue(user.getEnabled()); final Map metadata = new HashMap<>(); metadata.put("intelligence", 7); assertThat(metadata, equalTo(user.getMetadata())); } public void testEqualsHashCode() { - final List users = new ArrayList<>(); + 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, true, "Test User 1", null); + 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, true, "Test User 2", "testuser2@example.com"); + metadata, "Test User 2", "testuser2@example.com"); users.add(user2); - final GetUsersResponse getUsersResponse = new GetUsersResponse(users); + enabledUsers.add(user2); + final GetUsersResponse getUsersResponse = new GetUsersResponse(users, enabledUsers); assertNotNull(getUsersResponse); EqualsHashCodeTestUtils.checkEqualsAndHashCode(getUsersResponse, (original) -> { - return new GetUsersResponse(original.getUsers()); + return new GetUsersResponse(original.getUsers(), original.getEnabledUsers()); }); EqualsHashCodeTestUtils.checkEqualsAndHashCode(getUsersResponse, (original) -> { - return new GetUsersResponse(original.getUsers()); + return new GetUsersResponse(original.getUsers(), original.getEnabledUsers()); }, GetUsersResponseTests::mutateTestItem); } private static GetUsersResponse mutateTestItem(GetUsersResponse original) { if (randomBoolean()) { - final List users = new ArrayList<>(); + 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, true, "Test User 1", null); + metadata, "Test User 1", null); users.add(user1); - return new GetUsersResponse(users); + 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, true, "Test User 1", null); - List newUsers = original.getUsers().stream().collect(Collectors.toList()); - newUsers.remove(0); + 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); - return new GetUsersResponse(newUsers); + enabledUsers.add(user1); + return new GetUsersResponse(newUsers, enabledUsers); } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/PutUserRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/PutUserRequestTests.java index b01729eadf093..76d3b283b0d90 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/PutUserRequestTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/PutUserRequestTests.java @@ -37,7 +37,7 @@ public class PutUserRequestTests extends ESTestCase { public void testBuildRequestWithPassword() throws Exception { final User user = new User("hawkeye", Arrays.asList("kibana_user", "avengers"), - Collections.singletonMap("status", "active"), true, "Clinton Barton", null); + Collections.singletonMap("status", "active"), "Clinton Barton", null); final char[] password = "f@rmb0y".toCharArray(); final PutUserRequest request = PutUserRequest.withPassword(user, password, true, RefreshPolicy.IMMEDIATE); String json = Strings.toString(request); @@ -58,7 +58,7 @@ public void testBuildRequestWithPassword() throws Exception { public void testBuildRequestWithPasswordHash() throws Exception { final User user = new User("hawkeye", Arrays.asList("kibana_user", "avengers"), - Collections.singletonMap("status", "active"), true, "Clinton Barton", null); + Collections.singletonMap("status", "active"), "Clinton Barton", null); final char[] passwordHash = "$2a$04$iu1G4x3ZKVDNi6egZIjkFuIPja6elQXiBF1LdRVauV4TGog6FYOpi".toCharArray(); final PutUserRequest request = PutUserRequest.withPasswordHash(user, passwordHash, true, RefreshPolicy.IMMEDIATE); String json = Strings.toString(request); @@ -79,7 +79,7 @@ public void testBuildRequestWithPasswordHash() throws Exception { public void testBuildRequestForUpdateOnly() throws Exception { final User user = new User("hawkeye", Arrays.asList("kibana_user", "avengers"), - Collections.singletonMap("status", "active"), true, "Clinton Barton", null); + Collections.singletonMap("status", "active"), "Clinton Barton", null); final char[] passwordHash = "$2a$04$iu1G4x3ZKVDNi6egZIjkFuIPja6elQXiBF1LdRVauV4TGog6FYOpi".toCharArray(); final PutUserRequest request = PutUserRequest.updateUser(user, true, RefreshPolicy.IMMEDIATE); String json = Strings.toString(request); From ba9a291279b406b3b8858e2ede64f25b68e224bd Mon Sep 17 00:00:00 2001 From: Nicholas Knize Date: Tue, 11 Dec 2018 13:01:02 -0600 Subject: [PATCH 03/11] fix failing test --- .../src/test/java/org/elasticsearch/client/SecurityIT.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 ac13ba0593ace..fb75b0ff3ca52 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 @@ -32,6 +32,7 @@ import org.elasticsearch.client.security.user.User; import org.elasticsearch.common.CharArrays; +import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.HashMap; @@ -83,7 +84,9 @@ public void testAuthenticate() throws Exception { new GetUsersRequest(putUserRequest.getUser().getUsername()); final GetUsersResponse getUsersResponse = execute(getUsersRequest, securityClient::getUsers, securityClient::getUsersAsync); - assertThat(Arrays.asList(getUsersResponse.getUsers()).get(0), is(putUserRequest.getUser())); + ArrayList usrs = new ArrayList<>(); + usrs.addAll(getUsersResponse.getUsers()); + assertThat(usrs.get(0), is(putUserRequest.getUser())); // delete user final DeleteUserRequest deleteUserRequest = From e73c4a47e93154fbcad95129d9f5d116a07cb82c Mon Sep 17 00:00:00 2001 From: Nicholas Knize Date: Tue, 11 Dec 2018 13:10:10 -0600 Subject: [PATCH 04/11] remove unused imports --- .../java/org/elasticsearch/client/security/user/User.java | 5 ----- 1 file changed, 5 deletions(-) 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 f18452d77caef..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 @@ -20,9 +20,7 @@ package org.elasticsearch.client.security.user; import org.elasticsearch.common.Nullable; -import org.elasticsearch.common.ParseField; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.xcontent.ConstructingObjectParser; import java.util.Collection; import java.util.Collections; @@ -31,9 +29,6 @@ import java.util.Objects; import java.util.Set; -import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; - /** * 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. From 37007fdb7fce32fc7ffabdc2b2335d871e49b121 Mon Sep 17 00:00:00 2001 From: Nicholas Knize Date: Tue, 11 Dec 2018 13:11:03 -0600 Subject: [PATCH 05/11] revert AuthenticateResponseTests formatting changes --- .../security/AuthenticateResponseTests.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) 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 2dc8b37f20ca9..9fd82e0ae30a0 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,7 +117,7 @@ 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()); case 2: @@ -134,26 +134,26 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { response.getAuthenticationRealm(), response.getLookupRealm()); case 4: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - originalUser.getFullName() + "wrong", originalUser.getEmail()), response.enabled(), - response.getAuthenticationRealm(), response.getLookupRealm()); + 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(), - response.getAuthenticationRealm(), response.getLookupRealm()); + originalUser.getFullName(), originalUser.getEmail() + "wrong"), response.enabled(), + response.getAuthenticationRealm(), response.getLookupRealm()); case 6: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - originalUser.getFullName(), originalUser.getEmail()), !response.enabled(), - response.getAuthenticationRealm(), response.getLookupRealm()); + originalUser.getFullName(), originalUser.getEmail()), !response.enabled(), + response.getAuthenticationRealm(), response.getLookupRealm()); case 7: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - originalUser.getFullName(), originalUser.getEmail()), response.enabled(), + originalUser.getFullName(), originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(), new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5))); case 8: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - originalUser.getFullName(), originalUser.getEmail()), response.enabled(), - new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)), - response.getLookupRealm()); + originalUser.getFullName(), originalUser.getEmail()), response.enabled(), + new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)), + response.getLookupRealm()); } throw new IllegalStateException("Bad random number"); } From e9e4385b685d83121062737ed433f54cfafcf8bd Mon Sep 17 00:00:00 2001 From: Nicholas Knize Date: Tue, 11 Dec 2018 13:17:36 -0600 Subject: [PATCH 06/11] revert AuthenticateResponseTests formatting changes for real --- .../security/AuthenticateResponseTests.java | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) 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 9fd82e0ae30a0..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 @@ -118,20 +118,20 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { switch (randomIntBetween(1, 8)) { case 1: return new AuthenticateResponse(new User(originalUser.getUsername() + "wrong", originalUser.getRoles(), - originalUser.getMetadata(), originalUser.getFullName(), originalUser.getEmail()), - response.enabled(), response.getAuthenticationRealm(), response.getLookupRealm()); + originalUser.getMetadata(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(), + response.getAuthenticationRealm(), response.getLookupRealm()); case 2: final Collection wrongRoles = new ArrayList<>(originalUser.getRoles()); wrongRoles.add(randomAlphaOfLengthBetween(1, 4)); return new AuthenticateResponse(new User(originalUser.getUsername(), wrongRoles, originalUser.getMetadata(), - originalUser.getFullName(), originalUser.getEmail()), response.enabled(), - response.getAuthenticationRealm(), response.getLookupRealm()); + originalUser.getFullName(), originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(), + response.getLookupRealm()); case 3: final Map wrongMetadata = new HashMap<>(originalUser.getMetadata()); wrongMetadata.put("wrong_string", randomAlphaOfLengthBetween(0, 4)); return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), wrongMetadata, - originalUser.getFullName(), originalUser.getEmail()), response.enabled(), - response.getAuthenticationRealm(), response.getLookupRealm()); + originalUser.getFullName(), originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(), + response.getLookupRealm()); case 4: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), originalUser.getFullName() + "wrong", originalUser.getEmail()), response.enabled(), @@ -142,18 +142,16 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { response.getAuthenticationRealm(), response.getLookupRealm()); case 6: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - originalUser.getFullName(), originalUser.getEmail()), !response.enabled(), - response.getAuthenticationRealm(), response.getLookupRealm()); + originalUser.getFullName(), originalUser.getEmail()), !response.enabled(), response.getAuthenticationRealm(), + response.getLookupRealm()); case 7: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - originalUser.getFullName(), originalUser.getEmail()), response.enabled(), - response.getAuthenticationRealm(), new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), - randomAlphaOfLength(5))); + originalUser.getFullName(), originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(), + new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5))); case 8: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(), - new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)), - response.getLookupRealm()); + new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)), response.getLookupRealm()); } throw new IllegalStateException("Bad random number"); } From 70c2b3ff22c34a0a1a74e2e2e422170140a8f8ef Mon Sep 17 00:00:00 2001 From: Nicholas Knize Date: Tue, 11 Dec 2018 14:54:52 -0600 Subject: [PATCH 07/11] remove xpack from endpoint --- .../org/elasticsearch/client/SecurityRequestConverters.java | 2 +- .../elasticsearch/client/SecurityRequestConvertersTests.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 5780a7e01f542..64c09fe7e3272 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 @@ -70,7 +70,7 @@ static Request changePassword(ChangePasswordRequest changePasswordRequest) throw static Request getUsers(GetUsersRequest getUsersRequest) { RequestConverters.EndpointBuilder builder = new RequestConverters.EndpointBuilder() - .addPathPartAsIs("_xpack/security/user"); + .addPathPartAsIs("/_security/user"); if (getUsersRequest.getUsernames().size() > 0) { builder.addPathPart(Strings.collectionToCommaDelimitedString(getUsersRequest.getUsernames())); } 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 fb896c2de5977..b4ed64c3651c8 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 @@ -109,7 +109,7 @@ public void testGetUsers() { final Map expectedParams = getExpectedParamsFromRefreshPolicy(refreshPolicy); Request request = SecurityRequestConverters.getUsers(getUsersRequest); assertEquals(HttpGet.METHOD_NAME, request.getMethod()); - assertEquals("/_xpack/security/user/test", request.getEndpoint()); + assertEquals("/_security/user/test", request.getEndpoint()); assertEquals(expectedParams, request.getParameters()); assertNull(request.getEntity()); From 5550a149635e07c4acd1fd439e6bf13babcb3ddd Mon Sep 17 00:00:00 2001 From: Nicholas Knize Date: Tue, 11 Dec 2018 18:56:26 -0600 Subject: [PATCH 08/11] fix checkStyle failure --- .../org/elasticsearch/client/security/GetUsersResponse.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index 15f01e63d39f5..107b93afe7ce4 100644 --- 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 @@ -120,8 +120,8 @@ 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) { + 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."))); From 783fee2953121dc5bc6009015fc4e9342ce47a12 Mon Sep 17 00:00:00 2001 From: Nicholas Knize Date: Wed, 12 Dec 2018 13:49:24 -0600 Subject: [PATCH 09/11] remove unused imports --- .../src/test/java/org/elasticsearch/client/SecurityIT.java | 1 - .../elasticsearch/client/security/GetUsersResponseTests.java | 3 --- 2 files changed, 4 deletions(-) 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 7bade4f8e2326..8d08e78590eab 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 @@ -52,7 +52,6 @@ import java.util.Locale; import java.util.Map; -import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.contains; 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 index 427405b5fd211..3025241bb3909 100644 --- 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 @@ -26,13 +26,10 @@ import org.elasticsearch.test.EqualsHashCodeTestUtils; import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; From 8db32fc93fae0d9b1e45c1efdf0667d89e208bc5 Mon Sep 17 00:00:00 2001 From: Nicholas Knize Date: Thu, 13 Dec 2018 10:30:43 -0600 Subject: [PATCH 10/11] cleanup tests, fix docs --- .../client/SecurityRequestConverters.java | 2 +- .../org/elasticsearch/client/SecurityIT.java | 22 +++++++-- .../SecurityRequestConvertersTests.java | 14 +++--- .../high-level/security/get-users.asciidoc | 48 +++++++++++++++++++ .../high-level/supported-apis.asciidoc | 2 + 5 files changed, 78 insertions(+), 10 deletions(-) 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/SecurityRequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityRequestConverters.java index 64c09fe7e3272..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 @@ -70,7 +70,7 @@ static Request changePassword(ChangePasswordRequest changePasswordRequest) throw static Request getUsers(GetUsersRequest getUsersRequest) { RequestConverters.EndpointBuilder builder = new RequestConverters.EndpointBuilder() - .addPathPartAsIs("/_security/user"); + .addPathPartAsIs("_security/user"); if (getUsersRequest.getUsernames().size() > 0) { builder.addPathPart(Strings.collectionToCommaDelimitedString(getUsersRequest.getUsernames())); } 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 8d08e78590eab..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 @@ -77,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 @@ -97,9 +113,9 @@ public void testAuthenticate() throws Exception { new GetUsersRequest(putUserRequest.getUser().getUsername()); final GetUsersResponse getUsersResponse = execute(getUsersRequest, securityClient::getUsers, securityClient::getUsersAsync); - ArrayList usrs = new ArrayList<>(); - usrs.addAll(getUsersResponse.getUsers()); - assertThat(usrs.get(0), is(putUserRequest.getUser())); + ArrayList users = new ArrayList<>(); + users.addAll(getUsersResponse.getUsers()); + assertThat(users.get(0), is(putUserRequest.getUser())); // delete user final DeleteUserRequest deleteUserRequest = 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 b4ed64c3651c8..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 @@ -103,16 +103,18 @@ public void testDeleteUser() { } public void testGetUsers() { - final String[] users = new String[] {"test"}; + final String[] users = randomArray(0, 5, String[]::new, () -> randomAlphaOfLength(5)); GetUsersRequest getUsersRequest = new GetUsersRequest(users); - final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); - final Map expectedParams = getExpectedParamsFromRefreshPolicy(refreshPolicy); Request request = SecurityRequestConverters.getUsers(getUsersRequest); assertEquals(HttpGet.METHOD_NAME, request.getMethod()); - assertEquals("/_security/user/test", request.getEndpoint()); - assertEquals(expectedParams, request.getParameters()); + 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 { 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[] From 6228afc408fb33c4f8b1194dfc25c993bfe14660 Mon Sep 17 00:00:00 2001 From: Nicholas Knize Date: Thu, 13 Dec 2018 10:48:59 -0600 Subject: [PATCH 11/11] finish out SecurityDocumentationIT#testGetUsers --- .../SecurityDocumentationIT.java | 89 +++++++++++++++++-- 1 file changed, 84 insertions(+), 5 deletions(-) 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 b1782e14eda66..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 @@ -112,16 +112,95 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase { public void testGetUsers() throws Exception { - RestHighLevelClient client = highLevelClient(); - addUser(client, "testUser", "testPassword"); + 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])); + } { - GetUsersRequest getUsersRequest = new GetUsersRequest(); - GetUsersResponse getUsersResponse = client.security().getUsers(getUsersRequest, RequestOptions.DEFAULT); - assertNotNull(getUsersResponse.getUsers()); + //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();