Handle missing user in user privilege APIs (#34575)
For user/_has_privileges and user/_privileges, handle the case where there is no user in the security context. This is likely to indicate that the server is running with a basic license, in which case the action will be rejected with a non-compliance exception (provided we don't throw a NPE). The implementation here is based on the _authenticate API. Resolves: #34567
This commit is contained in:
parent
56d4f69718
commit
670ccfb853
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.security.rest.action.user;
|
||||
|
||||
import org.elasticsearch.ElasticsearchSecurityException;
|
||||
import org.elasticsearch.client.node.NodeClient;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
|
@ -24,6 +25,7 @@ import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
|
|||
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
|
||||
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges;
|
||||
import org.elasticsearch.xpack.core.security.client.SecurityClient;
|
||||
import org.elasticsearch.xpack.core.security.user.User;
|
||||
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -52,7 +54,11 @@ public class RestGetUserPrivilegesAction extends SecurityBaseRestHandler {
|
|||
|
||||
@Override
|
||||
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
|
||||
final String username = securityContext.getUser().principal();
|
||||
final User user = securityContext.getUser();
|
||||
if (user == null) {
|
||||
return restChannel -> { throw new ElasticsearchSecurityException("there is no authenticated user"); };
|
||||
}
|
||||
final String username = user.principal();
|
||||
final GetUserPrivilegesRequestBuilder requestBuilder = new SecurityClient(client).prepareGetUserPrivileges(username);
|
||||
return channel -> requestBuilder.execute(new RestListener(channel));
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.security.rest.action.user;
|
||||
|
||||
import org.elasticsearch.ElasticsearchSecurityException;
|
||||
import org.elasticsearch.client.node.NodeClient;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
|
@ -24,6 +25,7 @@ import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesRequestBui
|
|||
import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesResponse;
|
||||
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
|
||||
import org.elasticsearch.xpack.core.security.client.SecurityClient;
|
||||
import org.elasticsearch.xpack.core.security.user.User;
|
||||
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -59,6 +61,9 @@ public class RestHasPrivilegesAction extends SecurityBaseRestHandler {
|
|||
@Override
|
||||
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
|
||||
final String username = getUsername(request);
|
||||
if (username == null) {
|
||||
return restChannel -> { throw new ElasticsearchSecurityException("there is no authenticated user"); };
|
||||
}
|
||||
final Tuple<XContentType, BytesReference> content = request.contentOrSourceParam();
|
||||
HasPrivilegesRequestBuilder requestBuilder = new SecurityClient(client).prepareHasPrivileges(username, content.v2(), content.v1());
|
||||
return channel -> requestBuilder.execute(new HasPrivilegesRestResponseBuilder(username, channel));
|
||||
|
@ -69,7 +74,11 @@ public class RestHasPrivilegesAction extends SecurityBaseRestHandler {
|
|||
if (username != null) {
|
||||
return username;
|
||||
}
|
||||
return securityContext.getUser().principal();
|
||||
final User user = securityContext.getUser();
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
return user.principal();
|
||||
}
|
||||
|
||||
static class HasPrivilegesRestResponseBuilder extends RestBuilderListener<HasPrivilegesResponse> {
|
||||
|
|
|
@ -6,17 +6,24 @@
|
|||
|
||||
package org.elasticsearch.xpack.security.rest.action.user;
|
||||
|
||||
import org.elasticsearch.client.node.NodeClient;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.set.Sets;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.license.XPackLicenseState;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.test.rest.FakeRestChannel;
|
||||
import org.elasticsearch.test.rest.FakeRestRequest;
|
||||
import org.elasticsearch.xpack.core.security.SecurityContext;
|
||||
import org.elasticsearch.xpack.core.security.action.user.GetUserPrivilegesResponse;
|
||||
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.ApplicationResourcePrivileges;
|
||||
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsDefinition;
|
||||
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
|
||||
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges;
|
||||
import org.hamcrest.Matchers;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -24,9 +31,27 @@ import java.util.LinkedHashSet;
|
|||
import java.util.Set;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class RestGetUserPrivilegesActionTests extends ESTestCase {
|
||||
|
||||
public void testBasicLicense() throws Exception {
|
||||
final XPackLicenseState licenseState = mock(XPackLicenseState.class);
|
||||
final RestGetUserPrivilegesAction action = new RestGetUserPrivilegesAction(Settings.EMPTY, mock(RestController.class),
|
||||
mock(SecurityContext.class), licenseState);
|
||||
when(licenseState.isSecurityAvailable()).thenReturn(false);
|
||||
final FakeRestRequest request = new FakeRestRequest();
|
||||
final FakeRestChannel channel = new FakeRestChannel(request, true, 1);
|
||||
action.handleRequest(request, channel, mock(NodeClient.class));
|
||||
assertThat(channel.capturedResponse(), notNullValue());
|
||||
assertThat(channel.capturedResponse().status(), equalTo(RestStatus.FORBIDDEN));
|
||||
assertThat(channel.capturedResponse().content().utf8ToString(), containsString("current license is non-compliant for [security]"));
|
||||
}
|
||||
|
||||
public void testBuildResponse() throws Exception {
|
||||
final RestGetUserPrivilegesAction.RestListener listener = new RestGetUserPrivilegesAction.RestListener(null);
|
||||
|
||||
|
@ -37,8 +62,8 @@ public class RestGetUserPrivilegesActionTests extends ESTestCase {
|
|||
final Set<GetUserPrivilegesResponse.Indices> index = new LinkedHashSet<>(Arrays.asList(
|
||||
new GetUserPrivilegesResponse.Indices(Arrays.asList("index-1", "index-2", "index-3-*"), Arrays.asList("read", "write"),
|
||||
new LinkedHashSet<>(Arrays.asList(
|
||||
new FieldPermissionsDefinition.FieldGrantExcludeGroup(new String[]{ "public.*" }, new String[0]),
|
||||
new FieldPermissionsDefinition.FieldGrantExcludeGroup(new String[]{ "*" }, new String[]{ "private.*" })
|
||||
new FieldPermissionsDefinition.FieldGrantExcludeGroup(new String[]{"public.*"}, new String[0]),
|
||||
new FieldPermissionsDefinition.FieldGrantExcludeGroup(new String[]{"*"}, new String[]{"private.*"})
|
||||
)),
|
||||
new LinkedHashSet<>(Arrays.asList(
|
||||
new BytesArray("{ \"term\": { \"access\": \"public\" } }"),
|
||||
|
@ -60,7 +85,7 @@ public class RestGetUserPrivilegesActionTests extends ESTestCase {
|
|||
listener.buildResponse(response, builder);
|
||||
|
||||
String json = Strings.toString(builder);
|
||||
assertThat(json, Matchers.equalTo("{" +
|
||||
assertThat(json, equalTo("{" +
|
||||
"\"cluster\":[\"monitor\",\"manage_ml\",\"manage_watcher\"]," +
|
||||
"\"global\":[" +
|
||||
"{\"application\":{\"manage\":{\"applications\":[\"app01\",\"app02\"]}}}" +
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.security.rest.action.user;
|
||||
|
||||
import org.elasticsearch.client.node.NodeClient;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.XPackLicenseState;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.test.rest.FakeRestChannel;
|
||||
import org.elasticsearch.test.rest.FakeRestRequest;
|
||||
import org.elasticsearch.xpack.core.security.SecurityContext;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class RestHasPrivilegesActionTests extends ESTestCase {
|
||||
|
||||
public void testBasicLicense() throws Exception {
|
||||
final XPackLicenseState licenseState = mock(XPackLicenseState.class);
|
||||
final RestHasPrivilegesAction action = new RestHasPrivilegesAction(Settings.EMPTY, mock(RestController.class),
|
||||
mock(SecurityContext.class), licenseState);
|
||||
when(licenseState.isSecurityAvailable()).thenReturn(false);
|
||||
final FakeRestRequest request = new FakeRestRequest();
|
||||
final FakeRestChannel channel = new FakeRestChannel(request, true, 1);
|
||||
action.handleRequest(request, channel, mock(NodeClient.class));
|
||||
assertThat(channel.capturedResponse(), notNullValue());
|
||||
assertThat(channel.capturedResponse().status(), equalTo(RestStatus.FORBIDDEN));
|
||||
assertThat(channel.capturedResponse().content().utf8ToString(), containsString("current license is non-compliant for [security]"));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue