Skip to content

Commit 5b7446b

Browse files
authored
Add Delete Privileges API to HLRC (#35454)
This commit adds the Delete Privileges API to the high level REST client. Related to #29827
1 parent e7b7d52 commit 5b7446b

File tree

8 files changed

+352
-9
lines changed

8 files changed

+352
-9
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java

+33-3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import org.elasticsearch.client.security.ClearRolesCacheResponse;
3030
import org.elasticsearch.client.security.CreateTokenRequest;
3131
import org.elasticsearch.client.security.CreateTokenResponse;
32+
import org.elasticsearch.client.security.DeletePrivilegesRequest;
33+
import org.elasticsearch.client.security.DeletePrivilegesResponse;
3234
import org.elasticsearch.client.security.DeleteRoleMappingRequest;
3335
import org.elasticsearch.client.security.DeleteRoleMappingResponse;
3436
import org.elasticsearch.client.security.DeleteRoleRequest;
@@ -221,7 +223,7 @@ public void disableUserAsync(DisableUserRequest request, RequestOptions options,
221223
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-authenticate.html">
222224
* the docs</a> for more.
223225
*
224-
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
226+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
225227
* @return the responsee from the authenticate user call
226228
*/
227229
public AuthenticateResponse authenticate(RequestOptions options) throws IOException {
@@ -234,8 +236,8 @@ public AuthenticateResponse authenticate(RequestOptions options) throws IOExcept
234236
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-authenticate.html">
235237
* the docs</a> for more.
236238
*
237-
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
238-
* @param listener the listener to be notified upon request completion
239+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
240+
* @param listener the listener to be notified upon request completion
239241
*/
240242
public void authenticateAsync(RequestOptions options, ActionListener<AuthenticateResponse> listener) {
241243
restHighLevelClient.performRequestAsyncAndParseEntity(AuthenticateRequest.INSTANCE, AuthenticateRequest::getRequest, options,
@@ -473,4 +475,32 @@ public void invalidateTokenAsync(InvalidateTokenRequest request, RequestOptions
473475
InvalidateTokenResponse::fromXContent, listener, emptySet());
474476
}
475477

478+
/**
479+
* Removes application privilege(s)
480+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-delete-privilege.html">
481+
* the docs</a> for more.
482+
* @param request the request with the application privilege to delete
483+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
484+
* @return the response from the delete application privilege call
485+
* @throws IOException in case there is a problem sending the request or parsing back the response
486+
*/
487+
public DeletePrivilegesResponse deletePrivileges(DeletePrivilegesRequest request, RequestOptions options) throws IOException {
488+
return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::deletePrivileges, options,
489+
DeletePrivilegesResponse::fromXContent, singleton(404));
490+
}
491+
492+
/**
493+
* Asynchronously removes an application privilege
494+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-delete-privilege.html">
495+
* the docs</a> for more.
496+
* @param request the request with the application privilege to delete
497+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
498+
* @param listener the listener to be notified upon request completion
499+
*/
500+
public void deletePrivilegesAsync(DeletePrivilegesRequest request, RequestOptions options,
501+
ActionListener<DeletePrivilegesResponse> listener) {
502+
restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::deletePrivileges, options,
503+
DeletePrivilegesResponse::fromXContent, listener, singleton(404));
504+
}
505+
476506
}

client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityRequestConverters.java

+17-4
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,22 @@
1919

2020
package org.elasticsearch.client;
2121

22-
import org.apache.http.client.methods.HttpGet;
2322
import org.apache.http.client.methods.HttpDelete;
23+
import org.apache.http.client.methods.HttpGet;
2424
import org.apache.http.client.methods.HttpPost;
2525
import org.apache.http.client.methods.HttpPut;
26+
import org.elasticsearch.client.security.ChangePasswordRequest;
2627
import org.elasticsearch.client.security.ClearRealmCacheRequest;
2728
import org.elasticsearch.client.security.ClearRolesCacheRequest;
2829
import org.elasticsearch.client.security.CreateTokenRequest;
30+
import org.elasticsearch.client.security.DeletePrivilegesRequest;
2931
import org.elasticsearch.client.security.DeleteRoleMappingRequest;
3032
import org.elasticsearch.client.security.DeleteRoleRequest;
31-
import org.elasticsearch.client.security.InvalidateTokenRequest;
32-
import org.elasticsearch.client.security.PutRoleMappingRequest;
3333
import org.elasticsearch.client.security.DisableUserRequest;
3434
import org.elasticsearch.client.security.EnableUserRequest;
3535
import org.elasticsearch.client.security.GetRoleMappingsRequest;
36-
import org.elasticsearch.client.security.ChangePasswordRequest;
36+
import org.elasticsearch.client.security.InvalidateTokenRequest;
37+
import org.elasticsearch.client.security.PutRoleMappingRequest;
3738
import org.elasticsearch.client.security.PutUserRequest;
3839
import org.elasticsearch.client.security.SetUserEnabledRequest;
3940
import org.elasticsearch.common.Strings;
@@ -172,4 +173,16 @@ static Request invalidateToken(InvalidateTokenRequest invalidateTokenRequest) th
172173
request.setEntity(createEntity(invalidateTokenRequest, REQUEST_BODY_CONTENT_TYPE));
173174
return request;
174175
}
176+
177+
static Request deletePrivileges(DeletePrivilegesRequest deletePrivilegeRequest) {
178+
String endpoint = new RequestConverters.EndpointBuilder()
179+
.addPathPartAsIs("_xpack/security/privilege")
180+
.addPathPart(deletePrivilegeRequest.getApplication())
181+
.addCommaSeparatedPathParts(deletePrivilegeRequest.getPrivileges())
182+
.build();
183+
Request request = new Request(HttpDelete.METHOD_NAME, endpoint);
184+
RequestConverters.Params params = new RequestConverters.Params(request);
185+
params.withRefreshPolicy(deletePrivilegeRequest.getRefreshPolicy());
186+
return request;
187+
}
175188
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.client.security;
21+
22+
import org.elasticsearch.client.Validatable;
23+
import org.elasticsearch.common.Nullable;
24+
import org.elasticsearch.common.Strings;
25+
import org.elasticsearch.common.util.CollectionUtils;
26+
27+
/**
28+
* A request to delete application privileges
29+
*/
30+
public final class DeletePrivilegesRequest implements Validatable {
31+
32+
private final String application;
33+
private final String[] privileges;
34+
private final RefreshPolicy refreshPolicy;
35+
36+
/**
37+
* Creates a new {@link DeletePrivilegesRequest} using the default {@link RefreshPolicy#getDefault()} refresh policy.
38+
*
39+
* @param application the name of the application for which the privileges will be deleted
40+
* @param privileges the privileges to delete
41+
*/
42+
public DeletePrivilegesRequest(String application, String... privileges) {
43+
this(application, privileges, null);
44+
}
45+
46+
/**
47+
* Creates a new {@link DeletePrivilegesRequest}.
48+
*
49+
* @param application the name of the application for which the privileges will be deleted
50+
* @param privileges the privileges to delete
51+
* @param refreshPolicy the refresh policy {@link RefreshPolicy} for the request, defaults to {@link RefreshPolicy#getDefault()}
52+
*/
53+
public DeletePrivilegesRequest(String application, String[] privileges, @Nullable RefreshPolicy refreshPolicy) {
54+
if (Strings.hasText(application) == false) {
55+
throw new IllegalArgumentException("application name is required");
56+
}
57+
if (CollectionUtils.isEmpty(privileges)) {
58+
throw new IllegalArgumentException("privileges are required");
59+
}
60+
this.application = application;
61+
this.privileges = privileges;
62+
this.refreshPolicy = (refreshPolicy == null) ? RefreshPolicy.getDefault() : refreshPolicy;
63+
}
64+
65+
public String getApplication() {
66+
return application;
67+
}
68+
69+
public String[] getPrivileges() {
70+
return privileges;
71+
}
72+
73+
public RefreshPolicy getRefreshPolicy() {
74+
return refreshPolicy;
75+
}
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.client.security;
21+
22+
import org.elasticsearch.common.xcontent.XContentParser;
23+
24+
import java.io.IOException;
25+
import java.util.ArrayList;
26+
import java.util.List;
27+
import java.util.Objects;
28+
29+
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
30+
31+
/**
32+
* Response for application privileges deletion
33+
*/
34+
public final class DeletePrivilegesResponse {
35+
36+
private final String application;
37+
private final List<String> privileges;
38+
39+
DeletePrivilegesResponse(String application, List<String> privileges) {
40+
this.application = Objects.requireNonNull(application, "application is required");
41+
this.privileges = Objects.requireNonNull(privileges, "privileges are required");
42+
}
43+
44+
public String getApplication() {
45+
return application;
46+
}
47+
48+
/**
49+
* Indicates if the given privilege was successfully found and deleted from the list of application privileges.
50+
*
51+
* @param privilege the privilege
52+
* @return true if the privilege was found and deleted, false otherwise.
53+
*/
54+
public boolean isFound(final String privilege) {
55+
return privileges.contains(privilege);
56+
}
57+
58+
public static DeletePrivilegesResponse fromXContent(XContentParser parser) throws IOException {
59+
XContentParser.Token token = parser.currentToken();
60+
if (token == null) {
61+
token = parser.nextToken();
62+
}
63+
ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser::getTokenLocation);
64+
token = parser.nextToken();
65+
ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation);
66+
final String application = parser.currentName();
67+
final List<String> foundAndDeletedPrivileges = new ArrayList<>();
68+
token = parser.nextToken();
69+
if (token == XContentParser.Token.START_OBJECT) {
70+
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
71+
if (token == XContentParser.Token.FIELD_NAME) {
72+
String privilege = parser.currentName();
73+
token = parser.nextToken();
74+
if (token == XContentParser.Token.START_OBJECT) {
75+
String currentFieldName = null;
76+
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
77+
if (token == XContentParser.Token.FIELD_NAME) {
78+
currentFieldName = parser.currentName();
79+
} else if (token == XContentParser.Token.VALUE_BOOLEAN) {
80+
if ("found".equals(currentFieldName) && parser.booleanValue()) {
81+
foundAndDeletedPrivileges.add(privilege);
82+
}
83+
}
84+
}
85+
}
86+
}
87+
}
88+
}
89+
return new DeletePrivilegesResponse(application, foundAndDeletedPrivileges);
90+
}
91+
}

