[HLRC] Add support for put privileges API (#35679)

This commit adds support for API to create or update
application privileges in high-level rest client.
This commit is contained in:
Yogesh Gaikwad 2018-12-09 16:03:28 +11:00 committed by GitHub
parent 902d6f579a
commit 32c4f99238
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 725 additions and 68 deletions

View File

@ -52,6 +52,8 @@ import org.elasticsearch.client.security.HasPrivilegesRequest;
import org.elasticsearch.client.security.HasPrivilegesResponse;
import org.elasticsearch.client.security.InvalidateTokenRequest;
import org.elasticsearch.client.security.InvalidateTokenResponse;
import org.elasticsearch.client.security.PutPrivilegesRequest;
import org.elasticsearch.client.security.PutPrivilegesResponse;
import org.elasticsearch.client.security.PutRoleMappingRequest;
import org.elasticsearch.client.security.PutRoleMappingResponse;
import org.elasticsearch.client.security.PutUserRequest;
@ -603,6 +605,38 @@ public final class SecurityClient {
options, GetPrivilegesResponse::fromXContent, listener, emptySet());
}
/**
* Create or update application privileges.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-put-privileges.html">
* the docs</a> for more.
*
* @param request the request to create or update application privileges
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response from the create or update application privileges call
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public PutPrivilegesResponse putPrivileges(final PutPrivilegesRequest request, final RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::putPrivileges, options,
PutPrivilegesResponse::fromXContent, emptySet());
}
/**
* Asynchronously create or update application privileges.<br>
* See <a href=
* "https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-put-privileges.html">
* the docs</a> for more.
*
* @param request the request to create or update application privileges
* @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 putPrivilegesAsync(final PutPrivilegesRequest request, final RequestOptions options,
final ActionListener<PutPrivilegesResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::putPrivileges, options,
PutPrivilegesResponse::fromXContent, listener, emptySet());
}
/**
* Removes application privilege(s)
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-delete-privilege.html">

View File

@ -28,17 +28,18 @@ 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.GetPrivilegesRequest;
import org.elasticsearch.client.security.DeleteRoleMappingRequest;
import org.elasticsearch.client.security.DeleteRoleRequest;
import org.elasticsearch.client.security.DeleteUserRequest;
import org.elasticsearch.client.security.InvalidateTokenRequest;
import org.elasticsearch.client.security.GetRolesRequest;
import org.elasticsearch.client.security.PutRoleMappingRequest;
import org.elasticsearch.client.security.HasPrivilegesRequest;
import org.elasticsearch.client.security.DisableUserRequest;
import org.elasticsearch.client.security.EnableUserRequest;
import org.elasticsearch.client.security.GetPrivilegesRequest;
import org.elasticsearch.client.security.GetRoleMappingsRequest;
import org.elasticsearch.client.security.GetRolesRequest;
import org.elasticsearch.client.security.HasPrivilegesRequest;
import org.elasticsearch.client.security.InvalidateTokenRequest;
import org.elasticsearch.client.security.PutPrivilegesRequest;
import org.elasticsearch.client.security.PutRoleMappingRequest;
import org.elasticsearch.client.security.PutUserRequest;
import org.elasticsearch.client.security.SetUserEnabledRequest;
import org.elasticsearch.common.Strings;
@ -213,6 +214,14 @@ final class SecurityRequestConverters {
return new Request(HttpGet.METHOD_NAME, endpoint);
}
static Request putPrivileges(final PutPrivilegesRequest putPrivilegesRequest) throws IOException {
Request request = new Request(HttpPut.METHOD_NAME, "/_xpack/security/privilege");
request.setEntity(createEntity(putPrivilegesRequest, REQUEST_BODY_CONTENT_TYPE));
RequestConverters.Params params = new RequestConverters.Params(request);
params.withRefreshPolicy(putPrivilegesRequest.getRefreshPolicy());
return request;
}
static Request deletePrivileges(DeletePrivilegesRequest deletePrivilegeRequest) {
String endpoint = new RequestConverters.EndpointBuilder()
.addPathPartAsIs("_xpack/security/privilege")

View File

@ -0,0 +1,98 @@
/*
* 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.client.security.user.privileges.ApplicationPrivilege;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.TreeMap;
import java.util.stream.Collectors;
/**
* Request object for creating/updating application privileges.
*/
public final class PutPrivilegesRequest implements Validatable, ToXContentObject {
private final Map<String, List<ApplicationPrivilege>> privileges;
private final RefreshPolicy refreshPolicy;
public PutPrivilegesRequest(final List<ApplicationPrivilege> privileges, @Nullable final RefreshPolicy refreshPolicy) {
if (privileges == null || privileges.isEmpty()) {
throw new IllegalArgumentException("privileges are required");
}
this.privileges = Collections.unmodifiableMap(privileges.stream()
.collect(Collectors.groupingBy(ApplicationPrivilege::getApplication, TreeMap::new, Collectors.toList())));
this.refreshPolicy = refreshPolicy == null ? RefreshPolicy.IMMEDIATE : refreshPolicy;
}
/**
* @return a map of application name to list of
* {@link ApplicationPrivilege}s
*/
public Map<String, List<ApplicationPrivilege>> getPrivileges() {
return privileges;
}
public RefreshPolicy getRefreshPolicy() {
return refreshPolicy;
}
@Override
public int hashCode() {
return Objects.hash(privileges, refreshPolicy);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || (this.getClass() != o.getClass())) {
return false;
}
final PutPrivilegesRequest that = (PutPrivilegesRequest) o;
return privileges.equals(that.privileges) && (refreshPolicy == that.refreshPolicy);
}
@Override
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
builder.startObject();
for (Entry<String, List<ApplicationPrivilege>> entry : privileges.entrySet()) {
builder.field(entry.getKey());
builder.startObject();
for (ApplicationPrivilege applicationPrivilege : entry.getValue()) {
builder.field(applicationPrivilege.getName());
applicationPrivilege.toXContent(builder, params);
}
builder.endObject();
}
return builder.endObject();
}
}

View File

@ -0,0 +1,105 @@
/*
* 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.common.ParsingException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* Response when creating/updating one or more application privileges to the
* security index.
*/
public final class PutPrivilegesResponse {
/*
* Map of application name to a map of privilege name to boolean denoting
* created or update status.
*/
private final Map<String, Map<String, Boolean>> applicationPrivilegesCreatedOrUpdated;
public PutPrivilegesResponse(final Map<String, Map<String, Boolean>> applicationPrivilegesCreatedOrUpdated) {
this.applicationPrivilegesCreatedOrUpdated = Collections.unmodifiableMap(applicationPrivilegesCreatedOrUpdated);
}
/**
* Get response status for the request to create or update application
* privileges.
*
* @param applicationName application name as specified in the request
* @param privilegeName privilege name as specified in the request
* @return {@code true} if the privilege was created, {@code false} if the
* privilege was updated
* @throws IllegalArgumentException thrown for unknown application name or
* privilege name.
*/
public boolean wasCreated(final String applicationName, final String privilegeName) {
if (Strings.hasText(applicationName) == false) {
throw new IllegalArgumentException("application name is required");
}
if (Strings.hasText(privilegeName) == false) {
throw new IllegalArgumentException("privilege name is required");
}
if (applicationPrivilegesCreatedOrUpdated.get(applicationName) == null
|| applicationPrivilegesCreatedOrUpdated.get(applicationName).get(privilegeName) == null) {
throw new IllegalArgumentException("application name or privilege name not found in the response");
}
return applicationPrivilegesCreatedOrUpdated.get(applicationName).get(privilegeName);
}
@SuppressWarnings("unchecked")
public static PutPrivilegesResponse fromXContent(final XContentParser parser) throws IOException {
final Map<String, Map<String, Boolean>> applicationPrivilegesCreatedOrUpdated = new HashMap<>();
XContentParser.Token token = parser.currentToken();
if (token == null) {
token = parser.nextToken();
}
final Map<String, Object> appNameToPrivStatus = parser.map();
for (Entry<String, Object> entry : appNameToPrivStatus.entrySet()) {
if (entry.getValue() instanceof Map) {
final Map<String, Boolean> privilegeToStatus = applicationPrivilegesCreatedOrUpdated.computeIfAbsent(entry.getKey(),
(a) -> new HashMap<>());
final Map<String, Object> createdOrUpdated = (Map<String, Object>) entry.getValue();
for (String privilegeName : createdOrUpdated.keySet()) {
if (createdOrUpdated.get(privilegeName) instanceof Map) {
final Map<String, Object> statusMap = (Map<String, Object>) createdOrUpdated.get(privilegeName);
final Object status = statusMap.get("created");
if (status instanceof Boolean) {
privilegeToStatus.put(privilegeName, (Boolean) status);
} else {
throw new ParsingException(parser.getTokenLocation(), "Failed to parse object, unexpected structure");
}
} else {
throw new ParsingException(parser.getTokenLocation(), "Failed to parse object, unexpected structure");
}
}
} else {
throw new ParsingException(parser.getTokenLocation(), "Failed to parse object, unexpected structure");
}
}
return new PutPrivilegesResponse(applicationPrivilegesCreatedOrUpdated);
}
}

