Provide clearer errors if SAML is not licensed (elastic/x-pack-elasticsearch#4096)
SAML is only available on a Platinum license. If you try and use SAML on a Gold license, then the error message is misleading - it gives the equivalent of "cannot find saml realm". This change adds a standard license error of "current license is non-compliant for [saml]" if SAML rest actions are used when SAML is not licensed. Original commit: elastic/x-pack-elasticsearch@7c0e26d58e
This commit is contained in:
parent
0de6376452
commit
063ed78c42
|
@ -266,7 +266,7 @@ public class Realms extends AbstractComponent implements Iterable<Realm> {
|
|||
return converted;
|
||||
}
|
||||
|
||||
private static boolean isRealmTypeAvailable(AllowedRealmType enabledRealmType, String type) {
|
||||
public static boolean isRealmTypeAvailable(AllowedRealmType enabledRealmType, String type) {
|
||||
switch (enabledRealmType) {
|
||||
case ALL:
|
||||
return true;
|
||||
|
|
|
@ -12,11 +12,10 @@ import org.elasticsearch.license.XPackLicenseState;
|
|||
import org.elasticsearch.rest.BaseRestHandler;
|
||||
import org.elasticsearch.rest.BytesRestResponse;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.xpack.core.XPackField;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.xpack.core.XPackField.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!
|
||||
|
@ -45,13 +44,26 @@ public abstract class SecurityBaseRestHandler extends BaseRestHandler {
|
|||
*/
|
||||
protected final RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
|
||||
RestChannelConsumer consumer = innerPrepareRequest(request, client);
|
||||
if (licenseState.isAuthAllowed()) {
|
||||
final String failedFeature = checkLicensedFeature(request);
|
||||
if (failedFeature == null) {
|
||||
return consumer;
|
||||
} else {
|
||||
return channel -> channel.sendResponse(new BytesRestResponse(channel, LicenseUtils.newComplianceException(SECURITY)));
|
||||
return channel -> channel.sendResponse(new BytesRestResponse(channel, LicenseUtils.newComplianceException(failedFeature)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given request is allowed within the current license state, and return the name of any unlicensed feature.
|
||||
* By default this returns {@link org.elasticsearch.xpack.core.XPackField#SECURITY} if the license state does not
|
||||
* {@link XPackLicenseState#isAuthAllowed() allow authentication and authorization}.
|
||||
* Sub-classes can override this method if they have additional licensing requirements.
|
||||
* @return {@code null} if all required features are licensed, otherwise the name of the most significant unlicensed feature.
|
||||
*/
|
||||
protected String checkLicensedFeature(RestRequest request) {
|
||||
return licenseState.isAuthAllowed() ? null : XPackField.SECURITY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implementers should implement this method as they normally would for
|
||||
* {@link BaseRestHandler#prepareRequest(RestRequest, NodeClient)} and ensure that all request
|
||||
|
|
|
@ -34,7 +34,7 @@ import static org.elasticsearch.rest.RestRequest.Method.POST;
|
|||
/**
|
||||
* A REST handler that attempts to authenticate a user based on the provided SAML response/assertion.
|
||||
*/
|
||||
public class RestSamlAuthenticateAction extends SecurityBaseRestHandler implements RestHandler {
|
||||
public class RestSamlAuthenticateAction extends SamlBaseRestHandler implements RestHandler {
|
||||
|
||||
static class Input {
|
||||
String content;
|
||||
|
|
|
@ -31,7 +31,7 @@ import static org.elasticsearch.rest.RestRequest.Method.POST;
|
|||
* Invalidates any security tokens associated with the provided SAML session.
|
||||
* The session identity is provided in a SAML {@code <LogoutRequest>}
|
||||
*/
|
||||
public class RestSamlInvalidateSessionAction extends SecurityBaseRestHandler {
|
||||
public class RestSamlInvalidateSessionAction extends SamlBaseRestHandler {
|
||||
|
||||
static final ObjectParser<SamlInvalidateSessionRequest, RestSamlInvalidateSessionAction> PARSER =
|
||||
new ObjectParser<>("saml_invalidate_session", SamlInvalidateSessionRequest::new);
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.elasticsearch.rest.action.RestBuilderListener;
|
|||
import org.elasticsearch.xpack.core.security.action.saml.SamlLogoutAction;
|
||||
import org.elasticsearch.xpack.core.security.action.saml.SamlLogoutRequest;
|
||||
import org.elasticsearch.xpack.core.security.action.saml.SamlLogoutResponse;
|
||||
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
|
||||
|
||||
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
||||
|
||||
|
@ -33,7 +32,7 @@ import static org.elasticsearch.rest.RestRequest.Method.POST;
|
|||
* This logout request is returned in the REST response as a redirect URI, and the REST client should
|
||||
* make it available to the browser.
|
||||
*/
|
||||
public class RestSamlLogoutAction extends SecurityBaseRestHandler {
|
||||
public class RestSamlLogoutAction extends SamlBaseRestHandler {
|
||||
|
||||
static final ObjectParser<SamlLogoutRequest, Void> PARSER = new ObjectParser<>("saml_logout", SamlLogoutRequest::new);
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ import static org.elasticsearch.rest.RestRequest.Method.POST;
|
|||
* The request is returned in the REST response, and the REST client should make it available
|
||||
* to the browser.
|
||||
*/
|
||||
public class RestSamlPrepareAuthenticationAction extends SecurityBaseRestHandler {
|
||||
public class RestSamlPrepareAuthenticationAction extends SamlBaseRestHandler {
|
||||
|
||||
static final ObjectParser<SamlPrepareAuthenticationRequest, Void> PARSER = new ObjectParser<>("saml_prepare_authn",
|
||||
SamlPrepareAuthenticationRequest::new);
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.saml;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.XPackLicenseState;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings;
|
||||
import org.elasticsearch.xpack.security.authc.Realms;
|
||||
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
|
||||
|
||||
/**
|
||||
* An abstract implementation of {@link SecurityBaseRestHandler} that performs a license check for the SAML realm type
|
||||
*/
|
||||
public abstract class SamlBaseRestHandler extends SecurityBaseRestHandler {
|
||||
|
||||
private static final String SAML_REALM_TYPE = SamlRealmSettings.TYPE;
|
||||
|
||||
public SamlBaseRestHandler(Settings settings, XPackLicenseState licenseState) {
|
||||
super(settings, licenseState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String checkLicensedFeature(RestRequest request) {
|
||||
String feature = super.checkLicensedFeature(request);
|
||||
if (feature != null) {
|
||||
return feature;
|
||||
} else if (Realms.isRealmTypeAvailable(licenseState.allowedRealmType(), SAML_REALM_TYPE)) {
|
||||
return null;
|
||||
} else {
|
||||
logger.info("The '{}' realm is not available under the current license", SAML_REALM_TYPE);
|
||||
return SAML_REALM_TYPE;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.saml;
|
||||
|
||||
import org.elasticsearch.client.node.NodeClient;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.License;
|
||||
import org.elasticsearch.license.TestUtils;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.test.rest.FakeRestRequest;
|
||||
import org.hamcrest.Matchers;
|
||||
|
||||
public class SamlBaseRestHandlerTests extends ESTestCase {
|
||||
|
||||
public void testSamlAvailableOnTrialAndPlatinum() {
|
||||
final SamlBaseRestHandler handler = buildHandler(randomFrom(License.OperationMode.TRIAL, License.OperationMode.PLATINUM));
|
||||
assertThat(handler.checkLicensedFeature(new FakeRestRequest()), Matchers.nullValue());
|
||||
}
|
||||
|
||||
public void testSecurityNotAvailableOnBasic() {
|
||||
final SamlBaseRestHandler handler = buildHandler(License.OperationMode.BASIC);
|
||||
assertThat(handler.checkLicensedFeature(new FakeRestRequest()), Matchers.equalTo("security"));
|
||||
}
|
||||
|
||||
public void testSamlNotAvailableOnStandardOrGold() {
|
||||
final SamlBaseRestHandler handler = buildHandler(randomFrom(License.OperationMode.STANDARD, License.OperationMode.GOLD));
|
||||
assertThat(handler.checkLicensedFeature(new FakeRestRequest()), Matchers.equalTo("saml"));
|
||||
}
|
||||
|
||||
private SamlBaseRestHandler buildHandler(License.OperationMode licenseMode) {
|
||||
final TestUtils.UpdatableLicenseState licenseState = new TestUtils.UpdatableLicenseState();
|
||||
licenseState.update(licenseMode, true);
|
||||
|
||||
return new SamlBaseRestHandler(Settings.EMPTY, licenseState) {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "saml_test";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue