Add a base security rest handler (elastic/x-pack-elasticsearch#1239)

This commit adds a base rest handler for security that handles the license checking in the security
apis. This was done previously in some rest handlers but not all and actually had issues where a
value would be returned but we may not have consumed all of the request parameters, which could
lead to a different response being returned than what we would have expected.

relates elastic/x-pack-elasticsearch#1236

Original commit: elastic/x-pack-elasticsearch@2f1100b64a
This commit is contained in:
Jay Modi 2017-05-09 14:47:11 -04:00 committed by GitHub
parent 2d8eb8fbf7
commit 590eea57ac
20 changed files with 251 additions and 145 deletions

View File

@ -586,22 +586,22 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
}
return Arrays.asList(
new RestAuthenticateAction(settings, restController, securityContext.get(), licenseState),
new RestClearRealmCacheAction(settings, restController),
new RestClearRolesCacheAction(settings, restController),
new RestGetUsersAction(settings, restController),
new RestPutUserAction(settings, restController),
new RestDeleteUserAction(settings, restController),
new RestGetRolesAction(settings, restController),
new RestPutRoleAction(settings, restController),
new RestDeleteRoleAction(settings, restController),
new RestChangePasswordAction(settings, restController, securityContext.get()),
new RestSetEnabledAction(settings, restController),
new RestHasPrivilegesAction(settings, restController, securityContext.get()),
new RestGetRoleMappingsAction(settings, restController),
new RestPutRoleMappingAction(settings, restController),
new RestDeleteRoleMappingAction(settings, restController),
new RestGetTokenAction(settings, licenseState, restController),
new RestInvalidateTokenAction(settings, licenseState, restController)
new RestClearRealmCacheAction(settings, restController, licenseState),
new RestClearRolesCacheAction(settings, restController, licenseState),
new RestGetUsersAction(settings, restController, licenseState),
new RestPutUserAction(settings, restController, licenseState),
new RestDeleteUserAction(settings, restController, licenseState),
new RestGetRolesAction(settings, restController, licenseState),
new RestPutRoleAction(settings, restController, licenseState),
new RestDeleteRoleAction(settings, restController, licenseState),
new RestChangePasswordAction(settings, restController, securityContext.get(), licenseState),
new RestSetEnabledAction(settings, restController, licenseState),
new RestHasPrivilegesAction(settings, restController, securityContext.get(), licenseState),
new RestGetRoleMappingsAction(settings, restController, licenseState),
new RestPutRoleMappingAction(settings, restController, licenseState),
new RestDeleteRoleMappingAction(settings, restController, licenseState),
new RestGetTokenAction(settings, restController, licenseState),
new RestInvalidateTokenAction(settings, restController, licenseState)
);
}

View File

@ -9,16 +9,13 @@ import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.license.LicenseUtils;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.RestResponse;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.rest.action.RestBuilderListener;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.security.SecurityContext;
import org.elasticsearch.xpack.security.action.user.AuthenticateAction;
import org.elasticsearch.xpack.security.action.user.AuthenticateRequest;
@ -29,16 +26,14 @@ import java.io.IOException;
import static org.elasticsearch.rest.RestRequest.Method.GET;
public class RestAuthenticateAction extends BaseRestHandler {
public class RestAuthenticateAction extends SecurityBaseRestHandler {
private final SecurityContext securityContext;
private final XPackLicenseState licenseState;
public RestAuthenticateAction(Settings settings, RestController controller, SecurityContext securityContext,
XPackLicenseState licenseState) {
super(settings);
super(settings, licenseState);
this.securityContext = securityContext;
this.licenseState = licenseState;
controller.registerHandler(GET, "/_xpack/security/_authenticate", this);
// @deprecated: Remove in 6.0
@ -49,15 +44,11 @@ public class RestAuthenticateAction extends BaseRestHandler {
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
// this API is a special case since we access the user here and we want it to fail with the proper error instead of a request
// validation error
if (licenseState.isAuthAllowed() == false) {
throw LicenseUtils.newComplianceException(XPackPlugin.SECURITY);
}
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
final User user = securityContext.getUser();
assert user != null;
if (user == null) {
return restChannel -> { throw new IllegalStateException("we should never have a null user and invoke this consumer"); };
}
final String username = user.runAs() == null ? user.principal() : user.runAs().principal();
return channel -> client.execute(AuthenticateAction.INSTANCE, new AuthenticateRequest(username),

View File

@ -0,0 +1,62 @@
/*
* 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;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.LicenseUtils;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestRequest;
import java.io.IOException;
import static org.elasticsearch.xpack.XPackPlugin.SECURITY;
/**
* Base class for security rest handlers. This handler takes care of ensuring that the license
* level is valid so that security can be used!
*/
public abstract class SecurityBaseRestHandler extends BaseRestHandler {
protected final XPackLicenseState licenseState;
/**
* @param settings the node's settings
* @param licenseState the license state that will be used to determine if security is licensed
*/
protected SecurityBaseRestHandler(Settings settings, XPackLicenseState licenseState) {
super(settings);
this.licenseState = licenseState;
}
/**
* Calls the {@link #innerPrepareRequest(RestRequest, NodeClient)} method and then checks the
* license state. If the license state allows auth, the result from
* {@link #innerPrepareRequest(RestRequest, NodeClient)} is returned, otherwise a default error
* response will be returned indicating that security is not licensed.
*
* Note: the implementing rest handler is called before the license is checked so that we do not
* trip the unused parameters check
*/
protected final RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
RestChannelConsumer consumer = innerPrepareRequest(request, client);
if (licenseState.isAuthAllowed()) {
return consumer;
} else {
return channel -> channel.sendResponse(new BytesRestResponse(channel, LicenseUtils.newComplianceException(SECURITY)));
}
}
/**
* Implementers should implement this method as they normally would for
* {@link BaseRestHandler#prepareRequest(RestRequest, NodeClient)} and ensure that all request
* parameters are consumed prior to returning a value. The returned value is not guaranteed to
* be executed unless security is licensed and all request parameters are known
*/
protected abstract RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException;
}

View File

@ -16,18 +16,16 @@ import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.license.LicenseUtils;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestChannel;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.security.action.token.CreateTokenAction;
import org.elasticsearch.xpack.security.action.token.CreateTokenRequest;
import org.elasticsearch.xpack.security.action.token.CreateTokenResponse;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import java.io.IOException;
import java.util.Arrays;
@ -42,7 +40,7 @@ import static org.elasticsearch.rest.RestRequest.Method.POST;
* specification as this aspect does not make the most sense since the response body is
* expected to be JSON
*/
public final class RestGetTokenAction extends BaseRestHandler {
public final class RestGetTokenAction extends SecurityBaseRestHandler {
static final ConstructingObjectParser<CreateTokenRequest, Void> PARSER = new ConstructingObjectParser<>("token_request",
a -> new CreateTokenRequest((String) a[0], (String) a[1], (SecureString) a[2], (String) a[3]));
@ -55,22 +53,13 @@ public final class RestGetTokenAction extends BaseRestHandler {
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), new ParseField("scope"));
}
private final XPackLicenseState licenseState;
public RestGetTokenAction(Settings settings, XPackLicenseState xPackLicenseState, RestController controller) {
super(settings);
this.licenseState = xPackLicenseState;
public RestGetTokenAction(Settings settings, RestController controller, XPackLicenseState xPackLicenseState) {
super(settings, xPackLicenseState);
controller.registerHandler(POST, "/_xpack/security/oauth2/token", this);
}
@Override
protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client)throws IOException {
// this API shouldn't be available if security is disabled by license
if (licenseState.isAuthAllowed() == false) {
return channel ->
channel.sendResponse(new BytesRestResponse(channel, LicenseUtils.newComplianceException(XPackPlugin.SECURITY)));
}
protected RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client)throws IOException {
try (XContentParser parser = request.contentParser()) {
final CreateTokenRequest tokenRequest = PARSER.parse(parser, null);
return channel -> client.execute(CreateTokenAction.INSTANCE, tokenRequest,

View File

@ -11,19 +11,17 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.license.LicenseUtils;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.RestResponse;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.rest.action.RestBuilderListener;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.security.action.token.InvalidateTokenAction;
import org.elasticsearch.xpack.security.action.token.InvalidateTokenRequest;
import org.elasticsearch.xpack.security.action.token.InvalidateTokenResponse;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import java.io.IOException;
@ -32,7 +30,7 @@ import static org.elasticsearch.rest.RestRequest.Method.DELETE;
/**
* Rest handler for handling access token invalidation requests
*/
public final class RestInvalidateTokenAction extends BaseRestHandler {
public final class RestInvalidateTokenAction extends SecurityBaseRestHandler {
static final ConstructingObjectParser<String, Void> PARSER =
new ConstructingObjectParser<>("invalidate_token", a -> ((String) a[0]));
@ -40,24 +38,13 @@ public final class RestInvalidateTokenAction extends BaseRestHandler {
PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("token"));
}
private final XPackLicenseState licenseState;
public RestInvalidateTokenAction(Settings settings, XPackLicenseState xPackLicenseState,
RestController controller) {
super(settings);
this.licenseState = xPackLicenseState;
public RestInvalidateTokenAction(Settings settings, RestController controller, XPackLicenseState xPackLicenseState) {
super(settings, xPackLicenseState);
controller.registerHandler(DELETE, "/_xpack/security/oauth2/token", this);
}
@Override
protected RestChannelConsumer prepareRequest(RestRequest request,
NodeClient client)throws IOException {
// this API shouldn't be available if security is disabled by license
if (licenseState.isAuthAllowed() == false) {
return channel ->
channel.sendResponse(new BytesRestResponse(channel, LicenseUtils.newComplianceException(XPackPlugin.SECURITY)));
}
protected RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
try (XContentParser parser = request.contentParser()) {
final String token = PARSER.parse(parser, null);
final InvalidateTokenRequest tokenRequest = new InvalidateTokenRequest(token);

View File

@ -7,20 +7,22 @@ package org.elasticsearch.xpack.security.rest.action.realm;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.RestActions.NodesResponseRestListener;
import org.elasticsearch.xpack.security.action.realm.ClearRealmCacheRequest;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import java.io.IOException;
import static org.elasticsearch.rest.RestRequest.Method.POST;
public class RestClearRealmCacheAction extends BaseRestHandler {
public RestClearRealmCacheAction(Settings settings, RestController controller) {
super(settings);
public final class RestClearRealmCacheAction extends SecurityBaseRestHandler {
public RestClearRealmCacheAction(Settings settings, RestController controller, XPackLicenseState licenseState) {
super(settings, licenseState);
controller.registerHandler(POST, "/_xpack/security/realm/{realms}/_clear_cache", this);
// @deprecated: Remove in 6.0
@ -35,8 +37,7 @@ public class RestClearRealmCacheAction extends BaseRestHandler {
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
String[] realms = request.paramAsStringArrayOrEmptyIfAll("realms");
String[] usernames = request.paramAsStringArrayOrEmptyIfAll("usernames");

View File

@ -7,20 +7,22 @@ package org.elasticsearch.xpack.security.rest.action.role;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.RestActions.NodesResponseRestListener;
import org.elasticsearch.xpack.security.action.role.ClearRolesCacheRequest;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import java.io.IOException;
import static org.elasticsearch.rest.RestRequest.Method.POST;
public class RestClearRolesCacheAction extends BaseRestHandler {
public RestClearRolesCacheAction(Settings settings, RestController controller) {
super(settings);
public final class RestClearRolesCacheAction extends SecurityBaseRestHandler {
public RestClearRolesCacheAction(Settings settings, RestController controller, XPackLicenseState licenseState) {
super(settings, licenseState);
controller.registerHandler(POST, "/_xpack/security/role/{name}/_clear_cache", this);
// @deprecated: Remove in 6.0
@ -31,8 +33,7 @@ public class RestClearRolesCacheAction extends BaseRestHandler {
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
String[] roles = request.paramAsStringArrayOrEmptyIfAll("name");
ClearRolesCacheRequest req = new ClearRolesCacheRequest().names(roles);

View File

@ -8,7 +8,7 @@ package org.elasticsearch.xpack.security.rest.action.role;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
@ -17,6 +17,7 @@ import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.rest.action.RestBuilderListener;
import org.elasticsearch.xpack.security.action.role.DeleteRoleResponse;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import java.io.IOException;
@ -25,9 +26,10 @@ import static org.elasticsearch.rest.RestRequest.Method.DELETE;
/**
* Rest endpoint to delete a Role from the security index
*/
public class RestDeleteRoleAction extends BaseRestHandler {
public RestDeleteRoleAction(Settings settings, RestController controller) {
super(settings);
public class RestDeleteRoleAction extends SecurityBaseRestHandler {
public RestDeleteRoleAction(Settings settings, RestController controller, XPackLicenseState licenseState) {
super(settings, licenseState);
controller.registerHandler(DELETE, "/_xpack/security/role/{name}", this);
// @deprecated: Remove in 6.0
@ -38,7 +40,7 @@ public class RestDeleteRoleAction extends BaseRestHandler {
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
final String name = request.param("name");
final String refresh = request.param("refresh");

View File

@ -9,7 +9,7 @@ import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
@ -19,6 +19,7 @@ import org.elasticsearch.rest.action.RestBuilderListener;
import org.elasticsearch.xpack.security.action.role.GetRolesResponse;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import java.io.IOException;
@ -27,9 +28,9 @@ import static org.elasticsearch.rest.RestRequest.Method.GET;
/**
* Rest endpoint to retrieve a Role from the security index
*/
public class RestGetRolesAction extends BaseRestHandler {
public RestGetRolesAction(Settings settings, RestController controller) {
super(settings);
public class RestGetRolesAction extends SecurityBaseRestHandler {
public RestGetRolesAction(Settings settings, RestController controller, XPackLicenseState licenseState) {
super(settings, licenseState);
controller.registerHandler(GET, "/_xpack/security/role/", this);
controller.registerHandler(GET, "/_xpack/security/role/{name}", this);
@ -45,7 +46,7 @@ public class RestGetRolesAction extends BaseRestHandler {
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
final String[] roles = request.paramAsStringArray("name", Strings.EMPTY_ARRAY);
return channel -> new SecurityClient(client).prepareGetRoles(roles).execute(new RestBuilderListener<GetRolesResponse>(channel) {
@Override

View File

@ -8,7 +8,7 @@ package org.elasticsearch.xpack.security.rest.action.role;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
@ -18,6 +18,7 @@ import org.elasticsearch.rest.action.RestBuilderListener;
import org.elasticsearch.xpack.security.action.role.PutRoleRequestBuilder;
import org.elasticsearch.xpack.security.action.role.PutRoleResponse;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import java.io.IOException;
@ -27,9 +28,10 @@ import static org.elasticsearch.rest.RestRequest.Method.PUT;
/**
* Rest endpoint to add a Role to the security index
*/
public class RestPutRoleAction extends BaseRestHandler {
public RestPutRoleAction(Settings settings, RestController controller) {
super(settings);
public class RestPutRoleAction extends SecurityBaseRestHandler {
public RestPutRoleAction(Settings settings, RestController controller, XPackLicenseState licenseState) {
super(settings, licenseState);
controller.registerHandler(POST, "/_xpack/security/role/{name}", this);
controller.registerHandler(PUT, "/_xpack/security/role/{name}", this);
@ -45,7 +47,7 @@ public class RestPutRoleAction extends BaseRestHandler {
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
PutRoleRequestBuilder requestBuilder = new SecurityClient(client)
.preparePutRole(request.param("name"), request.content(), request.getXContentType())
.setRefreshPolicy(request.param("refresh"));

View File

@ -10,7 +10,7 @@ import java.io.IOException;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
@ -19,22 +19,22 @@ import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.rest.action.RestBuilderListener;
import org.elasticsearch.xpack.security.action.rolemapping.DeleteRoleMappingResponse;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import static org.elasticsearch.rest.RestRequest.Method.DELETE;
/**
* Rest endpoint to delete a role-mapping from the {@link org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore}
*/
public class RestDeleteRoleMappingAction extends BaseRestHandler {
public class RestDeleteRoleMappingAction extends SecurityBaseRestHandler {
public RestDeleteRoleMappingAction(Settings settings, RestController controller) {
super(settings);
public RestDeleteRoleMappingAction(Settings settings, RestController controller, XPackLicenseState licenseState) {
super(settings, licenseState);
controller.registerHandler(DELETE, "/_xpack/security/role_mapping/{name}", this);
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client)
throws IOException {
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
final String name = request.param("name");
final String refresh = request.param("refresh");

View File

@ -8,10 +8,9 @@ package org.elasticsearch.xpack.security.rest.action.rolemapping;
import java.io.IOException;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
@ -21,22 +20,23 @@ import org.elasticsearch.rest.action.RestBuilderListener;
import org.elasticsearch.xpack.security.action.rolemapping.GetRoleMappingsResponse;
import org.elasticsearch.xpack.security.authc.support.mapper.ExpressionRoleMapping;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import static org.elasticsearch.rest.RestRequest.Method.GET;
/**
* Rest endpoint to retrieve a role-mapping from the {@link org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore}
*/
public class RestGetRoleMappingsAction extends BaseRestHandler {
public RestGetRoleMappingsAction(Settings settings, RestController controller) {
super(settings);
public class RestGetRoleMappingsAction extends SecurityBaseRestHandler {
public RestGetRoleMappingsAction(Settings settings, RestController controller, XPackLicenseState licenseState) {
super(settings, licenseState);
controller.registerHandler(GET, "/_xpack/security/role_mapping/", this);
controller.registerHandler(GET, "/_xpack/security/role_mapping/{name}", this);
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client)
throws IOException {
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
final String[] names = request.paramAsStringArrayOrEmptyIfAll("name");
return channel -> new SecurityClient(client).prepareGetRoleMappings(names)
.execute(new RestBuilderListener<GetRoleMappingsResponse>(channel) {

View File

@ -10,7 +10,7 @@ import java.io.IOException;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
@ -20,6 +20,7 @@ import org.elasticsearch.rest.action.RestBuilderListener;
import org.elasticsearch.xpack.security.action.rolemapping.PutRoleMappingRequestBuilder;
import org.elasticsearch.xpack.security.action.rolemapping.PutRoleMappingResponse;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import static org.elasticsearch.rest.RestRequest.Method.POST;
import static org.elasticsearch.rest.RestRequest.Method.PUT;
@ -29,17 +30,16 @@ import static org.elasticsearch.rest.RestRequest.Method.PUT;
*
* @see org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore
*/
public class RestPutRoleMappingAction extends BaseRestHandler {
public RestPutRoleMappingAction(Settings settings, RestController controller) {
super(settings);
public class RestPutRoleMappingAction extends SecurityBaseRestHandler {
public RestPutRoleMappingAction(Settings settings, RestController controller, XPackLicenseState licenseState) {
super(settings, licenseState);
controller.registerHandler(POST, "/_xpack/security/role_mapping/{name}", this);
controller.registerHandler(PUT, "/_xpack/security/role_mapping/{name}", this);
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client)
throws IOException {
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
final String name = request.param("name");
PutRoleMappingRequestBuilder requestBuilder = new SecurityClient(client)
.preparePutRoleMapping(name, request.content(), request.getXContentType())

View File

@ -8,7 +8,7 @@ package org.elasticsearch.xpack.security.rest.action.user;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
@ -19,6 +19,7 @@ import org.elasticsearch.xpack.security.SecurityContext;
import org.elasticsearch.xpack.security.action.user.ChangePasswordResponse;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.rest.RestRequestFilter;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import org.elasticsearch.xpack.security.user.User;
import java.io.IOException;
@ -28,12 +29,13 @@ import java.util.Set;
import static org.elasticsearch.rest.RestRequest.Method.POST;
import static org.elasticsearch.rest.RestRequest.Method.PUT;
public class RestChangePasswordAction extends BaseRestHandler implements RestRequestFilter {
public class RestChangePasswordAction extends SecurityBaseRestHandler implements RestRequestFilter {
private final SecurityContext securityContext;
public RestChangePasswordAction(Settings settings, RestController controller, SecurityContext securityContext) {
super(settings);
public RestChangePasswordAction(Settings settings, RestController controller, SecurityContext securityContext,
XPackLicenseState licenseState) {
super(settings, licenseState);
this.securityContext = securityContext;
controller.registerHandler(POST, "/_xpack/security/user/{username}/_password", this);
controller.registerHandler(PUT, "/_xpack/security/user/{username}/_password", this);
@ -42,7 +44,7 @@ public class RestChangePasswordAction extends BaseRestHandler implements RestReq
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
final User user = securityContext.getUser();
final String username;
if (request.param("username") == null) {
@ -58,10 +60,8 @@ public class RestChangePasswordAction extends BaseRestHandler implements RestReq
.setRefreshPolicy(refresh)
.execute(new RestBuilderListener<ChangePasswordResponse>(channel) {
@Override
public RestResponse buildResponse(
ChangePasswordResponse changePasswordResponse,
XContentBuilder builder)
throws Exception {
public RestResponse buildResponse(ChangePasswordResponse changePasswordResponse,
XContentBuilder builder) throws Exception {
return new BytesRestResponse(RestStatus.OK, builder.startObject().endObject());
}
});

View File

@ -8,7 +8,7 @@ package org.elasticsearch.xpack.security.rest.action.user;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
@ -17,6 +17,7 @@ import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.rest.action.RestBuilderListener;
import org.elasticsearch.xpack.security.action.user.DeleteUserResponse;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import java.io.IOException;
@ -25,9 +26,10 @@ import static org.elasticsearch.rest.RestRequest.Method.DELETE;
/**
* Rest action to delete a user from the security index
*/
public class RestDeleteUserAction extends BaseRestHandler {
public RestDeleteUserAction(Settings settings, RestController controller) {
super(settings);
public class RestDeleteUserAction extends SecurityBaseRestHandler {
public RestDeleteUserAction(Settings settings, RestController controller, XPackLicenseState licenseState) {
super(settings, licenseState);
controller.registerHandler(DELETE, "/_xpack/security/user/{username}", this);
// @deprecated: Remove in 6.0
@ -38,7 +40,7 @@ public class RestDeleteUserAction extends BaseRestHandler {
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
final String username = request.param("username");
final String refresh = request.param("refresh");
return channel -> new SecurityClient(client).prepareDeleteUser(username)

View File

@ -9,7 +9,7 @@ import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
@ -18,6 +18,7 @@ import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.rest.action.RestBuilderListener;
import org.elasticsearch.xpack.security.action.user.GetUsersResponse;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import org.elasticsearch.xpack.security.user.User;
import java.io.IOException;
@ -27,9 +28,10 @@ import static org.elasticsearch.rest.RestRequest.Method.GET;
/**
* Rest action to retrieve a user from the security index
*/
public class RestGetUsersAction extends BaseRestHandler {
public RestGetUsersAction(Settings settings, RestController controller) {
super(settings);
public class RestGetUsersAction extends SecurityBaseRestHandler {
public RestGetUsersAction(Settings settings, RestController controller, XPackLicenseState licenseState) {
super(settings, licenseState);
controller.registerHandler(GET, "/_xpack/security/user/", this);
controller.registerHandler(GET, "/_xpack/security/user/{username}", this);
@ -45,7 +47,7 @@ public class RestGetUsersAction extends BaseRestHandler {
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
String[] usernames = request.paramAsStringArray("username", Strings.EMPTY_ARRAY);
return channel -> new SecurityClient(client).prepareGetUsers(usernames).execute(new RestBuilderListener<GetUsersResponse>(channel) {

View File

@ -10,7 +10,7 @@ import java.io.IOException;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestChannel;
import org.elasticsearch.rest.RestController;
@ -22,6 +22,7 @@ import org.elasticsearch.xpack.security.SecurityContext;
import org.elasticsearch.xpack.security.action.user.HasPrivilegesRequestBuilder;
import org.elasticsearch.xpack.security.action.user.HasPrivilegesResponse;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import org.elasticsearch.xpack.security.user.User;
import static org.elasticsearch.rest.RestRequest.Method.GET;
@ -31,12 +32,13 @@ import static org.elasticsearch.rest.RestRequest.Method.POST;
* REST handler that tests whether a user has the specified
* {@link org.elasticsearch.xpack.security.authz.RoleDescriptor.IndicesPrivileges privileges}
*/
public class RestHasPrivilegesAction extends BaseRestHandler {
public class RestHasPrivilegesAction extends SecurityBaseRestHandler {
private final SecurityContext securityContext;
public RestHasPrivilegesAction(Settings settings, RestController controller, SecurityContext securityContext) {
super(settings);
public RestHasPrivilegesAction(Settings settings, RestController controller, SecurityContext securityContext,
XPackLicenseState licenseState) {
super(settings, licenseState);
this.securityContext = securityContext;
controller.registerHandler(GET, "/_xpack/security/user/{username}/_has_privileges", this);
controller.registerHandler(POST, "/_xpack/security/user/{username}/_has_privileges", this);
@ -45,7 +47,7 @@ public class RestHasPrivilegesAction extends BaseRestHandler {
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
final String username = getUsername(request);
HasPrivilegesRequestBuilder requestBuilder = new SecurityClient(client)
.prepareHasPrivileges(username, request.content(), request.getXContentType());

View File

@ -9,7 +9,7 @@ import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
@ -20,6 +20,7 @@ import org.elasticsearch.xpack.security.action.user.PutUserRequestBuilder;
import org.elasticsearch.xpack.security.action.user.PutUserResponse;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.rest.RestRequestFilter;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import java.io.IOException;
import java.util.Collections;
@ -31,10 +32,10 @@ import static org.elasticsearch.rest.RestRequest.Method.PUT;
/**
* Rest endpoint to add a User to the security index
*/
public class RestPutUserAction extends BaseRestHandler implements RestRequestFilter {
public class RestPutUserAction extends SecurityBaseRestHandler implements RestRequestFilter {
public RestPutUserAction(Settings settings, RestController controller) {
super(settings);
public RestPutUserAction(Settings settings, RestController controller, XPackLicenseState licenseState) {
super(settings, licenseState);
controller.registerHandler(POST, "/_xpack/security/user/{username}", this);
controller.registerHandler(PUT, "/_xpack/security/user/{username}", this);
@ -50,7 +51,7 @@ public class RestPutUserAction extends BaseRestHandler implements RestRequestFil
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
PutUserRequestBuilder requestBuilder = new SecurityClient(client)
.preparePutUser(request.param("username"), request.content(), request.getXContentType())
.setRefreshPolicy(request.param("refresh"));

View File

@ -8,6 +8,7 @@ package org.elasticsearch.xpack.security.rest.action.user;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestController;
@ -17,6 +18,7 @@ import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.rest.action.RestBuilderListener;
import org.elasticsearch.xpack.security.action.user.SetEnabledResponse;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
import java.io.IOException;
@ -27,9 +29,10 @@ import static org.elasticsearch.rest.RestRequest.Method.PUT;
* REST handler for enabling and disabling users. The username is required and we use the path to determine if the user is being
* enabled or disabled.
*/
public class RestSetEnabledAction extends BaseRestHandler {
public RestSetEnabledAction(Settings settings, RestController controller) {
super(settings);
public class RestSetEnabledAction extends SecurityBaseRestHandler {
public RestSetEnabledAction(Settings settings, RestController controller, XPackLicenseState licenseState) {
super(settings, licenseState);
controller.registerHandler(POST, "/_xpack/security/user/{username}/_enable", this);
controller.registerHandler(PUT, "/_xpack/security/user/{username}/_enable", this);
controller.registerHandler(POST, "/_xpack/security/user/{username}/_disable", this);
@ -37,7 +40,7 @@ public class RestSetEnabledAction extends BaseRestHandler {
}
@Override
public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
final boolean enabled = request.path().endsWith("_enable");
assert enabled || request.path().endsWith("_disable");
final String username = request.param("username");

View File

@ -0,0 +1,60 @@
/*
* 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;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.rest.FakeRestChannel;
import org.elasticsearch.test.rest.FakeRestRequest;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
public class SecurityBaseRestHandlerTests extends ESTestCase {
public void testSecurityBaseRestHandlerChecksLicenseState() throws Exception {
final boolean securityEnabled = randomBoolean();
final AtomicBoolean consumerCalled = new AtomicBoolean(false);
final XPackLicenseState licenseState = mock(XPackLicenseState.class);
when(licenseState.isAuthAllowed()).thenReturn(securityEnabled);
SecurityBaseRestHandler handler = new SecurityBaseRestHandler(Settings.EMPTY, licenseState) {
@Override
protected RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
return channel -> {
if (consumerCalled.compareAndSet(false, true) == false) {
fail("consumerCalled was not false");
}
};
}
};
FakeRestRequest fakeRestRequest = new FakeRestRequest();
FakeRestChannel fakeRestChannel = new FakeRestChannel(fakeRestRequest, randomBoolean(), securityEnabled ? 0 : 1);
NodeClient client = mock(NodeClient.class);
assertFalse(consumerCalled.get());
verifyZeroInteractions(licenseState);
handler.handleRequest(fakeRestRequest, fakeRestChannel, client);
verify(licenseState).isAuthAllowed();
if (securityEnabled) {
assertTrue(consumerCalled.get());
assertEquals(0, fakeRestChannel.responses().get());
assertEquals(0, fakeRestChannel.errors().get());
} else {
assertFalse(consumerCalled.get());
assertEquals(0, fakeRestChannel.responses().get());
assertEquals(1, fakeRestChannel.errors().get());
}
}
}