diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilter.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilter.java index d4b62a1969d..be38137b6c0 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilter.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/action/filter/SecurityActionFilter.java @@ -38,6 +38,7 @@ import org.elasticsearch.xpack.security.authc.Authentication; import org.elasticsearch.xpack.security.authc.AuthenticationService; import org.elasticsearch.xpack.security.authz.AuthorizationService; import org.elasticsearch.xpack.security.authz.AuthorizationUtils; +import org.elasticsearch.xpack.security.authz.privilege.GeneralPrivilege; import org.elasticsearch.xpack.security.authz.privilege.HealthAndStatsPrivilege; import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.xpack.security.user.SystemUser; @@ -48,6 +49,8 @@ import static org.elasticsearch.xpack.security.support.Exceptions.authorizationE public class SecurityActionFilter extends AbstractComponent implements ActionFilter { private static final Predicate LICENSE_EXPIRATION_ACTION_MATCHER = HealthAndStatsPrivilege.INSTANCE.predicate(); + private static final Predicate SECURITY_ACTION_MATCHER = + new GeneralPrivilege("_security_matcher", "cluster:admin/xpack/security*").predicate(); private final AuthenticationService authcService; private final AuthorizationService authzService; @@ -104,6 +107,8 @@ public class SecurityActionFilter extends AbstractComponent implements ActionFil applyInternal(task, action, request, new SigningListener(this, listener, restoreOriginalContext ? original : null), chain); } + } else if (SECURITY_ACTION_MATCHER.test(action)) { + throw LicenseUtils.newComplianceException(XPackPlugin.SECURITY); } else { chain.proceed(task, action, request, listener); } diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateAction.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateAction.java index 7f3054b178b..ec1d9b977f3 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateAction.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateAction.java @@ -10,6 +10,8 @@ import org.elasticsearch.common.inject.Inject; 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.RestChannel; @@ -18,6 +20,7 @@ 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.user.User; import org.elasticsearch.xpack.security.action.user.AuthenticateAction; @@ -29,11 +32,14 @@ import static org.elasticsearch.rest.RestRequest.Method.GET; public class RestAuthenticateAction extends BaseRestHandler { private final SecurityContext securityContext; + private final XPackLicenseState licenseState; @Inject - public RestAuthenticateAction(Settings settings, RestController controller, SecurityContext securityContext) { + public RestAuthenticateAction(Settings settings, RestController controller, SecurityContext securityContext, + XPackLicenseState licenseState) { super(settings); this.securityContext = securityContext; + this.licenseState = licenseState; controller.registerHandler(GET, "/_xpack/security/_authenticate", this); // @deprecated: Remove in 6.0 @@ -45,6 +51,12 @@ public class RestAuthenticateAction extends BaseRestHandler { @Override public void handleRequest(RestRequest request, RestChannel channel, NodeClient client) throws Exception { + // 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); + } + final User user = securityContext.getUser(); assert user != null; final String username = user.runAs() == null ? user.principal() : user.runAs().principal(); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/license/LicensingTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/license/LicensingTests.java index ad1d46f30a2..15d72107643 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/license/LicensingTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/license/LicensingTests.java @@ -8,6 +8,7 @@ package org.elasticsearch.license; import java.util.ArrayList; import java.util.Collection; +import org.apache.http.message.BasicHeader; import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.action.DocWriteResponse; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; @@ -34,11 +35,15 @@ import org.elasticsearch.transport.Transport; import org.elasticsearch.xpack.XPackPlugin; import org.elasticsearch.xpack.XPackTransportClient; import org.elasticsearch.xpack.security.Security; +import org.elasticsearch.xpack.security.action.user.GetUsersResponse; +import org.elasticsearch.xpack.security.authc.support.SecuredString; import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken; +import org.elasticsearch.xpack.security.client.SecurityClient; import org.junit.Before; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.is; @@ -160,13 +165,50 @@ public class LicensingTests extends SecurityIntegTestCase { Response response = getRestClient().performRequest("GET", "/"); // the default of the licensing tests is basic assertThat(response.getStatusLine().getStatusCode(), is(200)); + ResponseException e = expectThrows(ResponseException.class, + () -> getRestClient().performRequest("GET", "/_xpack/security/_authenticate")); + assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403)); // generate a new license with a mode that enables auth License.OperationMode mode = randomFrom(License.OperationMode.GOLD, License.OperationMode.TRIAL, License.OperationMode.PLATINUM, License.OperationMode.STANDARD); enableLicensing(mode); - ResponseException e = expectThrows(ResponseException.class, () -> getRestClient().performRequest("GET", "/")); + e = expectThrows(ResponseException.class, () -> getRestClient().performRequest("GET", "/")); assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401)); + e = expectThrows(ResponseException.class, () -> getRestClient().performRequest("GET", "/_xpack/security/_authenticate")); + assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401)); + + final String basicAuthValue = UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME, + new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())); + response = getRestClient().performRequest("GET", "/", new BasicHeader("Authorization", basicAuthValue)); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + response = getRestClient() + .performRequest("GET", "/_xpack/security/_authenticate", new BasicHeader("Authorization", basicAuthValue)); + assertThat(response.getStatusLine().getStatusCode(), is(200)); + + } + + public void testSecurityActionsByLicenseType() throws Exception { + // security actions should not work! + try (TransportClient client = new XPackTransportClient(internalCluster().transportClient().settings())) { + client.addTransportAddress(internalCluster().getDataNodeInstance(Transport.class).boundAddress().publishAddress()); + new SecurityClient(client).prepareGetUsers().get(); + fail("security actions should not be enabled!"); + } catch (ElasticsearchSecurityException e) { + assertThat(e.status(), is(RestStatus.FORBIDDEN)); + assertThat(e.getMessage(), containsString("non-compliant")); + } + + // enable a license that enables security + License.OperationMode mode = randomFrom(License.OperationMode.GOLD, License.OperationMode.TRIAL, + License.OperationMode.PLATINUM, License.OperationMode.STANDARD); + enableLicensing(mode); + // security actions should not work! + try (TransportClient client = new XPackTransportClient(internalCluster().transportClient().settings())) { + client.addTransportAddress(internalCluster().getDataNodeInstance(Transport.class).boundAddress().publishAddress()); + GetUsersResponse response = new SecurityClient(client).prepareGetUsers().get(); + assertNotNull(response); + } } public void testTransportClientAuthenticationByLicenseType() throws Exception {