diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java index d02c15b59706f..48a1cdb778243 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java @@ -50,6 +50,8 @@ import org.elasticsearch.client.security.GetSslCertificatesResponse; import org.elasticsearch.client.security.GetUserPrivilegesRequest; import org.elasticsearch.client.security.GetUserPrivilegesResponse; +import org.elasticsearch.client.security.GetUsersRequest; +import org.elasticsearch.client.security.GetUsersResponse; import org.elasticsearch.client.security.HasPrivilegesRequest; import org.elasticsearch.client.security.HasPrivilegesResponse; import org.elasticsearch.client.security.InvalidateTokenRequest; @@ -81,6 +83,33 @@ public final class SecurityClient { this.restHighLevelClient = restHighLevelClient; } + /** + * Get a user, or list of users, in the native realm synchronously. + * See + * the docs for more information. + * @param request the request with the user's name + * @param options the request options (e.g., headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response from the get users call + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public GetUsersResponse getUsers(GetUsersRequest request, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::getUsers, options, + GetUsersResponse::fromXContent, emptySet()); + } + + /** + * Get a user, or list of users, in the native realm asynchronously. + * See + * the docs for more information. + * @param request the request with the user's name + * @param options the request options (e.g., headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + */ + public void getUsersAsync(GetUsersRequest request, RequestOptions options, ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::getUsers, options, + GetUsersResponse::fromXContent, listener, emptySet()); + } + /** * Create/update a user in the native realm synchronously. * See diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityRequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityRequestConverters.java index aa09b9596a83f..9e9698ded1cd8 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityRequestConverters.java @@ -36,6 +36,7 @@ import org.elasticsearch.client.security.GetPrivilegesRequest; import org.elasticsearch.client.security.GetRoleMappingsRequest; import org.elasticsearch.client.security.GetRolesRequest; +import org.elasticsearch.client.security.GetUsersRequest; import org.elasticsearch.client.security.HasPrivilegesRequest; import org.elasticsearch.client.security.InvalidateTokenRequest; import org.elasticsearch.client.security.PutPrivilegesRequest; @@ -67,6 +68,15 @@ static Request changePassword(ChangePasswordRequest changePasswordRequest) throw return request; } + static Request getUsers(GetUsersRequest getUsersRequest) { + RequestConverters.EndpointBuilder builder = new RequestConverters.EndpointBuilder() + .addPathPartAsIs("_security/user"); + if (getUsersRequest.getUsernames().size() > 0) { + builder.addPathPart(Strings.collectionToCommaDelimitedString(getUsersRequest.getUsernames())); + } + return new Request(HttpGet.METHOD_NAME, builder.build()); + } + static Request putUser(PutUserRequest putUserRequest) throws IOException { String endpoint = new RequestConverters.EndpointBuilder() .addPathPartAsIs("_security/user") diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetUsersRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetUsersRequest.java new file mode 100644 index 0000000000000..0a6b5e9bb2578 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetUsersRequest.java @@ -0,0 +1,58 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.client.security; + +import org.elasticsearch.client.Validatable; +import org.elasticsearch.common.util.set.Sets; + +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +/** + * Request object to retrieve users from the native realm + */ +public class GetUsersRequest implements Validatable { + private final Set usernames; + + public GetUsersRequest(final String... usernames) { + if (usernames != null) { + this.usernames = Collections.unmodifiableSet(Sets.newHashSet(usernames)); + } else { + this.usernames = Collections.emptySet(); + } + } + + public Set getUsernames() { + return usernames; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof GetUsersRequest)) return false; + GetUsersRequest that = (GetUsersRequest) o; + return Objects.equals(usernames, that.usernames); + } + + @Override + public int hashCode() { + return Objects.hash(usernames); + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetUsersResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetUsersResponse.java new file mode 100644 index 0000000000000..107b93afe7ce4 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetUsersResponse.java @@ -0,0 +1,134 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.client.security; + +import org.elasticsearch.client.security.user.User; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParser.Token; +import org.elasticsearch.common.xcontent.XContentParserUtils; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; + +/** + * Response when requesting zero or more users. + * Returns a List of {@link User} objects + */ +public class GetUsersResponse { + private final Set users; + private final Set enabledUsers; + + public GetUsersResponse(Set users, Set enabledUsers) { + this.users = Collections.unmodifiableSet(users); + this.enabledUsers = Collections.unmodifiableSet(enabledUsers); + } + + public Set getUsers() { + return users; + } + + public Set getEnabledUsers() { + return enabledUsers; + } + + public static GetUsersResponse fromXContent(XContentParser parser) throws IOException { + XContentParserUtils.ensureExpectedToken(Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + final Set users = new HashSet<>(); + final Set enabledUsers = new HashSet<>(); + Token token; + while ((token = parser.nextToken()) != Token.END_OBJECT) { + XContentParserUtils.ensureExpectedToken(Token.FIELD_NAME, token, parser::getTokenLocation); + ParsedUser parsedUser = USER_PARSER.parse(parser, parser.currentName()); + users.add(parsedUser.user); + if (parsedUser.enabled) { + enabledUsers.add(parsedUser.user); + } + } + return new GetUsersResponse(users, enabledUsers); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof GetUsersResponse)) return false; + GetUsersResponse that = (GetUsersResponse) o; + return Objects.equals(users, that.users); + } + + @Override + public int hashCode() { + return Objects.hash(users); + } + + public static final ParseField USERNAME = new ParseField("username"); + public static final ParseField ROLES = new ParseField("roles"); + public static final ParseField FULL_NAME = new ParseField("full_name"); + public static final ParseField EMAIL = new ParseField("email"); + public static final ParseField METADATA = new ParseField("metadata"); + public static final ParseField ENABLED = new ParseField("enabled"); + + @SuppressWarnings("unchecked") + public static final ConstructingObjectParser USER_PARSER = new ConstructingObjectParser<>("user_info", + (constructorObjects) -> { + int i = 0; + final String username = (String) constructorObjects[i++]; + final Collection roles = (Collection) constructorObjects[i++]; + final Map metadata = (Map) constructorObjects[i++]; + final Boolean enabled = (Boolean) constructorObjects[i++]; + final String fullName = (String) constructorObjects[i++]; + final String email = (String) constructorObjects[i++]; + return new ParsedUser(username, roles, metadata, enabled, fullName, email); + }); + + static { + USER_PARSER.declareString(constructorArg(), USERNAME); + USER_PARSER.declareStringArray(constructorArg(), ROLES); + USER_PARSER.declareObject(constructorArg(), (parser, c) -> parser.map(), METADATA); + USER_PARSER.declareBoolean(constructorArg(), ENABLED); + USER_PARSER.declareStringOrNull(optionalConstructorArg(), FULL_NAME); + USER_PARSER.declareStringOrNull(optionalConstructorArg(), EMAIL); + } + + protected static final class ParsedUser { + protected User user; + protected boolean enabled; + + public ParsedUser(String username, Collection roles, Map metadata, Boolean enabled, + @Nullable String fullName, @Nullable String email) { + String checkedUsername = username = Objects.requireNonNull(username, "`username` is required, cannot be null"); + Collection checkedRoles = Collections.unmodifiableSet(new HashSet<>( + Objects.requireNonNull(roles, "`roles` is required, cannot be null. Pass an empty Collection instead."))); + Map checkedMetadata = Collections + .unmodifiableMap(Objects.requireNonNull(metadata, "`metadata` is required, cannot be null. Pass an empty map instead.")); + this.user = new User(checkedUsername, checkedRoles, checkedMetadata, fullName, email); + this.enabled = enabled; + } + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/User.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/User.java index ba6cd5f2f8ef5..4ac8f54c4741b 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/User.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/User.java @@ -29,7 +29,6 @@ import java.util.Objects; import java.util.Set; - /** * A user to be utilized with security APIs. * Can be an existing authenticated user or it can be a new user to be enrolled to the native realm. diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java index 0ccd1aabeba55..c6dc6910d97b0 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/user/privileges/Role.java @@ -315,9 +315,11 @@ public static class ClusterPrivilegeName { public static final String MANAGE_PIPELINE = "manage_pipeline"; public static final String MANAGE_CCR = "manage_ccr"; public static final String READ_CCR = "read_ccr"; + public static final String MANAGE_ILM = "manage_ilm"; + public static final String READ_ILM = "read_ilm"; public static final String[] ALL_ARRAY = new String[] { NONE, ALL, MONITOR, MONITOR_ML, MONITOR_WATCHER, MONITOR_ROLLUP, MANAGE, MANAGE_ML, MANAGE_WATCHER, MANAGE_ROLLUP, MANAGE_INDEX_TEMPLATES, MANAGE_INGEST_PIPELINES, TRANSPORT_CLIENT, - MANAGE_SECURITY, MANAGE_SAML, MANAGE_TOKEN, MANAGE_PIPELINE, MANAGE_CCR, READ_CCR }; + MANAGE_SECURITY, MANAGE_SAML, MANAGE_TOKEN, MANAGE_PIPELINE, MANAGE_CCR, READ_CCR, MANAGE_ILM, READ_ILM }; } /** @@ -338,8 +340,9 @@ public static class IndexPrivilegeName { public static final String CREATE_INDEX = "create_index"; public static final String VIEW_INDEX_METADATA = "view_index_metadata"; public static final String MANAGE_FOLLOW_INDEX = "manage_follow_index"; + public static final String MANAGE_ILM = "manage_ilm"; public static final String[] ALL_ARRAY = new String[] { NONE, ALL, READ, READ_CROSS, CREATE, INDEX, DELETE, WRITE, MONITOR, MANAGE, - DELETE_INDEX, CREATE_INDEX, VIEW_INDEX_METADATA, MANAGE_FOLLOW_INDEX }; + DELETE_INDEX, CREATE_INDEX, VIEW_INDEX_METADATA, MANAGE_FOLLOW_INDEX, MANAGE_ILM }; } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityIT.java index 05a854299a6bb..abf65d19df3b7 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityIT.java @@ -28,6 +28,8 @@ import org.elasticsearch.client.security.DeleteUserResponse; import org.elasticsearch.client.security.GetRolesRequest; import org.elasticsearch.client.security.GetRolesResponse; +import org.elasticsearch.client.security.GetUsersRequest; +import org.elasticsearch.client.security.GetUsersResponse; import org.elasticsearch.client.security.PutRoleRequest; import org.elasticsearch.client.security.PutRoleResponse; import org.elasticsearch.client.security.PutUserRequest; @@ -42,6 +44,7 @@ import org.elasticsearch.client.security.user.privileges.Role; import org.elasticsearch.common.CharArrays; +import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.HashMap; @@ -74,6 +77,22 @@ public void testPutUser() throws Exception { highLevelClient().getLowLevelClient().performRequest(deleteUserRequest); } + public void testGetUser() throws Exception { + final SecurityClient securityClient = highLevelClient().security(); + // create user + final PutUserRequest putUserRequest = randomPutUserRequest(randomBoolean()); + final PutUserResponse putUserResponse = execute(putUserRequest, securityClient::putUser, securityClient::putUserAsync); + // assert user created + assertThat(putUserResponse.isCreated(), is(true)); + // get user + final GetUsersRequest getUsersRequest = new GetUsersRequest(putUserRequest.getUser().getUsername()); + final GetUsersResponse getUsersResponse = execute(getUsersRequest, securityClient::getUsers, securityClient::getUsersAsync); + // assert user was correctly retrieved + ArrayList users = new ArrayList<>(); + users.addAll(getUsersResponse.getUsers()); + assertThat(users.get(0), is(putUserRequest.getUser())); + } + public void testAuthenticate() throws Exception { final SecurityClient securityClient = highLevelClient().security(); // test fixture: put enabled user @@ -89,6 +108,15 @@ public void testAuthenticate() throws Exception { assertThat(authenticateResponse.getUser(), is(putUserRequest.getUser())); assertThat(authenticateResponse.enabled(), is(true)); + // get user + final GetUsersRequest getUsersRequest = + new GetUsersRequest(putUserRequest.getUser().getUsername()); + final GetUsersResponse getUsersResponse = + execute(getUsersRequest, securityClient::getUsers, securityClient::getUsersAsync); + ArrayList users = new ArrayList<>(); + users.addAll(getUsersResponse.getUsers()); + assertThat(users.get(0), is(putUserRequest.getUser())); + // delete user final DeleteUserRequest deleteUserRequest = new DeleteUserRequest(putUserRequest.getUser().getUsername(), putUserRequest.getRefreshPolicy()); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java index 87c692d9f2a3b..900f4210a9952 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java @@ -34,6 +34,7 @@ import org.elasticsearch.client.security.GetPrivilegesRequest; import org.elasticsearch.client.security.GetRoleMappingsRequest; import org.elasticsearch.client.security.GetRolesRequest; +import org.elasticsearch.client.security.GetUsersRequest; import org.elasticsearch.client.security.PutPrivilegesRequest; import org.elasticsearch.client.security.PutRoleMappingRequest; import org.elasticsearch.client.security.PutRoleRequest; @@ -101,6 +102,21 @@ public void testDeleteUser() { assertNull(request.getEntity()); } + public void testGetUsers() { + final String[] users = randomArray(0, 5, String[]::new, () -> randomAlphaOfLength(5)); + GetUsersRequest getUsersRequest = new GetUsersRequest(users); + Request request = SecurityRequestConverters.getUsers(getUsersRequest); + assertEquals(HttpGet.METHOD_NAME, request.getMethod()); + if (users.length == 0) { + assertEquals("/_security/user", request.getEndpoint()); + } else { + assertEquals("/_security/user/" + Strings.collectionToCommaDelimitedString(getUsersRequest.getUsernames()), + request.getEndpoint()); + } + assertNull(request.getEntity()); + assertEquals(Collections.emptyMap(), request.getParameters()); + } + public void testPutRoleMapping() throws IOException { final String username = randomAlphaOfLengthBetween(4, 7); final String rolename = randomAlphaOfLengthBetween(4, 7); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java index 7d0438238e50c..c225685ad646e 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java @@ -54,6 +54,8 @@ import org.elasticsearch.client.security.GetRolesResponse; import org.elasticsearch.client.security.GetSslCertificatesResponse; import org.elasticsearch.client.security.GetUserPrivilegesResponse; +import org.elasticsearch.client.security.GetUsersRequest; +import org.elasticsearch.client.security.GetUsersResponse; import org.elasticsearch.client.security.HasPrivilegesRequest; import org.elasticsearch.client.security.HasPrivilegesResponse; import org.elasticsearch.client.security.InvalidateTokenRequest; @@ -109,6 +111,96 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase { + public void testGetUsers() throws Exception { + final RestHighLevelClient client = highLevelClient(); + String[] usernames = new String[] {"user1", "user2", "user3"}; + addUser(client, usernames[0], randomAlphaOfLength(4)); + addUser(client, usernames[1], randomAlphaOfLength(4)); + addUser(client, usernames[2], randomAlphaOfLength(4)); + { + //tag::get-users-request + GetUsersRequest request = new GetUsersRequest(usernames[0]); + //end::get-users-request + //tag::get-users-execute + GetUsersResponse response = client.security().getUsers(request, RequestOptions.DEFAULT); + //end::get-users-execute + //tag::get-users-response + List users = new ArrayList<>(1); + users.addAll(response.getUsers()); + //end::get-users-response + + assertNotNull(response); + assertThat(users.size(), equalTo(1)); + assertThat(users.get(0), is(usernames[0])); + } + + { + //tag::get-users-list-request + GetUsersRequest request = new GetUsersRequest(usernames); + GetUsersResponse response = client.security().getUsers(request, RequestOptions.DEFAULT); + //end::get-users-list-request + + List users = new ArrayList<>(3); + users.addAll(response.getUsers()); + assertNotNull(response); + assertThat(users.size(), equalTo(3)); + assertThat(users.get(0).getUsername(), equalTo(usernames[0])); + assertThat(users.get(1).getUsername(), equalTo(usernames[1])); + assertThat(users.get(2).getUsername(), equalTo(usernames[2])); + assertThat(users.size(), equalTo(3)); + } + + { + //tag::get-users-all-request + GetUsersRequest request = new GetUsersRequest(); + GetUsersResponse response = client.security().getUsers(request, RequestOptions.DEFAULT); + //end::get-users-all-request + + List users = new ArrayList<>(3); + users.addAll(response.getUsers()); + assertNotNull(response); + // 4 system users plus the three we created + assertThat(users.size(), equalTo(7)); + } + + { + GetUsersRequest request = new GetUsersRequest(usernames[0]); + ActionListener listener; + + //tag::get-roles-execute-listener + listener = new ActionListener() { + @Override + public void onResponse(GetUsersResponse getRolesResponse) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + //end::get-users-execute-listener + + assertNotNull(listener); + + // Replace the empty listener by a blocking listener in test + final PlainActionFuture future = new PlainActionFuture<>(); + listener = future; + + //tag::get-users-execute-async + client.security().getUsersAsync(request, RequestOptions.DEFAULT, listener); // <1> + //end::get-users-execute-async + + final GetUsersResponse response = future.get(30, TimeUnit.SECONDS); + List users = new ArrayList<>(1); + users.addAll(response.getUsers()); + assertNotNull(response); + assertThat(users.size(), equalTo(1)); + assertThat(users.get(0).getUsername(), equalTo(usernames[0])); + } + } + + public void testPutUser() throws Exception { RestHighLevelClient client = highLevelClient(); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/AuthenticateResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/AuthenticateResponseTests.java index f09340fa09ffd..f59038af55af7 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/AuthenticateResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/AuthenticateResponseTests.java @@ -38,12 +38,12 @@ public class AuthenticateResponseTests extends ESTestCase { public void testFromXContent() throws IOException { xContentTester( - this::createParser, - this::createTestInstance, - this::toXContent, - AuthenticateResponse::fromXContent) - .supportsUnknownFields(false) - .test(); + this::createParser, + this::createTestInstance, + this::toXContent, + AuthenticateResponse::fromXContent) + .supportsUnknownFields(false) + .test(); } public void testEqualsAndHashCode() { @@ -108,7 +108,7 @@ private void toXContent(AuthenticateResponse response, XContentBuilder builder) private AuthenticateResponse copy(AuthenticateResponse response) { final User originalUser = response.getUser(); final User copyUser = new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - originalUser.getFullName(), originalUser.getEmail()); + originalUser.getFullName(), originalUser.getEmail()); return new AuthenticateResponse(copyUser, response.enabled(), response.getAuthenticationRealm(), response.getLookupRealm()); } @@ -117,9 +117,9 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { final User originalUser = response.getUser(); switch (randomIntBetween(1, 8)) { case 1: - return new AuthenticateResponse(new User(originalUser.getUsername() + "wrong", originalUser.getRoles(), + return new AuthenticateResponse(new User(originalUser.getUsername() + "wrong", originalUser.getRoles(), originalUser.getMetadata(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(), - response.getAuthenticationRealm(), response.getLookupRealm()); + response.getAuthenticationRealm(), response.getLookupRealm()); case 2: final Collection wrongRoles = new ArrayList<>(originalUser.getRoles()); wrongRoles.add(randomAlphaOfLengthBetween(1, 4)); @@ -134,11 +134,11 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { response.getLookupRealm()); case 4: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - originalUser.getFullName() + "wrong", originalUser.getEmail()), response.enabled(), + originalUser.getFullName() + "wrong", originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(), response.getLookupRealm()); case 5: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), - originalUser.getFullName(), originalUser.getEmail() + "wrong"), response.enabled(), + originalUser.getFullName(), originalUser.getEmail() + "wrong"), response.enabled(), response.getAuthenticationRealm(), response.getLookupRealm()); case 6: return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(), diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetUsersRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetUsersRequestTests.java new file mode 100644 index 0000000000000..68b1751716e1f --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetUsersRequestTests.java @@ -0,0 +1,53 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.client.security; + +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.EqualsHashCodeTestUtils; + +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.equalTo; + +public class GetUsersRequestTests extends ESTestCase { + + public void testGetUsersRequest() { + final String[] users = randomArray(0, 5, String[]::new, () -> randomAlphaOfLength(5)); + GetUsersRequest getUsersRequest = new GetUsersRequest(users); + assertThat(getUsersRequest.getUsernames().size(), equalTo(users.length)); + assertThat(getUsersRequest.getUsernames(), containsInAnyOrder(users)); + } + + public void testEqualsHashCode() { + final String[] users = randomArray(0, 5, String[]::new, () -> randomAlphaOfLength(5)); + final GetUsersRequest getUsersRequest = new GetUsersRequest(users); + assertNotNull(getUsersRequest); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(getUsersRequest, (original) -> { + return new GetUsersRequest(original.getUsernames().toArray(new String[0])); + }); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(getUsersRequest, (original) -> { + return new GetUsersRequest(original.getUsernames().toArray(new String[0])); + }, GetUsersRequestTests::mutateTestItem); + } + + private static GetUsersRequest mutateTestItem(GetUsersRequest original) { + final int minRoles = original.getUsernames().isEmpty() ? 1 : 0; + return new GetUsersRequest(randomArray(minRoles, 5, String[]::new, () -> randomAlphaOfLength(6))); + } + +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetUsersResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetUsersResponseTests.java new file mode 100644 index 0000000000000..3025241bb3909 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetUsersResponseTests.java @@ -0,0 +1,126 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.client.security; + +import org.elasticsearch.client.security.user.User; +import org.elasticsearch.common.xcontent.DeprecationHandler; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.EqualsHashCodeTestUtils; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.hamcrest.Matchers.equalTo; + +/** tests the Response for getting users from the security HLRC */ +public class GetUsersResponseTests extends ESTestCase { + public void testFromXContent() throws IOException { + String json = + "{\n" + + " \"jacknich\": {\n" + + " \"username\": \"jacknich\",\n" + + " \"roles\": [\n" + + " \"admin\", \"other_role1\"\n" + + " ],\n" + + " \"full_name\": \"Jack Nicholson\",\n" + + " \"email\": \"jacknich@example.com\",\n" + + " \"metadata\": { \"intelligence\" : 7 },\n" + + " \"enabled\": true\n" + + " }\n" + + "}"; + final GetUsersResponse response = GetUsersResponse.fromXContent((XContentType.JSON.xContent().createParser( + new NamedXContentRegistry(Collections.emptyList()), new DeprecationHandler() { + @Override + public void usedDeprecatedName(String usedName, String modernName) { + } + + @Override + public void usedDeprecatedField(String usedName, String replacedWith) { + } + }, json))); + assertThat(response.getUsers().size(), equalTo(1)); + final User user = response.getUsers().iterator().next(); + assertThat(user.getUsername(), equalTo("jacknich")); + assertThat(user.getRoles().size(), equalTo(2)); + assertThat(user.getFullName(), equalTo("Jack Nicholson")); + assertThat(user.getEmail(), equalTo("jacknich@example.com")); + final Map metadata = new HashMap<>(); + metadata.put("intelligence", 7); + assertThat(metadata, equalTo(user.getMetadata())); + } + + public void testEqualsHashCode() { + final Set users = new HashSet<>(); + final Set enabledUsers = new HashSet<>(); + Map metadata = new HashMap<>(); + metadata.put("intelligence", 1); + final User user1 = new User("testUser1", Arrays.asList(new String[] {"admin", "other_role1"}), + metadata, "Test User 1", null); + users.add(user1); + enabledUsers.add(user1); + Map metadata2 = new HashMap<>(); + metadata2.put("intelligence", 9); + metadata2.put("specialty", "geo"); + final User user2 = new User("testUser2", Arrays.asList(new String[] {"admin"}), + metadata, "Test User 2", "testuser2@example.com"); + users.add(user2); + enabledUsers.add(user2); + final GetUsersResponse getUsersResponse = new GetUsersResponse(users, enabledUsers); + assertNotNull(getUsersResponse); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(getUsersResponse, (original) -> { + return new GetUsersResponse(original.getUsers(), original.getEnabledUsers()); + }); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(getUsersResponse, (original) -> { + return new GetUsersResponse(original.getUsers(), original.getEnabledUsers()); + }, GetUsersResponseTests::mutateTestItem); + } + + private static GetUsersResponse mutateTestItem(GetUsersResponse original) { + if (randomBoolean()) { + final Set users = new HashSet<>(); + final Set enabledUsers = new HashSet<>(); + Map metadata = new HashMap<>(); + metadata.put("intelligence", 1); + final User user1 = new User("testUser1", Arrays.asList(new String[] {"admin", "other_role1"}), + metadata, "Test User 1", null); + users.add(user1); + enabledUsers.add(user1); + return new GetUsersResponse(users, enabledUsers); + } + Map metadata = new HashMap<>(); + metadata.put("intelligence", 5); // change intelligence + final User user1 = new User("testUser1", Arrays.asList(new String[] {"admin", "other_role1"}), + metadata, "Test User 1", null); + Set newUsers = original.getUsers().stream().collect(Collectors.toSet()); + Set enabledUsers = original.getEnabledUsers().stream().collect(Collectors.toSet()); + newUsers.clear(); + enabledUsers.clear(); + newUsers.add(user1); + enabledUsers.add(user1); + return new GetUsersResponse(newUsers, enabledUsers); + } +} diff --git a/docs/java-rest/high-level/security/get-users.asciidoc b/docs/java-rest/high-level/security/get-users.asciidoc new file mode 100644 index 0000000000000..e9e4a0d94911b --- /dev/null +++ b/docs/java-rest/high-level/security/get-users.asciidoc @@ -0,0 +1,48 @@ + +-- +:api: get-users +:request: GetUsersRequest +:respnse: GetUsersResponse +-- + +[id="{upid}-{api}"] +=== Get Users API + +[id="{upid}-{api}-request"] +==== Get Users Request + +Retrieving a user can be performed using the `security().getUsers()` +method and by setting the username on +{request}+: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-request] +-------------------------------------------------- + +Retrieving multiple users can be performed using the `security().getUsers()` +method and by setting multiple usernames on +{request}+: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-list-request] +-------------------------------------------------- + +Retrieving all users can be performed using the `security().getUsers()` +method without specifying any usernames on +{request}+: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-all-request] +-------------------------------------------------- + +include::../execution.asciidoc[] + +[id="{upid}-{api}-response"] +==== Get Users Response + +The returned +{response}+ allows getting information about the retrieved users as follows. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-response] +-------------------------------------------------- \ No newline at end of file diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index 70b66074aadba..0b4a2570c896d 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -387,6 +387,7 @@ include::rollup/get_rollup_index_caps.asciidoc[] The Java High Level REST Client supports the following Security APIs: * <> +* <<{upid}-get-users>> * <<{upid}-delete-user>> * <> * <> @@ -410,6 +411,7 @@ The Java High Level REST Client supports the following Security APIs: * <<{upid}-delete-privileges>> include::security/put-user.asciidoc[] +include::security/get-users.asciidoc[] include::security/delete-user.asciidoc[] include::security/enable-user.asciidoc[] include::security/disable-user.asciidoc[] diff --git a/server/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java index 996e878dba85c..d8476d791d7ec 100644 --- a/server/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java @@ -67,7 +67,6 @@ public class MultiMatchQueryBuilder extends AbstractQueryBuilderTie-Breaker for "best-match" disjunction queries (OR-Queries). * The tie breaker capability allows documents that match more than one query clause @@ -593,9 +581,6 @@ public void doXContent(XContentBuilder builder, Params params) throws IOExceptio if (fuzzyRewrite != null) { builder.field(FUZZY_REWRITE_FIELD.getPreferredName(), fuzzyRewrite); } - if (useDisMax != null) { - builder.field(USE_DIS_MAX_FIELD.getPreferredName(), useDisMax); - } if (tieBreaker != null) { builder.field(TIE_BREAKER_FIELD.getPreferredName(), tieBreaker); } @@ -674,8 +659,6 @@ public static MultiMatchQueryBuilder fromXContent(XContentParser parser) throws minimumShouldMatch = parser.textOrNull(); } else if (FUZZY_REWRITE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { fuzzyRewrite = parser.textOrNull(); - } else if (USE_DIS_MAX_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { - useDisMax = parser.booleanValue(); } else if (TIE_BREAKER_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { tieBreaker = parser.floatValue(); } else if (CUTOFF_FREQUENCY_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { @@ -724,7 +707,6 @@ public static MultiMatchQueryBuilder fromXContent(XContentParser parser) throws .cutoffFrequency(cutoffFrequency) .fuzziness(fuzziness) .fuzzyRewrite(fuzzyRewrite) - .useDisMax(useDisMax) .maxExpansions(maxExpansions) .minimumShouldMatch(minimumShouldMatch) .operator(operator) @@ -798,16 +780,6 @@ protected Query doToQuery(QueryShardContext context) throws IOException { multiMatchQuery.setAutoGenerateSynonymsPhraseQuery(autoGenerateSynonymsPhraseQuery); multiMatchQuery.setTranspositions(fuzzyTranspositions); - if (useDisMax != null) { // backwards foobar - boolean typeUsesDismax = type.tieBreaker() != 1.0f; - if (typeUsesDismax != useDisMax) { - if (useDisMax && tieBreaker == null) { - multiMatchQuery.setTieBreaker(0.0f); - } else { - multiMatchQuery.setTieBreaker(1.0f); - } - } - } Map newFieldsBoosts; if (fieldsBoosts.isEmpty()) { // no fields provided, defaults to index.query.default_field @@ -828,7 +800,7 @@ protected Query doToQuery(QueryShardContext context) throws IOException { @Override protected int doHashCode() { return Objects.hash(value, fieldsBoosts, type, operator, analyzer, slop, fuzziness, - prefixLength, maxExpansions, minimumShouldMatch, fuzzyRewrite, useDisMax, tieBreaker, lenient, + prefixLength, maxExpansions, minimumShouldMatch, fuzzyRewrite, tieBreaker, lenient, cutoffFrequency, zeroTermsQuery, autoGenerateSynonymsPhraseQuery, fuzzyTranspositions); } @@ -845,7 +817,6 @@ protected boolean doEquals(MultiMatchQueryBuilder other) { Objects.equals(maxExpansions, other.maxExpansions) && Objects.equals(minimumShouldMatch, other.minimumShouldMatch) && Objects.equals(fuzzyRewrite, other.fuzzyRewrite) && - Objects.equals(useDisMax, other.useDisMax) && Objects.equals(tieBreaker, other.tieBreaker) && Objects.equals(lenient, other.lenient) && Objects.equals(cutoffFrequency, other.cutoffFrequency) && diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceAggregationBuilder.java index 333eb777ac422..acc0d2ee20bcb 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceAggregationBuilder.java @@ -207,7 +207,9 @@ protected boolean serializeTargetValueType() { public final XContentBuilder internalXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); if (fields != null) { - builder.field(CommonFields.FIELDS.getPreferredName(), fields); + for (Map.Entry fieldEntry : fields.entrySet()) { + builder.field(fieldEntry.getKey(), fieldEntry.getValue()); + } } if (format != null) { builder.field(CommonFields.FORMAT.getPreferredName(), format); diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceFieldConfig.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceFieldConfig.java index 56ceae69ff78e..fbc3081758f96 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceFieldConfig.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceFieldConfig.java @@ -25,16 +25,17 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.xcontent.ObjectParser; -import org.elasticsearch.common.xcontent.ToXContentFragment; +import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.script.Script; import org.joda.time.DateTimeZone; import java.io.IOException; +import java.util.Objects; import java.util.function.BiFunction; -public class MultiValuesSourceFieldConfig implements Writeable, ToXContentFragment { +public class MultiValuesSourceFieldConfig implements Writeable, ToXContentObject { private String fieldName; private Object missing; private Script script; @@ -110,6 +111,7 @@ public void writeTo(StreamOutput out) throws IOException { @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); if (missing != null) { builder.field(ParseField.CommonFields.MISSING.getPreferredName(), missing); } @@ -120,11 +122,33 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field(ParseField.CommonFields.FIELD.getPreferredName(), fieldName); } if (timeZone != null) { - builder.field(ParseField.CommonFields.TIME_ZONE.getPreferredName(), timeZone); + builder.field(ParseField.CommonFields.TIME_ZONE.getPreferredName(), timeZone.getID()); } + builder.endObject(); return builder; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MultiValuesSourceFieldConfig that = (MultiValuesSourceFieldConfig) o; + return Objects.equals(fieldName, that.fieldName) + && Objects.equals(missing, that.missing) + && Objects.equals(script, that.script) + && Objects.equals(timeZone, that.timeZone); + } + + @Override + public int hashCode() { + return Objects.hash(fieldName, missing, script, timeZone); + } + + @Override + public String toString() { + return Strings.toString(this); + } + public static class Builder { private String fieldName; private Object missing = null; diff --git a/server/src/test/java/org/elasticsearch/discovery/ZenFaultDetectionTests.java b/server/src/test/java/org/elasticsearch/discovery/ZenFaultDetectionTests.java index 03c0df43591ba..fbe3ef00a0656 100644 --- a/server/src/test/java/org/elasticsearch/discovery/ZenFaultDetectionTests.java +++ b/server/src/test/java/org/elasticsearch/discovery/ZenFaultDetectionTests.java @@ -32,7 +32,7 @@ import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeValue; -import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.discovery.zen.FaultDetection; import org.elasticsearch.discovery.zen.MasterFaultDetection; import org.elasticsearch.discovery.zen.NodesFaultDetection; @@ -43,10 +43,10 @@ import org.elasticsearch.test.transport.MockTransportService; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.transport.MockTcpTransport; import org.elasticsearch.transport.TransportConnectionListener; import org.elasticsearch.transport.TransportRequestOptions; import org.elasticsearch.transport.TransportService; +import org.elasticsearch.transport.nio.MockNioTransport; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import org.junit.After; @@ -138,8 +138,8 @@ protected MockTransportService build(Settings settings, Version version) { // trace zenfd actions but keep the default otherwise .putList(TransportService.TRACE_LOG_EXCLUDE_SETTING.getKey(), TransportLivenessAction.NAME) .build(), - new MockTcpTransport(settings, threadPool, BigArrays.NON_RECYCLING_INSTANCE, circuitBreakerService, - namedWriteableRegistry, new NetworkService(Collections.emptyList()), version), + new MockNioTransport(settings, version, threadPool, new NetworkService(Collections.emptyList()), + PageCacheRecycler.NON_RECYCLING_INSTANCE, namedWriteableRegistry, circuitBreakerService), threadPool, TransportService.NOOP_TRANSPORT_INTERCEPTOR, (boundAddress) -> diff --git a/server/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java b/server/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java index 1c0329a51e32a..c380341b51b86 100644 --- a/server/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java +++ b/server/src/test/java/org/elasticsearch/discovery/zen/UnicastZenPingTests.java @@ -38,6 +38,7 @@ import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.common.util.PageCacheRecycler; import org.elasticsearch.common.util.concurrent.AbstractRunnable; import org.elasticsearch.common.util.concurrent.ConcurrentCollections; import org.elasticsearch.common.util.concurrent.EsExecutors; @@ -56,6 +57,7 @@ import org.elasticsearch.transport.TransportRequestOptions; import org.elasticsearch.transport.TransportResponseHandler; import org.elasticsearch.transport.TransportService; +import org.elasticsearch.transport.nio.MockNioTransport; import org.junit.After; import org.junit.Before; import org.mockito.Matchers; @@ -143,14 +145,14 @@ public void testSimplePings() throws IOException, InterruptedException, Executio NetworkService networkService = new NetworkService(Collections.emptyList()); - final BiFunction supplier = (s, v) -> new MockTcpTransport( + final BiFunction supplier = (s, v) -> new MockNioTransport( s, + v, threadPool, - BigArrays.NON_RECYCLING_INSTANCE, - new NoneCircuitBreakerService(), - new NamedWriteableRegistry(Collections.emptyList()), networkService, - v); + PageCacheRecycler.NON_RECYCLING_INSTANCE, + new NamedWriteableRegistry(Collections.emptyList()), + new NoneCircuitBreakerService()); NetworkHandle handleA = startServices(settings, threadPool, "UZP_A", Version.CURRENT, supplier); closeables.push(handleA.transportService); @@ -268,14 +270,14 @@ public void testUnknownHostNotCached() throws ExecutionException, InterruptedExc final NetworkService networkService = new NetworkService(Collections.emptyList()); final Map addresses = new HashMap<>(); - final BiFunction supplier = (s, v) -> new MockTcpTransport( + final BiFunction supplier = (s, v) -> new MockNioTransport( s, + v, threadPool, - BigArrays.NON_RECYCLING_INSTANCE, - new NoneCircuitBreakerService(), - new NamedWriteableRegistry(Collections.emptyList()), networkService, - v) { + PageCacheRecycler.NON_RECYCLING_INSTANCE, + new NamedWriteableRegistry(Collections.emptyList()), + new NoneCircuitBreakerService()) { @Override public TransportAddress[] addressesFromString(String address, int perAddressLimit) throws UnknownHostException { final TransportAddress[] transportAddresses = addresses.get(address); @@ -634,14 +636,14 @@ public void testPingingTemporalPings() throws ExecutionException, InterruptedExc NetworkService networkService = new NetworkService(Collections.emptyList()); - final BiFunction supplier = (s, v) -> new MockTcpTransport( + final BiFunction supplier = (s, v) -> new MockNioTransport( s, + v, threadPool, - BigArrays.NON_RECYCLING_INSTANCE, - new NoneCircuitBreakerService(), - new NamedWriteableRegistry(Collections.emptyList()), networkService, - v); + PageCacheRecycler.NON_RECYCLING_INSTANCE, + new NamedWriteableRegistry(Collections.emptyList()), + new NoneCircuitBreakerService()); NetworkHandle handleA = startServices(settings, threadPool, "UZP_A", Version.CURRENT, supplier, EnumSet.allOf(Role.class)); closeables.push(handleA.transportService); @@ -689,15 +691,14 @@ public void testPingingTemporalPings() throws ExecutionException, InterruptedExc public void testInvalidHosts() throws InterruptedException { final Logger logger = mock(Logger.class); - final NetworkService networkService = new NetworkService(Collections.emptyList()); - final Transport transport = new MockTcpTransport( + final Transport transport = new MockNioTransport( Settings.EMPTY, + Version.CURRENT, threadPool, - BigArrays.NON_RECYCLING_INSTANCE, - new NoneCircuitBreakerService(), + new NetworkService(Collections.emptyList()), + PageCacheRecycler.NON_RECYCLING_INSTANCE, new NamedWriteableRegistry(Collections.emptyList()), - networkService, - Version.CURRENT) { + new NoneCircuitBreakerService()) { @Override public BoundTransportAddress boundAddress() { return new BoundTransportAddress( diff --git a/server/src/test/java/org/elasticsearch/index/query/MultiMatchQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/MultiMatchQueryBuilderTests.java index 58604f7e83c47..43c76f028e22e 100644 --- a/server/src/test/java/org/elasticsearch/index/query/MultiMatchQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/MultiMatchQueryBuilderTests.java @@ -118,9 +118,6 @@ protected MultiMatchQueryBuilder doCreateTestQueryBuilder() { if (randomBoolean()) { query.fuzzyRewrite(getRandomRewriteMethod()); } - if (randomBoolean()) { - query.useDisMax(randomBoolean()); - } if (randomBoolean()) { query.tieBreaker(randomFloat()); } @@ -189,7 +186,7 @@ public void testToQueryBoost() throws IOException { } public void testToQueryMultipleTermsBooleanQuery() throws Exception { - Query query = multiMatchQuery("test1 test2").field(STRING_FIELD_NAME).useDisMax(false).toQuery(createShardContext()); + Query query = multiMatchQuery("test1 test2").field(STRING_FIELD_NAME).toQuery(createShardContext()); assertThat(query, instanceOf(BooleanQuery.class)); BooleanQuery bQuery = (BooleanQuery) query; assertThat(bQuery.clauses().size(), equalTo(2)); @@ -198,8 +195,8 @@ public void testToQueryMultipleTermsBooleanQuery() throws Exception { } public void testToQueryMultipleFieldsDisableDismax() throws Exception { - Query query = multiMatchQuery("test").field(STRING_FIELD_NAME).field(STRING_FIELD_NAME_2).useDisMax(false) - .toQuery(createShardContext()); + Query query = multiMatchQuery("test").field(STRING_FIELD_NAME).field(STRING_FIELD_NAME_2).tieBreaker(1.0f) + .toQuery(createShardContext()); assertThat(query, instanceOf(DisjunctionMaxQuery.class)); DisjunctionMaxQuery dQuery = (DisjunctionMaxQuery) query; assertThat(dQuery.getTieBreakerMultiplier(), equalTo(1.0f)); @@ -209,8 +206,7 @@ public void testToQueryMultipleFieldsDisableDismax() throws Exception { } public void testToQueryMultipleFieldsDisMaxQuery() throws Exception { - Query query = multiMatchQuery("test").field(STRING_FIELD_NAME).field(STRING_FIELD_NAME_2).useDisMax(true) - .toQuery(createShardContext()); + Query query = multiMatchQuery("test").field(STRING_FIELD_NAME).field(STRING_FIELD_NAME_2).toQuery(createShardContext()); assertThat(query, instanceOf(DisjunctionMaxQuery.class)); DisjunctionMaxQuery disMaxQuery = (DisjunctionMaxQuery) query; assertThat(disMaxQuery.getTieBreakerMultiplier(), equalTo(0.0f)); @@ -222,7 +218,7 @@ public void testToQueryMultipleFieldsDisMaxQuery() throws Exception { } public void testToQueryFieldsWildcard() throws Exception { - Query query = multiMatchQuery("test").field("mapped_str*").useDisMax(false).toQuery(createShardContext()); + Query query = multiMatchQuery("test").field("mapped_str*").tieBreaker(1.0f).toQuery(createShardContext()); assertThat(query, instanceOf(DisjunctionMaxQuery.class)); DisjunctionMaxQuery dQuery = (DisjunctionMaxQuery) query; assertThat(dQuery.getTieBreakerMultiplier(), equalTo(1.0f)); diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/weighted_avg/WeightedAvgAggregationBuilderTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/weighted_avg/WeightedAvgAggregationBuilderTests.java new file mode 100644 index 0000000000000..0b7c5cd0f86e0 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/weighted_avg/WeightedAvgAggregationBuilderTests.java @@ -0,0 +1,78 @@ +/* + * 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.search.aggregations.metrics.weighted_avg; + +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.SearchModule; +import org.elasticsearch.search.aggregations.AggregatorFactories; +import org.elasticsearch.search.aggregations.metrics.WeightedAvgAggregationBuilder; +import org.elasticsearch.search.aggregations.support.MultiValuesSourceFieldConfig; +import org.elasticsearch.test.AbstractSerializingTestCase; +import org.junit.Before; + +import java.io.IOException; +import java.util.Collections; + +import static org.hamcrest.Matchers.hasSize; + +public class WeightedAvgAggregationBuilderTests extends AbstractSerializingTestCase { + String aggregationName; + + @Before + public void setupName() { + aggregationName = randomAlphaOfLength(10); + } + + @Override + protected NamedXContentRegistry xContentRegistry() { + SearchModule searchModule = new SearchModule(Settings.EMPTY, false, Collections.emptyList()); + return new NamedXContentRegistry(searchModule.getNamedXContents()); + } + + @Override + protected WeightedAvgAggregationBuilder doParseInstance(XContentParser parser) throws IOException { + assertSame(XContentParser.Token.START_OBJECT, parser.nextToken()); + AggregatorFactories.Builder parsed = AggregatorFactories.parseAggregators(parser); + assertThat(parsed.getAggregatorFactories(), hasSize(1)); + assertThat(parsed.getPipelineAggregatorFactories(), hasSize(0)); + WeightedAvgAggregationBuilder agg = (WeightedAvgAggregationBuilder) parsed.getAggregatorFactories().iterator().next(); + assertNull(parser.nextToken()); + assertNotNull(agg); + return agg; + } + + @Override + protected WeightedAvgAggregationBuilder createTestInstance() { + MultiValuesSourceFieldConfig valueConfig = new MultiValuesSourceFieldConfig.Builder().setFieldName("value_field").build(); + MultiValuesSourceFieldConfig weightConfig = new MultiValuesSourceFieldConfig.Builder().setFieldName("weight_field").build(); + WeightedAvgAggregationBuilder aggregationBuilder = new WeightedAvgAggregationBuilder(aggregationName) + .value(valueConfig) + .weight(weightConfig); + return aggregationBuilder; + } + + @Override + protected Writeable.Reader instanceReader() { + return WeightedAvgAggregationBuilder::new; + } +} diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceFieldConfigTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceFieldConfigTests.java index ac1c07a40490e..5007784a3d9a9 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceFieldConfigTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceFieldConfigTests.java @@ -19,12 +19,37 @@ package org.elasticsearch.search.aggregations.support; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.script.Script; -import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.AbstractSerializingTestCase; +import org.joda.time.DateTimeZone; + +import java.io.IOException; import static org.hamcrest.Matchers.equalTo; -public class MultiValuesSourceFieldConfigTests extends ESTestCase { +public class MultiValuesSourceFieldConfigTests extends AbstractSerializingTestCase { + + @Override + protected MultiValuesSourceFieldConfig doParseInstance(XContentParser parser) throws IOException { + return MultiValuesSourceFieldConfig.PARSER.apply(true, true).apply(parser, null).build(); + } + + @Override + protected MultiValuesSourceFieldConfig createTestInstance() { + String field = randomAlphaOfLength(10); + Object missing = randomBoolean() ? randomAlphaOfLength(10) : null; + DateTimeZone timeZone = randomBoolean() ? randomDateTimeZone() : null; + return new MultiValuesSourceFieldConfig.Builder() + .setFieldName(field).setMissing(missing).setScript(null).setTimeZone(timeZone).build(); + } + + @Override + protected Writeable.Reader instanceReader() { + return MultiValuesSourceFieldConfig::new; + } + public void testMissingFieldScript() { IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new MultiValuesSourceFieldConfig.Builder().build()); assertThat(e.getMessage(), equalTo("[field] and [script] cannot both be null. Please specify one or the other.")); diff --git a/server/src/test/java/org/elasticsearch/search/query/MultiMatchQueryIT.java b/server/src/test/java/org/elasticsearch/search/query/MultiMatchQueryIT.java index f5688e0a4215e..76850a197d969 100644 --- a/server/src/test/java/org/elasticsearch/search/query/MultiMatchQueryIT.java +++ b/server/src/test/java/org/elasticsearch/search/query/MultiMatchQueryIT.java @@ -203,7 +203,7 @@ public void testDefaults() throws ExecutionException, InterruptedException { searchResponse = client().prepareSearch("test") .setQuery(randomizeType(multiMatchQuery("marvel hero captain america", "full_name", "first_name", "last_name", "category") - .operator(Operator.OR).useDisMax(false).type(type))).get(); + .operator(Operator.OR).type(type))).get(); assertFirstHit(searchResponse, anyOf(hasId("theone"), hasId("theother"))); assertThat(searchResponse.getHits().getHits()[0].getScore(), greaterThan(searchResponse.getHits().getHits()[1].getScore())); @@ -323,14 +323,14 @@ public void testCutoffFreq() throws ExecutionException, InterruptedException { cutoffFrequency = randomBoolean() ? Math.min(1, numDocs * 1.f / between(10, 20)) : 1.f / between(10, 20); searchResponse = client().prepareSearch("test") .setQuery(randomizeType(multiMatchQuery("marvel hero captain america", "full_name", "first_name", "last_name", "category") - .operator(Operator.OR).useDisMax(false).cutoffFrequency(cutoffFrequency).type(type))).get(); + .operator(Operator.OR).cutoffFrequency(cutoffFrequency).type(type))).get(); assertFirstHit(searchResponse, anyOf(hasId("theone"), hasId("theother"))); assertThat(searchResponse.getHits().getHits()[0].getScore(), greaterThan(searchResponse.getHits().getHits()[1].getScore())); long size = searchResponse.getHits().getTotalHits().value; searchResponse = client().prepareSearch("test") .setQuery(randomizeType(multiMatchQuery("marvel hero captain america", "full_name", "first_name", "last_name", "category") - .operator(Operator.OR).useDisMax(false).type(type))).get(); + .operator(Operator.OR).type(type))).get(); assertFirstHit(searchResponse, anyOf(hasId("theone"), hasId("theother"))); assertThat("common terms expected to be a way smaller result set", size, lessThan(searchResponse.getHits().getTotalHits().value)); @@ -399,7 +399,7 @@ public void testEquivalence() { SearchResponse left = client().prepareSearch("test").setSize(numDocs) .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_id")) .setQuery(randomizeType(multiMatchQueryBuilder - .operator(op).useDisMax(false).minimumShouldMatch(minShouldMatch).type(type))).get(); + .operator(op).tieBreaker(1.0f).minimumShouldMatch(minShouldMatch).type(type))).get(); SearchResponse right = client().prepareSearch("test").setSize(numDocs) .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_id")) @@ -418,7 +418,7 @@ public void testEquivalence() { SearchResponse left = client().prepareSearch("test").setSize(numDocs) .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_id")) .setQuery(randomizeType(multiMatchQuery("capta", "full_name", "first_name", "last_name", "category") - .type(MatchQuery.Type.PHRASE_PREFIX).useDisMax(false).minimumShouldMatch(minShouldMatch))).get(); + .type(MatchQuery.Type.PHRASE_PREFIX).tieBreaker(1.0f).minimumShouldMatch(minShouldMatch))).get(); SearchResponse right = client().prepareSearch("test").setSize(numDocs) .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_id")) @@ -437,7 +437,7 @@ public void testEquivalence() { left = client().prepareSearch("test").setSize(numDocs) .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_id")) .setQuery(randomizeType(multiMatchQuery("captain america", "full_name", "first_name", "last_name", "category") - .type(MatchQuery.Type.PHRASE).useDisMax(false).minimumShouldMatch(minShouldMatch))).get(); + .type(MatchQuery.Type.PHRASE).minimumShouldMatch(minShouldMatch))).get(); } else { left = client().prepareSearch("test").setSize(numDocs) .addSort(SortBuilders.scoreSort()).addSort(SortBuilders.fieldSort("_id")) diff --git a/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java b/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java index ebfd4dbbebf29..2b6b8b1c60bcc 100644 --- a/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java +++ b/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java @@ -681,7 +681,6 @@ public void testMultiMatchQuery() throws Exception { // this uses dismax so scores are equal and the order can be arbitrary assertSearchHits(searchResponse, "1", "2"); - builder.useDisMax(false); searchResponse = client().prepareSearch() .setQuery(builder) .get(); @@ -786,7 +785,6 @@ public void testMultiMatchQueryMinShouldMatch() { MultiMatchQueryBuilder multiMatchQuery = multiMatchQuery("value1 value2 foo", "field1", "field2"); - multiMatchQuery.useDisMax(true); multiMatchQuery.minimumShouldMatch("70%"); SearchResponse searchResponse = client().prepareSearch() .setQuery(multiMatchQuery) @@ -800,7 +798,6 @@ public void testMultiMatchQueryMinShouldMatch() { assertFirstHit(searchResponse, hasId("1")); assertSecondHit(searchResponse, hasId("2")); - multiMatchQuery.useDisMax(false); multiMatchQuery.minimumShouldMatch("70%"); searchResponse = client().prepareSearch().setQuery(multiMatchQuery).get(); assertHitCount(searchResponse, 1L); @@ -1475,11 +1472,11 @@ public void testMultiMatchLenientIssue3797() { refresh(); SearchResponse searchResponse = client().prepareSearch("test") - .setQuery(multiMatchQuery("value2", "field2").field("field1", 2).lenient(true).useDisMax(false)).get(); + .setQuery(multiMatchQuery("value2", "field2").field("field1", 2).lenient(true)).get(); assertHitCount(searchResponse, 1L); searchResponse = client().prepareSearch("test") - .setQuery(multiMatchQuery("value2", "field2").field("field1", 2).lenient(true).useDisMax(true)).get(); + .setQuery(multiMatchQuery("value2", "field2").field("field1", 2).lenient(true)).get(); assertHitCount(searchResponse, 1L); searchResponse = client().prepareSearch("test") diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilege.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilege.java index 9c03941b0313a..fba595e7a09e4 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilege.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilege.java @@ -9,6 +9,8 @@ import org.elasticsearch.action.admin.cluster.state.ClusterStateAction; import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.MapBuilder; +import org.elasticsearch.xpack.core.indexlifecycle.action.GetLifecycleAction; +import org.elasticsearch.xpack.core.indexlifecycle.action.GetStatusAction; import org.elasticsearch.xpack.core.security.action.token.InvalidateTokenAction; import org.elasticsearch.xpack.core.security.action.token.RefreshTokenAction; import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesAction; @@ -47,6 +49,8 @@ public final class ClusterPrivilege extends Privilege { private static final Automaton MANAGE_CCR_AUTOMATON = patterns("cluster:admin/xpack/ccr/*", ClusterStateAction.NAME, HasPrivilegesAction.NAME); private static final Automaton READ_CCR_AUTOMATON = patterns(ClusterStateAction.NAME, HasPrivilegesAction.NAME); + private static final Automaton MANAGE_ILM_AUTOMATON = patterns("cluster:admin/ilm/*"); + private static final Automaton READ_ILM_AUTOMATON = patterns(GetLifecycleAction.NAME, GetStatusAction.NAME); public static final ClusterPrivilege NONE = new ClusterPrivilege("none", Automatons.EMPTY); public static final ClusterPrivilege ALL = new ClusterPrivilege("all", ALL_CLUSTER_AUTOMATON); @@ -69,6 +73,8 @@ public final class ClusterPrivilege extends Privilege { public static final ClusterPrivilege MANAGE_PIPELINE = new ClusterPrivilege("manage_pipeline", "cluster:admin/ingest/pipeline/*"); public static final ClusterPrivilege MANAGE_CCR = new ClusterPrivilege("manage_ccr", MANAGE_CCR_AUTOMATON); public static final ClusterPrivilege READ_CCR = new ClusterPrivilege("read_ccr", READ_CCR_AUTOMATON); + public static final ClusterPrivilege MANAGE_ILM = new ClusterPrivilege("manage_ilm", MANAGE_ILM_AUTOMATON); + public static final ClusterPrivilege READ_ILM = new ClusterPrivilege("read_ilm", READ_ILM_AUTOMATON); public static final Predicate ACTION_MATCHER = ClusterPrivilege.ALL.predicate(); @@ -92,6 +98,8 @@ public final class ClusterPrivilege extends Privilege { .put("manage_rollup", MANAGE_ROLLUP) .put("manage_ccr", MANAGE_CCR) .put("read_ccr", READ_CCR) + .put("manage_ilm", MANAGE_ILM) + .put("read_ilm", READ_ILM) .immutableMap(); private static final ConcurrentHashMap, ClusterPrivilege> CACHE = new ConcurrentHashMap<>(); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilege.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilege.java index 6281fbb2c8fd6..d6549df5f9d5a 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilege.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilege.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.xpack.core.ccr.action.PutFollowAction; import org.elasticsearch.xpack.core.ccr.action.UnfollowAction; +import org.elasticsearch.xpack.core.indexlifecycle.action.ExplainLifecycleAction; import org.elasticsearch.xpack.core.security.support.Automatons; import java.util.Arrays; @@ -57,9 +58,11 @@ public final class IndexPrivilege extends Privilege { private static final Automaton DELETE_INDEX_AUTOMATON = patterns(DeleteIndexAction.NAME); private static final Automaton VIEW_METADATA_AUTOMATON = patterns(GetAliasesAction.NAME, AliasesExistAction.NAME, GetIndexAction.NAME, IndicesExistsAction.NAME, GetFieldMappingsAction.NAME + "*", GetMappingsAction.NAME, - ClusterSearchShardsAction.NAME, TypesExistsAction.NAME, ValidateQueryAction.NAME + "*", GetSettingsAction.NAME); + ClusterSearchShardsAction.NAME, TypesExistsAction.NAME, ValidateQueryAction.NAME + "*", GetSettingsAction.NAME, + ExplainLifecycleAction.NAME); private static final Automaton MANAGE_FOLLOW_INDEX_AUTOMATON = patterns(PutFollowAction.NAME, UnfollowAction.NAME, CloseIndexAction.NAME); + private static final Automaton MANAGE_ILM_AUTOMATON = patterns("indices:admin/ilm/*"); public static final IndexPrivilege NONE = new IndexPrivilege("none", Automatons.EMPTY); public static final IndexPrivilege ALL = new IndexPrivilege("all", ALL_AUTOMATON); @@ -75,6 +78,7 @@ public final class IndexPrivilege extends Privilege { public static final IndexPrivilege CREATE_INDEX = new IndexPrivilege("create_index", CREATE_INDEX_AUTOMATON); public static final IndexPrivilege VIEW_METADATA = new IndexPrivilege("view_index_metadata", VIEW_METADATA_AUTOMATON); public static final IndexPrivilege MANAGE_FOLLOW_INDEX = new IndexPrivilege("manage_follow_index", MANAGE_FOLLOW_INDEX_AUTOMATON); + public static final IndexPrivilege MANAGE_ILM = new IndexPrivilege("manage_ilm", MANAGE_ILM_AUTOMATON); private static final Map VALUES = MapBuilder.newMapBuilder() .put("none", NONE) @@ -91,6 +95,7 @@ public final class IndexPrivilege extends Privilege { .put("view_index_metadata", VIEW_METADATA) .put("read_cross_cluster", READ_CROSS_CLUSTER) .put("manage_follow_index", MANAGE_FOLLOW_INDEX) + .put("manage_ilm", MANAGE_ILM) .immutableMap(); public static final Predicate ACTION_MATCHER = ALL.predicate(); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/PrivilegeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/PrivilegeTests.java index f2bfd8d2d8e6b..58432cdf6c79e 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/PrivilegeTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/PrivilegeTests.java @@ -138,4 +138,57 @@ public void testManageCcrPrivilege() { assertThat(predicate.test("cluster:admin/xpack/whatever"), is(false)); } + public void testIlmPrivileges() { + { + Predicate predicate = ClusterPrivilege.MANAGE_ILM.predicate(); + // check cluster actions + assertThat(predicate.test("cluster:admin/ilm/delete"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/_move/post"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/put"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/start"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/stop"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/brand_new_api"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/get"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/operation_mode/get"), is(true)); + // check non-ilm action + assertThat(predicate.test("cluster:admin/whatever"), is(false)); + } + + { + Predicate predicate = ClusterPrivilege.READ_ILM.predicate(); + // check cluster actions + assertThat(predicate.test("cluster:admin/ilm/delete"), is(false)); + assertThat(predicate.test("cluster:admin/ilm/_move/post"), is(false)); + assertThat(predicate.test("cluster:admin/ilm/put"), is(false)); + assertThat(predicate.test("cluster:admin/ilm/start"), is(false)); + assertThat(predicate.test("cluster:admin/ilm/stop"), is(false)); + assertThat(predicate.test("cluster:admin/ilm/brand_new_api"), is(false)); + assertThat(predicate.test("cluster:admin/ilm/get"), is(true)); + assertThat(predicate.test("cluster:admin/ilm/operation_mode/get"), is(true)); + // check non-ilm action + assertThat(predicate.test("cluster:admin/whatever"), is(false)); + } + + { + Predicate predicate = IndexPrivilege.MANAGE_ILM.predicate(); + // check indices actions + assertThat(predicate.test("indices:admin/ilm/retry"), is(true)); + assertThat(predicate.test("indices:admin/ilm/remove_policy"), is(true)); + assertThat(predicate.test("indices:admin/ilm/brand_new_api"), is(true)); + assertThat(predicate.test("indices:admin/ilm/explain"), is(true)); + // check non-ilm action + assertThat(predicate.test("indices:admin/whatever"), is(false)); + } + + { + Predicate predicate = IndexPrivilege.VIEW_METADATA.predicate(); + // check indices actions + assertThat(predicate.test("indices:admin/ilm/retry"), is(false)); + assertThat(predicate.test("indices:admin/ilm/remove_policy"), is(false)); + assertThat(predicate.test("indices:admin/ilm/brand_new_api"), is(false)); + assertThat(predicate.test("indices:admin/ilm/explain"), is(true)); + // check non-ilm action + assertThat(predicate.test("indices:admin/whatever"), is(false)); + } + } } diff --git a/x-pack/plugin/ilm/qa/with-security/roles.yml b/x-pack/plugin/ilm/qa/with-security/roles.yml index baf89bea34568..64d437bbfd291 100644 --- a/x-pack/plugin/ilm/qa/with-security/roles.yml +++ b/x-pack/plugin/ilm/qa/with-security/roles.yml @@ -1,8 +1,11 @@ ilm: cluster: - monitor - - manage + - manage_ilm indices: + - names: [ 'view-only-*' ] + privileges: + - view_index_metadata - names: [ 'ilm-*' ] privileges: - monitor diff --git a/x-pack/plugin/ilm/qa/with-security/src/test/java/org/elasticsearch/xpack/security/PermissionsIT.java b/x-pack/plugin/ilm/qa/with-security/src/test/java/org/elasticsearch/xpack/security/PermissionsIT.java index 01eb07bb35b4f..a0c21f4614de3 100644 --- a/x-pack/plugin/ilm/qa/with-security/src/test/java/org/elasticsearch/xpack/security/PermissionsIT.java +++ b/x-pack/plugin/ilm/qa/with-security/src/test/java/org/elasticsearch/xpack/security/PermissionsIT.java @@ -119,6 +119,13 @@ public void testCanManageIndexWithNoPermissions() throws Exception { }); } + public void testCanViewExplainOnUnmanagedIndex() throws Exception { + createIndexAsAdmin("view-only-ilm", indexSettingsWithPolicy, ""); + Request request = new Request("GET", "/view-only-ilm/_ilm/explain"); + // test_ilm user has permissions to view + assertOK(client().performRequest(request)); + } + private void createNewSingletonPolicy(String policy, String phaseName, LifecycleAction action) throws IOException { Phase phase = new Phase(phaseName, TimeValue.ZERO, singletonMap(action.getWriteableName(), action)); LifecyclePolicy lifecyclePolicy = new LifecyclePolicy(policy, singletonMap(phase.getName(), phase)); diff --git a/x-pack/plugin/sql/qa/src/main/resources/fulltext.csv-spec b/x-pack/plugin/sql/qa/src/main/resources/fulltext.csv-spec index 93493ffdc2acb..0cfcf743414b9 100644 --- a/x-pack/plugin/sql/qa/src/main/resources/fulltext.csv-spec +++ b/x-pack/plugin/sql/qa/src/main/resources/fulltext.csv-spec @@ -59,14 +59,14 @@ SELECT emp_no, first_name, gender, last_name FROM test_emp WHERE MATCH('first_na ; multiMatchQueryAllOptions -SELECT emp_no, first_name, gender, last_name FROM test_emp WHERE MATCH('first_name,last_name', 'Morton', 'slop=1;lenient=true;cutoff_frequency=2;tie_breaker=0.1;use_dis_max=true;fuzzy_rewrite=scoring_boolean;minimum_should_match=1;operator=AND;max_expansions=30;prefix_length=1;analyzer=english;type=best_fields;auto_generate_synonyms_phrase_query=true;fuzzy_transpositions=true'); +SELECT emp_no, first_name, gender, last_name FROM test_emp WHERE MATCH('first_name,last_name', 'Morton', 'slop=1;lenient=true;cutoff_frequency=2;tie_breaker=0.1;fuzzy_rewrite=scoring_boolean;minimum_should_match=1;operator=AND;max_expansions=30;prefix_length=1;analyzer=english;type=best_fields;auto_generate_synonyms_phrase_query=true;fuzzy_transpositions=true'); emp_no:i | first_name:s | gender:s | last_name:s 10095 |Hilari |M |Morton ; multiMatchQueryWithInMultipleCommaSeparatedStrings -SELECT emp_no, first_name, gender, last_name FROM test_emp WHERE MATCH('first_name,last_name', 'Morton', 'slop=1;lenient=true', 'cutoff_frequency=2','tie_breaker=0.1;use_dis_max=true;fuzzy_rewrite=scoring_boolean','minimum_should_match=1;operator=AND;max_expansions=30;prefix_length=1;analyzer=english;type=best_fields;auto_generate_synonyms_phrase_query=true;fuzzy_transpositions=true'); +SELECT emp_no, first_name, gender, last_name FROM test_emp WHERE MATCH('first_name,last_name', 'Morton', 'slop=1;lenient=true', 'cutoff_frequency=2','tie_breaker=0.1;fuzzy_rewrite=scoring_boolean','minimum_should_match=1;operator=AND;max_expansions=30;prefix_length=1;analyzer=english;type=best_fields;auto_generate_synonyms_phrase_query=true;fuzzy_transpositions=true'); emp_no:i | first_name:s | gender:s | last_name:s 10095 |Hilari |M |Morton diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQuery.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQuery.java index 81c990f85bdb4..30def4db3dac4 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQuery.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQuery.java @@ -6,10 +6,10 @@ package org.elasticsearch.xpack.sql.querydsl.query; import org.elasticsearch.common.Booleans; -import org.elasticsearch.index.query.Operator; -import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.MultiMatchQueryBuilder; +import org.elasticsearch.index.query.Operator; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MultiMatchQueryPredicate; import org.elasticsearch.xpack.sql.tree.Location; @@ -32,7 +32,6 @@ public class MultiMatchQuery extends LeafQuery { appliers.put("lenient", (qb, s) -> qb.lenient(Booleans.parseBoolean(s))); appliers.put("cutoff_frequency", (qb, s) -> qb.cutoffFrequency(Float.valueOf(s))); appliers.put("tie_breaker", (qb, s) -> qb.tieBreaker(Float.valueOf(s))); - appliers.put("use_dis_max", (qb, s) -> qb.useDisMax(Booleans.parseBoolean(s))); appliers.put("fuzzy_rewrite", (qb, s) -> qb.fuzzyRewrite(s)); appliers.put("minimum_should_match", (qb, s) -> qb.minimumShouldMatch(s)); appliers.put("operator", (qb, s) -> qb.operator(Operator.fromString(s))); diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQueryTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQueryTests.java index ba2d548cde9dd..2e26c7c0595c2 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQueryTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQueryTests.java @@ -23,8 +23,7 @@ public void testQueryBuilding() { MultiMatchQueryBuilder qb = getBuilder("lenient=true"); assertThat(qb.lenient(), equalTo(true)); - qb = getBuilder("use_dis_max=true;type=best_fields"); - assertThat(qb.useDisMax(), equalTo(true)); + qb = getBuilder("type=best_fields"); assertThat(qb.getType(), equalTo(MultiMatchQueryBuilder.Type.BEST_FIELDS)); Exception e = expectThrows(IllegalArgumentException.class, () -> getBuilder("pizza=yummy"));