View File

@ -24,6 +24,8 @@ import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
@ -44,7 +46,7 @@ import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optiona
* actions and metadata are completely managed by the client and can contain arbitrary
* string values.
*/
public final class ApplicationPrivilege {
public final class ApplicationPrivilege implements ToXContentObject {
private static final ParseField APPLICATION = new ParseField("application");
private static final ParseField NAME = new ParseField("name");
@ -171,4 +173,16 @@ public final class ApplicationPrivilege {
}
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject()
.field(APPLICATION.getPreferredName(), application)
.field(NAME.getPreferredName(), name)
.field(ACTIONS.getPreferredName(), actions);
if (metadata != null && metadata.isEmpty() == false) {
builder.field(METADATA.getPreferredName(), metadata);
}
return builder.endObject();
}
}

View File

@ -19,10 +19,11 @@
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.CreateTokenRequest;
import org.elasticsearch.client.security.DeletePrivilegesRequest;
import org.elasticsearch.client.security.DeleteRoleMappingRequest;
@ -32,8 +33,8 @@ import org.elasticsearch.client.security.DisableUserRequest;
import org.elasticsearch.client.security.EnableUserRequest;
import org.elasticsearch.client.security.GetPrivilegesRequest;
import org.elasticsearch.client.security.GetRoleMappingsRequest;
import org.elasticsearch.client.security.ChangePasswordRequest;
import org.elasticsearch.client.security.GetRolesRequest;
import org.elasticsearch.client.security.PutPrivilegesRequest;
import org.elasticsearch.client.security.PutRoleMappingRequest;
import org.elasticsearch.client.security.PutUserRequest;
import org.elasticsearch.client.security.RefreshPolicy;
@ -41,10 +42,13 @@ import org.elasticsearch.client.security.support.expressiondsl.RoleMapperExpress
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.user.privileges.ApplicationPrivilege;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@ -318,6 +322,27 @@ public class SecurityRequestConvertersTests extends ESTestCase {
assertNull(request.getEntity());
}
public void testPutPrivileges() throws Exception {
int noOfApplicationPrivileges = randomIntBetween(2, 4);
final List<ApplicationPrivilege> privileges = new ArrayList<>();
for (int count = 0; count < noOfApplicationPrivileges; count++) {
privileges.add(ApplicationPrivilege.builder()
.application(randomAlphaOfLength(4))
.privilege(randomAlphaOfLengthBetween(3, 5))
.actions(Sets.newHashSet(generateRandomStringArray(3, 5, false, false)))
.metadata(Collections.singletonMap("k1", "v1"))
.build());
}
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
final Map<String, String> expectedParams = getExpectedParamsFromRefreshPolicy(refreshPolicy);
final PutPrivilegesRequest putPrivilegesRequest = new PutPrivilegesRequest(privileges, refreshPolicy);
final Request request = SecurityRequestConverters.putPrivileges(putPrivilegesRequest);
assertEquals(HttpPut.METHOD_NAME, request.getMethod());
assertEquals("/_xpack/security/privilege", request.getEndpoint());
assertEquals(expectedParams, request.getParameters());
assertToXContentBody(putPrivilegesRequest, request.getEntity());
}
public void testDeletePrivileges() {
final String application = randomAlphaOfLengthBetween(1, 12);
final List<String> privileges = randomSubsetOf(randomIntBetween(1, 3), "read", "write", "all");

View File

@ -29,7 +29,6 @@ import org.elasticsearch.action.support.PlainActionFuture;
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;
@ -62,6 +61,8 @@ import org.elasticsearch.client.security.HasPrivilegesRequest;
import org.elasticsearch.client.security.HasPrivilegesResponse;
import org.elasticsearch.client.security.InvalidateTokenRequest;
import org.elasticsearch.client.security.InvalidateTokenResponse;
import org.elasticsearch.client.security.PutPrivilegesRequest;
import org.elasticsearch.client.security.PutPrivilegesResponse;
import org.elasticsearch.client.security.PutRoleMappingRequest;
import org.elasticsearch.client.security.PutRoleMappingResponse;
import org.elasticsearch.client.security.PutUserRequest;
@ -78,7 +79,6 @@ import org.elasticsearch.client.security.user.privileges.IndicesPrivileges;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.RestStatus;
import org.hamcrest.Matchers;
import javax.crypto.SecretKeyFactory;
@ -86,6 +86,7 @@ import javax.crypto.spec.PBEKeySpec;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@ -1205,36 +1206,23 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
new ApplicationPrivilege("testapp2", "all", Arrays.asList("action:login", "data:write/*", "manage:*"), null);
{
//TODO Replace this with a call to PutPrivileges once it is implemented
final Request createPrivilegeRequest = new Request("POST", "/_xpack/security/privilege");
createPrivilegeRequest.setJsonEntity("{" +
" \"testapp\": {" +
" \"read\": {" +
" \"actions\": [ \"action:login\", \"data:read/*\" ]" +
" }," +
" \"write\": {" +
" \"actions\": [ \"action:login\", \"data:write/*\" ]," +
" \"metadata\": { \"key1\": \"value1\" }" +
" }," +
" \"all\": {" +
" \"actions\": [ \"action:login\", \"data:write/*\" , \"manage:*\"]" +
" }" +
" }," +
" \"testapp2\": {" +
" \"read\": {" +
" \"actions\": [ \"action:login\", \"data:read/*\" ]," +
" \"metadata\": { \"key2\": \"value2\" }" +
" }," +
" \"write\": {" +
" \"actions\": [ \"action:login\", \"data:write/*\" ]" +
" }," +
" \"all\": {" +
" \"actions\": [ \"action:login\", \"data:write/*\" , \"manage:*\"]" +
" }" +
" }" +
"}");
final Response createPrivilegeResponse = client.getLowLevelClient().performRequest(createPrivilegeRequest);
assertEquals(RestStatus.OK.getStatus(), createPrivilegeResponse.getStatusLine().getStatusCode());
List<ApplicationPrivilege> applicationPrivileges = new ArrayList<>();
applicationPrivileges.add(readTestappPrivilege);
applicationPrivileges.add(writeTestappPrivilege);
applicationPrivileges.add(allTestappPrivilege);
applicationPrivileges.add(readTestapp2Privilege);
applicationPrivileges.add(writeTestapp2Privilege);
applicationPrivileges.add(allTestapp2Privilege);
PutPrivilegesRequest putPrivilegesRequest = new PutPrivilegesRequest(applicationPrivileges, RefreshPolicy.IMMEDIATE);
PutPrivilegesResponse putPrivilegesResponse = client.security().putPrivileges(putPrivilegesRequest, RequestOptions.DEFAULT);
assertNotNull(putPrivilegesResponse);
assertThat(putPrivilegesResponse.wasCreated("testapp", "write"), is(true));
assertThat(putPrivilegesResponse.wasCreated("testapp", "read"), is(true));
assertThat(putPrivilegesResponse.wasCreated("testapp", "all"), is(true));
assertThat(putPrivilegesResponse.wasCreated("testapp2", "all"), is(true));
assertThat(putPrivilegesResponse.wasCreated("testapp2", "write"), is(true));
assertThat(putPrivilegesResponse.wasCreated("testapp2", "read"), is(true));
}
{
@ -1327,26 +1315,105 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
}
}
public void testPutPrivileges() throws Exception {
RestHighLevelClient client = highLevelClient();
{
// tag::put-privileges-request
final List<ApplicationPrivilege> privileges = new ArrayList<>();
privileges.add(ApplicationPrivilege.builder()
.application("app01")
.privilege("all")
.actions(Sets.newHashSet("action:login"))
.metadata(Collections.singletonMap("k1", "v1"))
.build());
privileges.add(ApplicationPrivilege.builder()
.application("app01")
.privilege("write")
.actions(Sets.newHashSet("action:write"))
.build());
final PutPrivilegesRequest putPrivilegesRequest = new PutPrivilegesRequest(privileges, RefreshPolicy.IMMEDIATE);
// end::put-privileges-request
// tag::put-privileges-execute
final PutPrivilegesResponse putPrivilegesResponse = client.security().putPrivileges(putPrivilegesRequest,
RequestOptions.DEFAULT);
// end::put-privileges-execute
final String applicationName = "app01";
final String privilegeName = "all";
// tag::put-privileges-response
final boolean status = putPrivilegesResponse.wasCreated(applicationName, privilegeName); // <1>
// end::put-privileges-response
assertThat(status, is(true));
}
{
final List<ApplicationPrivilege> privileges = new ArrayList<>();
privileges.add(ApplicationPrivilege.builder()
.application("app01")
.privilege("all")
.actions(Sets.newHashSet("action:login"))
.metadata(Collections.singletonMap("k1", "v1"))
.build());
final PutPrivilegesRequest putPrivilegesRequest = new PutPrivilegesRequest(privileges, RefreshPolicy.IMMEDIATE);
// tag::put-privileges-execute-listener
ActionListener<PutPrivilegesResponse> listener = new ActionListener<PutPrivilegesResponse>() {
@Override
public void onResponse(PutPrivilegesResponse response) {
// <1>
}
@Override
public void onFailure(Exception e) {
// <2>
}
};
// end::put-privileges-execute-listener
// Avoid unused variable warning
assertNotNull(listener);
// Replace the empty listener by a blocking listener in test
final PlainActionFuture<PutPrivilegesResponse> future = new PlainActionFuture<>();
listener = future;
//tag::put-privileges-execute-async
client.security().putPrivilegesAsync(putPrivilegesRequest, RequestOptions.DEFAULT, listener); // <1>
//end::put-privileges-execute-async
assertNotNull(future.get(30, TimeUnit.SECONDS));
assertThat(future.get().wasCreated("app01", "all"), is(false));
}
}
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/*\" ]" +
" }" +
" }" +
"}");
List<ApplicationPrivilege> applicationPrivileges = new ArrayList<>();
applicationPrivileges.add(ApplicationPrivilege.builder()
.application("testapp")
.privilege("read")
.actions("action:login", "data:read/*")
.build());
applicationPrivileges.add(ApplicationPrivilege.builder()
.application("testapp")
.privilege("write")
.actions("action:login", "data:write/*")
.build());
applicationPrivileges.add(ApplicationPrivilege.builder()
.application("testapp")
.privilege("all")
.actions("action:login", "data:write/*")
.build());
PutPrivilegesRequest putPrivilegesRequest = new PutPrivilegesRequest(applicationPrivileges, RefreshPolicy.IMMEDIATE);
PutPrivilegesResponse putPrivilegesResponse = client.security().putPrivileges(putPrivilegesRequest, RequestOptions.DEFAULT);
final Response createPrivilegeResponse = client.getLowLevelClient().performRequest(createPrivilegeRequest);
assertEquals(RestStatus.OK.getStatus(), createPrivilegeResponse.getStatusLine().getStatusCode());
assertNotNull(putPrivilegesResponse);
assertThat(putPrivilegesResponse.wasCreated("testapp", "write"), is(true));
assertThat(putPrivilegesResponse.wasCreated("testapp", "read"), is(true));
assertThat(putPrivilegesResponse.wasCreated("testapp", "all"), is(true));
}
{
// tag::delete-privileges-request

View File

@ -0,0 +1,170 @@
/*
* 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.privileges.ApplicationPrivilege;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.EqualsHashCodeTestUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.equalTo;
public class PutPrivilegesRequestTests extends ESTestCase {
public void testConstructor() {
final List<ApplicationPrivilege> privileges = randomFrom(
Arrays.asList(Collections.singletonList(ApplicationPrivilege.builder()
.application("app01")
.privilege("all")
.actions(Sets.newHashSet("action:login", "action:logout"))
.metadata(Collections.singletonMap("k1", "v1"))
.build()),
null, Collections.emptyList()));
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
if (privileges == null || privileges.isEmpty()) {
final IllegalArgumentException ile = expectThrows(IllegalArgumentException.class,
() -> new PutPrivilegesRequest(privileges, refreshPolicy));
assertThat(ile.getMessage(), equalTo("privileges are required"));
} else {
final PutPrivilegesRequest putPrivilegesRequest = new PutPrivilegesRequest(privileges, refreshPolicy);
assertThat(putPrivilegesRequest.getPrivileges().values().stream().flatMap(List::stream).collect(Collectors.toList()),
equalTo(privileges));
assertThat(putPrivilegesRequest.getRefreshPolicy(), equalTo(refreshPolicy));
}
}
public void testToXContent() throws IOException {
final String expected = "{\n"
+ " \"app01\" : {\n"
+ " \"all\" : {\n"
+ " \"application\" : \"app01\",\n"
+ " \"name\" : \"all\",\n"
+ " \"actions\" : [\n"
+ " \"action:logout\",\n"
+ " \"action:login\"\n"
+ " ],\n"
+ " \"metadata\" : {\n"
+ " \"k1\" : \"v1\"\n"
+ " }\n"
+ " },\n"
+ " \"read\" : {\n"
+ " \"application\" : \"app01\",\n"
+ " \"name\" : \"read\",\n"
+ " \"actions\" : [\n"
+ " \"data:read\"\n"
+ " ]\n" + " }\n"
+ " },\n"
+ " \"app02\" : {\n"
+ " \"all\" : {\n"
+ " \"application\" : \"app02\",\n"
+ " \"name\" : \"all\",\n"
+ " \"actions\" : [\n"
+ " \"action:logout\",\n"
+ " \"action:login\"\n"
+ " ],\n"
+ " \"metadata\" : {\n"
+ " \"k2\" : \"v2\"\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
List<ApplicationPrivilege> privileges = new ArrayList<>();
privileges.add(ApplicationPrivilege.builder()
.application("app01")
.privilege("all")
.actions(Sets.newHashSet("action:login", "action:logout"))
.metadata(Collections.singletonMap("k1", "v1"))
.build());
privileges.add(ApplicationPrivilege.builder()
.application("app01")
.privilege("read")
.actions(Sets.newHashSet("data:read"))
.build());
privileges.add(ApplicationPrivilege.builder()
.application("app02")
.privilege("all")
.actions(Sets.newHashSet("action:login", "action:logout"))
.metadata(Collections.singletonMap("k2", "v2"))
.build());
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
final PutPrivilegesRequest putPrivilegesRequest = new PutPrivilegesRequest(privileges, refreshPolicy);
final XContentBuilder builder = XContentFactory.jsonBuilder().prettyPrint();
assertThat(Strings.toString(putPrivilegesRequest.toXContent(builder, ToXContent.EMPTY_PARAMS)), equalTo(expected));
}
public void testEqualsHashCode() {
final List<ApplicationPrivilege> privileges = new ArrayList<>();
privileges.add(ApplicationPrivilege.builder()
.application(randomAlphaOfLength(5))
.privilege(randomAlphaOfLength(3))
.actions(Sets.newHashSet(randomAlphaOfLength(5), randomAlphaOfLength(5)))
.metadata(Collections.singletonMap(randomAlphaOfLength(3), randomAlphaOfLength(3)))
.build());
privileges.add(ApplicationPrivilege.builder()
.application(randomAlphaOfLength(5))
.privilege(randomAlphaOfLength(3))
.actions(Sets.newHashSet(randomAlphaOfLength(5), randomAlphaOfLength(5)))
.metadata(Collections.singletonMap(randomAlphaOfLength(3), randomAlphaOfLength(3)))
.build());
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
PutPrivilegesRequest putPrivilegesRequest = new PutPrivilegesRequest(privileges, refreshPolicy);
EqualsHashCodeTestUtils.checkEqualsAndHashCode(putPrivilegesRequest, (original) -> {
return new PutPrivilegesRequest(privileges, refreshPolicy);
});
EqualsHashCodeTestUtils.checkEqualsAndHashCode(putPrivilegesRequest, (original) -> {
return new PutPrivilegesRequest(original.getPrivileges().values().stream().flatMap(List::stream).collect(Collectors.toList()),
original.getRefreshPolicy());
}, PutPrivilegesRequestTests::mutateTestItem);
}
private static PutPrivilegesRequest mutateTestItem(PutPrivilegesRequest original) {
final Set<RefreshPolicy> policies = Sets.newHashSet(RefreshPolicy.values());
policies.remove(original.getRefreshPolicy());
switch (randomIntBetween(0, 1)) {
case 0:
final List<ApplicationPrivilege> privileges = new ArrayList<>();
privileges.add(ApplicationPrivilege.builder()
.application(randomAlphaOfLength(5))
.privilege(randomAlphaOfLength(3))
.actions(Sets.newHashSet(randomAlphaOfLength(6)))
.build());
return new PutPrivilegesRequest(privileges, original.getRefreshPolicy());
case 1:
return new PutPrivilegesRequest(original.getPrivileges().values().stream().flatMap(List::stream).collect(Collectors.toList()),
randomFrom(policies));
default:
return new PutPrivilegesRequest(original.getPrivileges().values().stream().flatMap(List::stream).collect(Collectors.toList()),
randomFrom(policies));
}
}
}

View File

@ -0,0 +1,86 @@
/*
* 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.common.xcontent.XContentType;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
public class PutPrivilegesResponseTests extends ESTestCase {
public void testFromXContent() throws IOException {
final String json = "{\n" +
" \"app02\": {\n" +
" \"all\": {\n" +
" \"created\": true\n" +
" }\n" +
" },\n" +
" \"app01\": {\n" +
" \"read\": {\n" +
" \"created\": false\n" +
" },\n" +
" \"write\": {\n" +
" \"created\": true\n" +
" }\n" +
" }\n" +
"}";
final PutPrivilegesResponse putPrivilegesResponse = PutPrivilegesResponse
.fromXContent(createParser(XContentType.JSON.xContent(), json));
assertThat(putPrivilegesResponse.wasCreated("app02", "all"), is(true));
assertThat(putPrivilegesResponse.wasCreated("app01", "read"), is(false));
assertThat(putPrivilegesResponse.wasCreated("app01", "write"), is(true));
expectThrows(IllegalArgumentException.class, () -> putPrivilegesResponse.wasCreated("unknown-app", "unknown-priv"));
expectThrows(IllegalArgumentException.class, () -> putPrivilegesResponse.wasCreated("app01", "unknown-priv"));
}
public void testGetStatusFailsForUnknownApplicationOrPrivilegeName() {
final PutPrivilegesResponse putPrivilegesResponse = new PutPrivilegesResponse(
Collections.singletonMap("app-1", Collections.singletonMap("priv", true)));
final boolean invalidAppName = randomBoolean();
final String applicationName = (invalidAppName) ? randomAlphaOfLength(4) : "app-1";
final String privilegeName = randomAlphaOfLength(4);
final IllegalArgumentException ile = expectThrows(IllegalArgumentException.class,
() -> putPrivilegesResponse.wasCreated(applicationName, privilegeName));
assertThat(ile.getMessage(), equalTo("application name or privilege name not found in the response"));
}
public void testGetStatusFailsForNullOrEmptyApplicationOrPrivilegeName() {
final PutPrivilegesResponse putPrivilegesResponse = new PutPrivilegesResponse(
Collections.singletonMap("app-1", Collections.singletonMap("priv", true)));
final boolean nullOrEmptyAppName = randomBoolean();
final String applicationName = (nullOrEmptyAppName) ? randomFrom(Arrays.asList("", " ", null)) : "app-1";
final String privilegeName = randomFrom(Arrays.asList("", " ", null));
final IllegalArgumentException ile = expectThrows(IllegalArgumentException.class,
() -> putPrivilegesResponse.wasCreated(applicationName, privilegeName));
assertThat(ile.getMessage(),
(nullOrEmptyAppName ? equalTo("application name is required") : equalTo("privilege name is required")));
}
}

View File

@ -19,8 +19,12 @@
package org.elasticsearch.client.security.user.privileges;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.DeprecationHandler;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.test.ESTestCase;
@ -36,19 +40,19 @@ import static org.hamcrest.Matchers.equalTo;
public class ApplicationPrivilegeTests extends ESTestCase {
public void testFromXContent() throws IOException {
public void testFromXContentAndToXContent() throws IOException {
String json =
" {" +
" \"application\": \"myapp\"," +
" \"name\": \"read\"," +
" \"actions\": [" +
" \"data:read/*\"," +
" \"action:login\"" +
" ],\n" +
" \"metadata\": {" +
" \"description\": \"Read access to myapp\"" +
" }" +
" }";
"{\n"
+ " \"application\" : \"myapp\",\n"
+ " \"name\" : \"read\",\n"
+ " \"actions\" : [\n"
+ " \"data:read/*\",\n"
+ " \"action:login\"\n"
+ " ],\n"
+ " \"metadata\" : {\n"
+ " \"description\" : \"Read access to myapp\"\n"
+ " }\n"
+ "}";
final ApplicationPrivilege privilege = ApplicationPrivilege.fromXContent(XContentType.JSON.xContent().createParser(
new NamedXContentRegistry(Collections.emptyList()), new DeprecationHandler() {
@Override
@ -64,6 +68,10 @@ public class ApplicationPrivilegeTests extends ESTestCase {
final ApplicationPrivilege expectedPrivilege =
new ApplicationPrivilege("myapp", "read", Arrays.asList("data:read/*", "action:login"), metadata);
assertThat(privilege, equalTo(expectedPrivilege));
XContentBuilder builder = privilege.toXContent(XContentFactory.jsonBuilder().prettyPrint(), ToXContent.EMPTY_PARAMS);
String toJson = Strings.toString(builder);
assertThat(toJson, equalTo(json));
}
public void testEmptyApplicationName() {

View File

@ -0,0 +1,39 @@
--
:api: put-privileges
:request: PutPrivilegesRequest
:response: PutPrivilegesResponse
--
[id="{upid}-{api}"]
=== Put Privileges API
Application privileges can be created or updated using this API.
[id="{upid}-{api}-request"]
==== Put Privileges Request
A +{request}+ contains list of application privileges that
need to be created or updated. Each application privilege
consists of an application name, application privilege,
set of actions and optional metadata.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests-file}[{api}-request]
--------------------------------------------------
include::../execution.asciidoc[]
[id="{upid}-{api}-response"]
==== Put Privileges Response
The returned +{response}+ contains the information about the status
for each privilege present in the +{request}+. The status would be
`true` if the privilege was created, `false` if the privilege was updated.
["source","java",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{doc-tests-file}[{api}-response]
--------------------------------------------------
<1> The response contains the status for given application name and
privilege name. The status would be `true` if the privilege was created,
`false` if the privilege was updated.

View File

@ -398,6 +398,7 @@ The Java High Level REST Client supports the following Security APIs:
* <<java-rest-high-security-create-token>>
* <<{upid}-invalidate-token>>
* <<{upid}-get-privileges>>
* <<{upid}-put-privileges>>
* <<{upid}-delete-privileges>>
include::security/put-user.asciidoc[]
@ -419,6 +420,7 @@ include::security/get-role-mappings.asciidoc[]
include::security/delete-role-mapping.asciidoc[]
include::security/create-token.asciidoc[]
include::security/invalidate-token.asciidoc[]
include::security/put-privileges.asciidoc[]
== Watcher APIs