From 97a2d92363b29557726dde9d7317c12bb1be3d51 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Mon, 12 Nov 2018 15:38:58 +0100 Subject: [PATCH 1/5] Add Delete Privileges API to HLRC This commit adds the Delete Privileges API to the high level REST client. Related to #29827 --- .../elasticsearch/client/SecurityClient.java | 36 +++++++- .../client/SecurityRequestConverters.java | 21 ++++- .../security/DeletePrivilegesRequest.java | 56 ++++++++++++ .../security/DeletePrivilegesResponse.java | 87 +++++++++++++++++++ .../SecurityRequestConvertersTests.java | 16 ++++ .../SecurityDocumentationIT.java | 82 ++++++++++++++++- .../security/delete-privileges.asciidoc | 37 ++++++++ .../high-level/supported-apis.asciidoc | 2 + 8 files changed, 328 insertions(+), 9 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesRequest.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesResponse.java create mode 100644 docs/java-rest/high-level/security/delete-privileges.asciidoc diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java index d3b38aaf9e9d2..8bde40acbe867 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 @@ -29,6 +29,8 @@ import org.elasticsearch.client.security.ClearRolesCacheResponse; import org.elasticsearch.client.security.CreateTokenRequest; import org.elasticsearch.client.security.CreateTokenResponse; +import org.elasticsearch.client.security.DeletePrivilegesRequest; +import org.elasticsearch.client.security.DeletePrivilegesResponse; import org.elasticsearch.client.security.DeleteRoleMappingRequest; import org.elasticsearch.client.security.DeleteRoleMappingResponse; import org.elasticsearch.client.security.DeleteRoleRequest; @@ -221,7 +223,7 @@ public void disableUserAsync(DisableUserRequest request, RequestOptions options, * See * the docs for more. * - * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized * @return the responsee from the authenticate user call */ public AuthenticateResponse authenticate(RequestOptions options) throws IOException { @@ -234,8 +236,8 @@ public AuthenticateResponse authenticate(RequestOptions options) throws IOExcept * See * the docs for more. * - * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized - * @param listener the listener to be notified upon request completion + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion */ public void authenticateAsync(RequestOptions options, ActionListener listener) { restHighLevelClient.performRequestAsyncAndParseEntity(AuthenticateRequest.INSTANCE, AuthenticateRequest::getRequest, options, @@ -473,4 +475,32 @@ public void invalidateTokenAsync(InvalidateTokenRequest request, RequestOptions InvalidateTokenResponse::fromXContent, listener, emptySet()); } + /** + * Removes an application privilege + * See + * the docs for more. + * @param request the request with the application privilege to delete + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response from the delete role call + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public DeletePrivilegesResponse deletePrivileges(DeletePrivilegesRequest request, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::deletePrivileges, options, + DeletePrivilegesResponse::fromXContent, singleton(404)); + } + + /** + * Asynchronously removes an application privilege + * See + * the docs for more. + * @param request the request with the application privilege to delete + * @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 deletePrivilegesAsync(DeletePrivilegesRequest request, RequestOptions options, + ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::deletePrivileges, options, + DeletePrivilegesResponse::fromXContent, listener, singleton(404)); + } + } 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 5958a763eeebc..160aa1fd82b0a 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 @@ -19,21 +19,22 @@ package org.elasticsearch.client; -import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; +import org.elasticsearch.client.security.ChangePasswordRequest; import org.elasticsearch.client.security.ClearRealmCacheRequest; import org.elasticsearch.client.security.ClearRolesCacheRequest; import org.elasticsearch.client.security.CreateTokenRequest; +import org.elasticsearch.client.security.DeletePrivilegesRequest; import org.elasticsearch.client.security.DeleteRoleMappingRequest; import org.elasticsearch.client.security.DeleteRoleRequest; -import org.elasticsearch.client.security.InvalidateTokenRequest; -import org.elasticsearch.client.security.PutRoleMappingRequest; import org.elasticsearch.client.security.DisableUserRequest; import org.elasticsearch.client.security.EnableUserRequest; import org.elasticsearch.client.security.GetRoleMappingsRequest; -import org.elasticsearch.client.security.ChangePasswordRequest; +import org.elasticsearch.client.security.InvalidateTokenRequest; +import org.elasticsearch.client.security.PutRoleMappingRequest; import org.elasticsearch.client.security.PutUserRequest; import org.elasticsearch.client.security.SetUserEnabledRequest; import org.elasticsearch.common.Strings; @@ -172,4 +173,16 @@ static Request invalidateToken(InvalidateTokenRequest invalidateTokenRequest) th request.setEntity(createEntity(invalidateTokenRequest, REQUEST_BODY_CONTENT_TYPE)); return request; } + + static Request deletePrivileges(DeletePrivilegesRequest deletePrivilegeRequest) { + String endpoint = new RequestConverters.EndpointBuilder() + .addPathPartAsIs("_xpack/security/privilege") + .addPathPart(deletePrivilegeRequest.getApplication()) + .addCommaSeparatedPathParts(deletePrivilegeRequest.getPrivileges()) + .build(); + Request request = new Request(HttpDelete.METHOD_NAME, endpoint); + RequestConverters.Params params = new RequestConverters.Params(request); + params.withRefreshPolicy(deletePrivilegeRequest.getRefreshPolicy()); + return request; + } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesRequest.java new file mode 100644 index 0000000000000..2a95f44019f78 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesRequest.java @@ -0,0 +1,56 @@ +/* + * 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 java.util.Objects; + +/** + * A request to delete application privileges + */ +public final class DeletePrivilegesRequest implements Validatable { + + private final String application; + private final String[] privileges; + private final RefreshPolicy refreshPolicy; + + public DeletePrivilegesRequest(String application, String... privileges) { + this(application, privileges, RefreshPolicy.IMMEDIATE); + } + + public DeletePrivilegesRequest(String application, String[] privileges, RefreshPolicy refreshPolicy) { + this.application = Objects.requireNonNull(application, "application is required"); + this.privileges = Objects.requireNonNull(privileges, "privileges are required"); + this.refreshPolicy = Objects.requireNonNull(refreshPolicy, "refresh policy is required"); + } + + public String getApplication() { + return application; + } + + public String[] getPrivileges() { + return privileges; + } + + public RefreshPolicy getRefreshPolicy() { + return refreshPolicy; + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesResponse.java new file mode 100644 index 0000000000000..6a2f857c37dec --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesResponse.java @@ -0,0 +1,87 @@ +/* + * 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.ElasticsearchParseException; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Response for application privileges deletion + */ +public final class DeletePrivilegesResponse { + + private final String application; + private final List privileges; + + DeletePrivilegesResponse(String application, List privileges) { + this.application = Objects.requireNonNull(application, "application is required"); + this.privileges = Objects.requireNonNull(privileges, "privileges are required"); + } + + public String getApplication() { + return application; + } + + public boolean isFound(final String privilege) { + return privileges.contains(privilege); + } + + public static DeletePrivilegesResponse fromXContent(XContentParser parser) throws IOException { + XContentParser.Token token = parser.currentToken(); + if (token == null) { + token = parser.nextToken(); + } + if (token == XContentParser.Token.START_OBJECT) { + token = parser.nextToken(); + if (token == XContentParser.Token.FIELD_NAME) { + final String application = parser.currentName(); + final List found = new ArrayList<>(); + token = parser.nextToken(); + if (token == XContentParser.Token.START_OBJECT) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + String privilege = parser.currentName(); + token = parser.nextToken(); + if (token == XContentParser.Token.START_OBJECT) { + String currentFieldName = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token == XContentParser.Token.VALUE_BOOLEAN) { + if ("found".equals(currentFieldName) && parser.booleanValue()) { + found.add(privilege); + } + } + } + } + } + } + } + return new DeletePrivilegesResponse(application, found); + } + } + throw new ElasticsearchParseException("Unable to parse DeletePrivilegesResponse"); + } +} 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 d2679906af207..199356de2902e 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 @@ -24,6 +24,7 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.elasticsearch.client.security.CreateTokenRequest; +import org.elasticsearch.client.security.DeletePrivilegesRequest; import org.elasticsearch.client.security.DeleteRoleMappingRequest; import org.elasticsearch.client.security.DeleteRoleRequest; import org.elasticsearch.client.security.DisableUserRequest; @@ -241,4 +242,19 @@ public void testCreateTokenWithClientCredentialsGrant() throws Exception { assertEquals(0, request.getParameters().size()); assertToXContentBody(createTokenRequest, request.getEntity()); } + + public void testDeletePrivileges() { + final String application = randomAlphaOfLengthBetween(1, 12); + final List privileges = randomSubsetOf(randomIntBetween(1, 3), "read", "write", "all"); + final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); + final Map expectedParams = getExpectedParamsFromRefreshPolicy(refreshPolicy); + DeletePrivilegesRequest deletePrivilegesRequest = + new DeletePrivilegesRequest(application, privileges.toArray(Strings.EMPTY_ARRAY), refreshPolicy); + Request request = SecurityRequestConverters.deletePrivileges(deletePrivilegesRequest); + assertEquals(HttpDelete.METHOD_NAME, request.getMethod()); + assertEquals("/_xpack/security/privilege/" + application + "/" + Strings.collectionToCommaDelimitedString(privileges), + request.getEndpoint()); + assertEquals(expectedParams, request.getParameters()); + assertNull(request.getEntity()); + } } 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 ffa30e16c0468..71cfdd4ba5b89 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 @@ -28,6 +28,7 @@ import org.elasticsearch.client.ESRestHighLevelClientTestCase; import org.elasticsearch.client.Request; import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.Response; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.security.AuthenticateResponse; import org.elasticsearch.client.security.ChangePasswordRequest; @@ -37,6 +38,8 @@ import org.elasticsearch.client.security.ClearRolesCacheResponse; import org.elasticsearch.client.security.CreateTokenRequest; import org.elasticsearch.client.security.CreateTokenResponse; +import org.elasticsearch.client.security.DeletePrivilegesRequest; +import org.elasticsearch.client.security.DeletePrivilegesResponse; import org.elasticsearch.client.security.DeleteRoleMappingRequest; import org.elasticsearch.client.security.DeleteRoleMappingResponse; import org.elasticsearch.client.security.DeleteRoleRequest; @@ -55,13 +58,14 @@ import org.elasticsearch.client.security.PutUserRequest; import org.elasticsearch.client.security.PutUserResponse; import org.elasticsearch.client.security.RefreshPolicy; +import org.elasticsearch.client.security.support.CertificateInfo; import org.elasticsearch.client.security.support.expressiondsl.RoleMapperExpression; +import org.elasticsearch.client.security.support.expressiondsl.expressions.AnyRoleMapperExpression; import org.elasticsearch.client.security.support.expressiondsl.fields.FieldRoleMapperExpression; import org.elasticsearch.client.security.user.User; -import org.elasticsearch.client.security.support.CertificateInfo; -import org.elasticsearch.client.security.support.expressiondsl.expressions.AnyRoleMapperExpression; import org.elasticsearch.common.Strings; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.rest.RestStatus; import org.hamcrest.Matchers; import java.io.IOException; @@ -916,4 +920,78 @@ public void onFailure(Exception e) { // See https://github.com/elastic/elasticsearch/issues/35115 } } + + public void testDeletePrivilege() throws Exception { + RestHighLevelClient client = highLevelClient(); + { + final Request createPrivilegeRequest = new Request("POST", "/_xpack/security/privilege"); + createPrivilegeRequest.setJsonEntity("{" + + " \"testapp\": {" + + " \"read\": {" + + " \"actions\": [ \"action:login\", \"data:read/*\" ]" + + " }," + + " \"write\": {" + + " \"actions\": [ \"action:login\", \"data:write/*\" ]" + + " }," + + " \"all\": {" + + " \"actions\": [ \"action:login\", \"data:write/*\" ]" + + " }" + + " }" + + "}"); + + final Response createPrivilegeResponse = client.getLowLevelClient().performRequest(createPrivilegeRequest); + assertEquals(RestStatus.OK.getStatus(), createPrivilegeResponse.getStatusLine().getStatusCode()); + } + { + // tag::delete-privileges-request + DeletePrivilegesRequest request = new DeletePrivilegesRequest( + "testapp", // <1> + "read", "write"); // <2> + // end::delete-privileges-request + + // tag::delete-privileges-execute + DeletePrivilegesResponse response = client.security().deletePrivileges(request, RequestOptions.DEFAULT); + // end::delete-privileges-execute + + // tag::delete-privileges-response + String application = response.getApplication(); // <1> + boolean found = response.isFound("read"); // <2> + // end::delete-privileges-response + assertThat(application, equalTo("testapp")); + assertTrue(response.isFound("write")); + assertTrue(found); + + // check if deleting the already deleted privileges again will give us a different response + response = client.security().deletePrivileges(request, RequestOptions.DEFAULT); + assertFalse(response.isFound("write")); + } + { + DeletePrivilegesRequest deletePrivilegesRequest = new DeletePrivilegesRequest("testapp", "all"); + + ActionListener listener; + //tag::delete-privileges-execute-listener + listener = new ActionListener() { + @Override + public void onResponse(DeletePrivilegesResponse deletePrivilegesResponse) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + //end::delete-privileges-execute-listener + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + //tag::delete-privileges-execute-async + client.security().deletePrivilegesAsync(deletePrivilegesRequest, RequestOptions.DEFAULT, listener); // <1> + //end::delete-privileges-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } } diff --git a/docs/java-rest/high-level/security/delete-privileges.asciidoc b/docs/java-rest/high-level/security/delete-privileges.asciidoc new file mode 100644 index 0000000000000..4a8c09c90dca0 --- /dev/null +++ b/docs/java-rest/high-level/security/delete-privileges.asciidoc @@ -0,0 +1,37 @@ +-- +:api: delete-privileges +:request: DeletePrivilegesResponse +:response: DeletePrivilegesResponse +-- + +[id="{upid}-{api}"] +=== Delete Privileges API + +This API can be used to delete application privileges. + +[id="{upid}-{api}-request"] +==== Delete Application Privileges Request + +A +{request}+ has two arguments + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-request] +-------------------------------------------------- +<1> the name of application +<2> the name(s) of the privileges to delete that belong to the given application + +include::../execution.asciidoc[] + +[id="{upid}-{api}-response"] +==== Delete Application Privileges Response + +The returned +{response}+ allows to retrieve information about the executed + operation as follows: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-response] +-------------------------------------------------- +<1> the name of the application +<2> whether the given privilege was found and deleted diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index 7deb214d29628..f036652e9e722 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -351,12 +351,14 @@ The Java High Level REST Client supports the following Security APIs: * <> * <> * <<{upid}-invalidate-token>> +* <> include::security/put-user.asciidoc[] include::security/enable-user.asciidoc[] include::security/disable-user.asciidoc[] include::security/change-password.asciidoc[] include::security/delete-role.asciidoc[] +include::security/delete-privileges.asciidoc[] include::security/clear-roles-cache.asciidoc[] include::security/clear-realm-cache.asciidoc[] include::security/authenticate.asciidoc[] From 93a78f5fdc0746cb313bc50ef9931d7f8de3c190 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Mon, 12 Nov 2018 16:09:25 +0100 Subject: [PATCH 2/5] delete application privilege call --- .../src/main/java/org/elasticsearch/client/SecurityClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8bde40acbe867..36ba9f1b97a1b 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 @@ -481,7 +481,7 @@ public void invalidateTokenAsync(InvalidateTokenRequest request, RequestOptions * the docs for more. * @param request the request with the application privilege to delete * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized - * @return the response from the delete role call + * @return the response from the delete application privilege call * @throws IOException in case there is a problem sending the request or parsing back the response */ public DeletePrivilegesResponse deletePrivileges(DeletePrivilegesRequest request, RequestOptions options) throws IOException { From b054f95437701320aa1705354f23c970ed53321b Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Mon, 12 Nov 2018 16:10:04 +0100 Subject: [PATCH 3/5] Fix link --- .../src/main/java/org/elasticsearch/client/SecurityClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 36ba9f1b97a1b..f8a82ef082f0f 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 @@ -491,7 +491,7 @@ public DeletePrivilegesResponse deletePrivileges(DeletePrivilegesRequest request /** * Asynchronously removes an application privilege - * See + * See * the docs for more. * @param request the request with the application privilege to delete * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized From c2c3796b71103d29d194a0b6dc8c285fafe4b357 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Tue, 13 Nov 2018 09:28:52 +0100 Subject: [PATCH 4/5] Apply feedback --- .../elasticsearch/client/SecurityClient.java | 2 +- .../security/DeletePrivilegesRequest.java | 34 ++++++++++--- .../security/DeletePrivilegesResponse.java | 50 ++++++++++--------- .../security/delete-privileges.asciidoc | 2 +- .../high-level/supported-apis.asciidoc | 2 +- 5 files changed, 57 insertions(+), 33 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 f8a82ef082f0f..68bb9b9a28b99 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 @@ -476,7 +476,7 @@ public void invalidateTokenAsync(InvalidateTokenRequest request, RequestOptions } /** - * Removes an application privilege + * Removes application privilege(s) * See * the docs for more. * @param request the request with the application privilege to delete diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesRequest.java index 2a95f44019f78..7ea416fc339c3 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesRequest.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesRequest.java @@ -20,8 +20,9 @@ package org.elasticsearch.client.security; import org.elasticsearch.client.Validatable; - -import java.util.Objects; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.util.CollectionUtils; /** * A request to delete application privileges @@ -32,14 +33,33 @@ public final class DeletePrivilegesRequest implements Validatable { private final String[] privileges; private final RefreshPolicy refreshPolicy; + /** + * Creates a new {@link DeletePrivilegesRequest} using the default {@link RefreshPolicy#getDefault()} refresh policy. + * + * @param application the name of the application for which the privileges will be deleted + * @param privileges the privileges to delete + */ public DeletePrivilegesRequest(String application, String... privileges) { - this(application, privileges, RefreshPolicy.IMMEDIATE); + this(application, privileges, null); } - public DeletePrivilegesRequest(String application, String[] privileges, RefreshPolicy refreshPolicy) { - this.application = Objects.requireNonNull(application, "application is required"); - this.privileges = Objects.requireNonNull(privileges, "privileges are required"); - this.refreshPolicy = Objects.requireNonNull(refreshPolicy, "refresh policy is required"); + /** + * Creates a new {@link DeletePrivilegesRequest}. + * + * @param application the name of the application for which the privileges will be deleted + * @param privileges the privileges to delete + * @param refreshPolicy the refresh policy {@link RefreshPolicy} for the request, defaults to {@link RefreshPolicy#getDefault()} + */ + public DeletePrivilegesRequest(String application, String[] privileges, @Nullable RefreshPolicy refreshPolicy) { + if (Strings.hasText(application) == false) { + throw new IllegalArgumentException("application name is required"); + } + if (CollectionUtils.isEmpty(privileges)) { + throw new IllegalArgumentException("privileges are required"); + } + this.application = application; + this.privileges = privileges; + this.refreshPolicy = (refreshPolicy == null) ? RefreshPolicy.getDefault() : refreshPolicy; } public String getApplication() { diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesResponse.java index 6a2f857c37dec..1319a09c13312 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesResponse.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesResponse.java @@ -19,7 +19,6 @@ package org.elasticsearch.client.security; -import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; @@ -27,6 +26,8 @@ import java.util.List; import java.util.Objects; +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; + /** * Response for application privileges deletion */ @@ -44,6 +45,12 @@ public String getApplication() { return application; } + /** + * Indicates if the given privilege was successfully found and deleted from the list of application privileges. + * + * @param privilege the privilege + * @return true if the privilege was found and deleted, false otherwise. + */ public boolean isFound(final String privilege) { return privileges.contains(privilege); } @@ -53,35 +60,32 @@ public static DeletePrivilegesResponse fromXContent(XContentParser parser) throw if (token == null) { token = parser.nextToken(); } + ensureExpectedToken(token, XContentParser.Token.START_OBJECT, parser::getTokenLocation); + token = parser.nextToken(); + ensureExpectedToken(token, XContentParser.Token.FIELD_NAME, parser::getTokenLocation); + final String application = parser.currentName(); + final List foundAndDeletedPrivileges = new ArrayList<>(); + token = parser.nextToken(); if (token == XContentParser.Token.START_OBJECT) { - token = parser.nextToken(); - if (token == XContentParser.Token.FIELD_NAME) { - final String application = parser.currentName(); - final List found = new ArrayList<>(); - token = parser.nextToken(); - if (token == XContentParser.Token.START_OBJECT) { - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - String privilege = parser.currentName(); - token = parser.nextToken(); - if (token == XContentParser.Token.START_OBJECT) { - String currentFieldName = null; - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_BOOLEAN) { - if ("found".equals(currentFieldName) && parser.booleanValue()) { - found.add(privilege); - } - } + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + String privilege = parser.currentName(); + token = parser.nextToken(); + if (token == XContentParser.Token.START_OBJECT) { + String currentFieldName = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token == XContentParser.Token.VALUE_BOOLEAN) { + if ("found".equals(currentFieldName) && parser.booleanValue()) { + foundAndDeletedPrivileges.add(privilege); } } } } } - return new DeletePrivilegesResponse(application, found); } } - throw new ElasticsearchParseException("Unable to parse DeletePrivilegesResponse"); + return new DeletePrivilegesResponse(application, foundAndDeletedPrivileges); } } diff --git a/docs/java-rest/high-level/security/delete-privileges.asciidoc b/docs/java-rest/high-level/security/delete-privileges.asciidoc index 4a8c09c90dca0..7f32d75107b97 100644 --- a/docs/java-rest/high-level/security/delete-privileges.asciidoc +++ b/docs/java-rest/high-level/security/delete-privileges.asciidoc @@ -1,6 +1,6 @@ -- :api: delete-privileges -:request: DeletePrivilegesResponse +:request: DeletePrivilegesRequest :response: DeletePrivilegesResponse -- diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index f036652e9e722..8ec6ac7a31164 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -351,7 +351,7 @@ The Java High Level REST Client supports the following Security APIs: * <> * <> * <<{upid}-invalidate-token>> -* <> +* <<{upid}-delete-privileges>> include::security/put-user.asciidoc[] include::security/enable-user.asciidoc[] From e33ac102f1140bc1556baa38739dec60b376e87a Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Wed, 14 Nov 2018 10:23:06 +0100 Subject: [PATCH 5/5] Fix ensureExpectedToken() --- .../client/security/DeletePrivilegesResponse.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesResponse.java index 1319a09c13312..fd6e30df10544 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesResponse.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/DeletePrivilegesResponse.java @@ -60,9 +60,9 @@ public static DeletePrivilegesResponse fromXContent(XContentParser parser) throw if (token == null) { token = parser.nextToken(); } - ensureExpectedToken(token, XContentParser.Token.START_OBJECT, parser::getTokenLocation); + ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser::getTokenLocation); token = parser.nextToken(); - ensureExpectedToken(token, XContentParser.Token.FIELD_NAME, parser::getTokenLocation); + ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation); final String application = parser.currentName(); final List foundAndDeletedPrivileges = new ArrayList<>(); token = parser.nextToken();