client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java

+16
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.http.client.methods.HttpPost;
2525
import org.apache.http.client.methods.HttpPut;
2626
import org.elasticsearch.client.security.CreateTokenRequest;
27+
import org.elasticsearch.client.security.DeletePrivilegesRequest;
2728
import org.elasticsearch.client.security.DeleteRoleMappingRequest;
2829
import org.elasticsearch.client.security.DeleteRoleRequest;
2930
import org.elasticsearch.client.security.DisableUserRequest;
@@ -241,4 +242,19 @@ public void testCreateTokenWithClientCredentialsGrant() throws Exception {
241242
assertEquals(0, request.getParameters().size());
242243
assertToXContentBody(createTokenRequest, request.getEntity());
243244
}
245+
246+
public void testDeletePrivileges() {
247+
final String application = randomAlphaOfLengthBetween(1, 12);
248+
final List<String> privileges = randomSubsetOf(randomIntBetween(1, 3), "read", "write", "all");
249+
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
250+
final Map<String, String> expectedParams = getExpectedParamsFromRefreshPolicy(refreshPolicy);
251+
DeletePrivilegesRequest deletePrivilegesRequest =
252+
new DeletePrivilegesRequest(application, privileges.toArray(Strings.EMPTY_ARRAY), refreshPolicy);
253+
Request request = SecurityRequestConverters.deletePrivileges(deletePrivilegesRequest);
254+
assertEquals(HttpDelete.METHOD_NAME, request.getMethod());
255+
assertEquals("/_xpack/security/privilege/" + application + "/" + Strings.collectionToCommaDelimitedString(privileges),
256+
request.getEndpoint());
257+
assertEquals(expectedParams, request.getParameters());
258+
assertNull(request.getEntity());
259+
}
244260
}

0 commit comments

Comments
 (0)