Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -387,10 +387,19 @@ public AllowedRealmType allowedRealmType() {
*/
public boolean isCustomRoleProvidersAllowed() {
final Status localStatus = status;
return (localStatus.mode == OperationMode.PLATINUM || localStatus.mode == OperationMode.TRIAL )
return (localStatus.mode == OperationMode.PLATINUM || localStatus.mode == OperationMode.TRIAL)
&& localStatus.active;
}

/**
* @return whether "authorizing_realms" are allowed based on the license {@link OperationMode}
*/
public boolean isAuthorizingRealmAllowed() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pardon my knowledge with licensing, I just wanted to check if we have some encrypted data about what features are available in the license or we allow/disallow based on these checks depending on the license mode?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We rely solely on the license "mode"
That way we can introduce new features without needing to reissue licenses.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it. Thanks.

final Status localStatus = status;
return (localStatus.mode == OperationMode.PLATINUM || localStatus.mode == OperationMode.TRIAL)
&& localStatus.active;
}

/**
* Determine if Watcher is available based on the current license.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.xpack.core.security.authc.support.DelegatedAuthorizationSettings;
import org.elasticsearch.xpack.core.security.user.User;

Expand Down Expand Up @@ -137,7 +138,7 @@ public String toString() {
*
* @see DelegatedAuthorizationSettings
*/
public void initialize(Iterable<Realm> realms) {
public void initialize(Iterable<Realm> realms, XPackLicenseState licenseState) {
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.elasticsearch.xpack.security.transport.netty4.SecurityNetty4HttpServerTransport;
import org.elasticsearch.xpack.security.transport.netty4.SecurityNetty4ServerTransport;
import org.elasticsearch.xpack.core.template.TemplateUtils;
import org.elasticsearch.xpack.security.transport.nio.SecurityNioHttpServerTransport;
import org.elasticsearch.xpack.security.transport.nio.SecurityNioTransport;
import org.joda.time.DateTime;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public Realms(Settings settings, Environment env, Map<String, Realm.Factory> fac

this.standardRealmsOnly = Collections.unmodifiableList(standardRealms);
this.nativeRealmsOnly = Collections.unmodifiableList(nativeRealms);
realms.forEach(r -> r.initialize(this));
realms.forEach(r -> r.initialize(this, licenseState));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.elasticsearch.common.util.concurrent.ReleasableLock;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.env.Environment;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.core.security.authc.AuthenticationResult;
import org.elasticsearch.xpack.core.security.authc.AuthenticationToken;
Expand Down Expand Up @@ -96,11 +97,11 @@ public PkiRealm(RealmConfig config, ResourceWatcherService watcherService, Nativ
}

@Override
public void initialize(Iterable<Realm> realms) {
public void initialize(Iterable<Realm> realms, XPackLicenseState licenseState) {
if (delegatedRealms != null) {
throw new IllegalStateException("Realm has already been initialized");
}
delegatedRealms = new DelegatedAuthorizationSupport(realms, config);
delegatedRealms = new DelegatedAuthorizationSupport(realms, config, licenseState);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.license.LicenseUtils;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.xpack.core.security.authc.AuthenticationResult;
import org.elasticsearch.xpack.core.security.authc.Realm;
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
Expand All @@ -34,23 +36,26 @@ public class DelegatedAuthorizationSupport {

private final RealmUserLookup lookup;
private final Logger logger;
private final XPackLicenseState licenseState;

/**
* Resolves the {@link DelegatedAuthorizationSettings#AUTHZ_REALMS} setting from {@code config} and calls
* {@link #DelegatedAuthorizationSupport(Iterable, List, ThreadContext)}
* {@link #DelegatedAuthorizationSupport(Iterable, List, ThreadContext, XPackLicenseState)}
*/
public DelegatedAuthorizationSupport(Iterable<? extends Realm> allRealms, RealmConfig config) {
this(allRealms, DelegatedAuthorizationSettings.AUTHZ_REALMS.get(config.settings()), config.threadContext());
public DelegatedAuthorizationSupport(Iterable<? extends Realm> allRealms, RealmConfig config, XPackLicenseState licenseState) {
this(allRealms, DelegatedAuthorizationSettings.AUTHZ_REALMS.get(config.settings()), config.threadContext(), licenseState);
}

/**
* Constructs a new object that delegates to the named realms ({@code lookupRealms}), which must exist within
* {@code allRealms}.
* @throws IllegalArgumentException if one of the specified realms does not exist
*/
protected DelegatedAuthorizationSupport(Iterable<? extends Realm> allRealms, List<String> lookupRealms, ThreadContext threadContext) {
protected DelegatedAuthorizationSupport(Iterable<? extends Realm> allRealms, List<String> lookupRealms, ThreadContext threadContext,
XPackLicenseState licenseState) {
this.lookup = new RealmUserLookup(resolveRealms(allRealms, lookupRealms), threadContext);
this.logger = Loggers.getLogger(getClass());
this.licenseState = licenseState;
}

/**
Expand All @@ -69,6 +74,13 @@ public boolean hasDelegation() {
* with a meaningful diagnostic message.
*/
public void resolve(String username, ActionListener<AuthenticationResult> resultListener) {
if (licenseState.isAuthorizingRealmAllowed() == false) {
resultListener.onResponse(AuthenticationResult.unsuccessful(
DelegatedAuthorizationSettings.AUTHZ_REALMS.getKey() + " are not permitted",
LicenseUtils.newComplianceException(DelegatedAuthorizationSettings.AUTHZ_REALMS.getKey())
));
return;
}
if (hasDelegation() == false) {
resultListener.onResponse(AuthenticationResult.unsuccessful(
"No [" + DelegatedAuthorizationSettings.AUTHZ_REALMS.getKey() + "] have been configured", null));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.elasticsearch.common.util.CollectionUtils;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.security.authc.AuthenticationResult;
import org.elasticsearch.xpack.core.security.authc.Realm;
Expand Down Expand Up @@ -61,12 +62,15 @@
public class PkiRealmTests extends ESTestCase {

private Settings globalSettings;
private XPackLicenseState licenseState;

@Before
public void setup() throws Exception {
globalSettings = Settings.builder()
.put("path.home", createTempDir())
.build();
licenseState = mock(XPackLicenseState.class);
when(licenseState.isAuthorizingRealmAllowed()).thenReturn(true);
}

public void testTokenSupport() {
Expand Down Expand Up @@ -161,7 +165,7 @@ private PkiRealm buildRealm(UserRoleMapper roleMapper, Settings realmSettings, R
List<Realm> allRealms = CollectionUtils.arrayAsArrayList(otherRealms);
allRealms.add(realm);
Collections.shuffle(allRealms, random());
realm.initialize(allRealms);
realm.initialize(allRealms, licenseState);
return realm;
}

Expand All @@ -182,7 +186,7 @@ public void testCustomUsernamePattern() throws Exception {
UserRoleMapper roleMapper = mock(UserRoleMapper.class);
PkiRealm realm = new PkiRealm(new RealmConfig("", Settings.builder().put("username_pattern", "OU=(.*?),").build(), globalSettings,
TestEnvironment.newEnvironment(globalSettings), threadContext), roleMapper);
realm.initialize(Collections.emptyList());
realm.initialize(Collections.emptyList(), licenseState);
Mockito.doAnswer(invocation -> {
ActionListener<Set<String>> listener = (ActionListener<Set<String>>) invocation.getArguments()[1];
listener.onResponse(Collections.emptySet());
Expand Down Expand Up @@ -213,7 +217,7 @@ public void testVerificationUsingATruststore() throws Exception {
ThreadContext threadContext = new ThreadContext(globalSettings);
PkiRealm realm = new PkiRealm(new RealmConfig("", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings),
threadContext), roleMapper);
realm.initialize(Collections.emptyList());
realm.initialize(Collections.emptyList(), licenseState);
Mockito.doAnswer(invocation -> {
ActionListener<Set<String>> listener = (ActionListener<Set<String>>) invocation.getArguments()[1];
listener.onResponse(Collections.emptySet());
Expand Down Expand Up @@ -245,7 +249,7 @@ public void testVerificationFailsUsingADifferentTruststore() throws Exception {
final ThreadContext threadContext = new ThreadContext(globalSettings);
PkiRealm realm = new PkiRealm(new RealmConfig("", settings, globalSettings, TestEnvironment.newEnvironment(globalSettings),
threadContext), roleMapper);
realm.initialize(Collections.emptyList());
realm.initialize(Collections.emptyList(), licenseState);
Mockito.doAnswer(invocation -> {
ActionListener<Set<String>> listener = (ActionListener<Set<String>>) invocation.getArguments()[1];
listener.onResponse(Collections.emptySet());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
*/
package org.elasticsearch.xpack.security.authc.support;

import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.action.support.PlainActionFuture;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.TestEnvironment;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.security.authc.AuthenticationResult;
import org.elasticsearch.xpack.core.security.authc.Realm;
Expand All @@ -26,8 +28,11 @@
import static org.elasticsearch.common.Strings.collectionToDelimitedString;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class DelegatedAuthorizationSupportTests extends ESTestCase {

Expand Down Expand Up @@ -62,7 +67,8 @@ private RealmConfig buildRealmConfig(String name, Settings settings) {
}

public void testEmptyDelegationList() throws ExecutionException, InterruptedException {
final DelegatedAuthorizationSupport das = new DelegatedAuthorizationSupport(realms, buildRealmConfig("r", Settings.EMPTY));
final XPackLicenseState license = getLicenseState(true);
final DelegatedAuthorizationSupport das = new DelegatedAuthorizationSupport(realms, buildRealmConfig("r", Settings.EMPTY), license);
assertThat(das.hasDelegation(), equalTo(false));
final PlainActionFuture<AuthenticationResult> future = new PlainActionFuture<>();
das.resolve("any", future);
Expand All @@ -73,23 +79,25 @@ public void testEmptyDelegationList() throws ExecutionException, InterruptedExce
}

public void testMissingRealmInDelegationList() {
final XPackLicenseState license = getLicenseState(true);
final Settings settings = Settings.builder()
.putList("authorizing_realms", "no-such-realm")
.build();
final IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () ->
new DelegatedAuthorizationSupport(realms, buildRealmConfig("r", settings))
new DelegatedAuthorizationSupport(realms, buildRealmConfig("r", settings), license)
);
assertThat(ex.getMessage(), equalTo("configured authorizing realm [no-such-realm] does not exist (or is not enabled)"));
}

public void testMatchInDelegationList() throws Exception {
final XPackLicenseState license = getLicenseState(true);
final List<MockLookupRealm> useRealms = shuffle(randomSubsetOf(randomIntBetween(1, realms.size()), realms));
final Settings settings = Settings.builder()
.putList("authorizing_realms", useRealms.stream().map(Realm::name).collect(Collectors.toList()))
.build();
final User user = new User("my_user");
randomFrom(useRealms).registerUser(user);
final DelegatedAuthorizationSupport das = new DelegatedAuthorizationSupport(realms, buildRealmConfig("r", settings));
final DelegatedAuthorizationSupport das = new DelegatedAuthorizationSupport(realms, buildRealmConfig("r", settings), license);
assertThat(das.hasDelegation(), equalTo(true));
final PlainActionFuture<AuthenticationResult> future = new PlainActionFuture<>();
das.resolve("my_user", future);
Expand All @@ -99,6 +107,7 @@ public void testMatchInDelegationList() throws Exception {
}

public void testRealmsAreOrdered() throws Exception {
final XPackLicenseState license = getLicenseState(true);
final List<MockLookupRealm> useRealms = shuffle(randomSubsetOf(randomIntBetween(3, realms.size()), realms));
final List<String> names = useRealms.stream().map(Realm::name).collect(Collectors.toList());
final Settings settings = Settings.builder()
Expand All @@ -112,7 +121,7 @@ public void testRealmsAreOrdered() throws Exception {
r.registerUser(user);
}

final DelegatedAuthorizationSupport das = new DelegatedAuthorizationSupport(realms, buildRealmConfig("r", settings));
final DelegatedAuthorizationSupport das = new DelegatedAuthorizationSupport(realms, buildRealmConfig("r", settings), license);
assertThat(das.hasDelegation(), equalTo(true));
final PlainActionFuture<AuthenticationResult> future = new PlainActionFuture<>();
das.resolve(username, future);
Expand All @@ -123,11 +132,12 @@ public void testRealmsAreOrdered() throws Exception {
}

public void testNoMatchInDelegationList() throws Exception {
final XPackLicenseState license = getLicenseState(true);
final List<MockLookupRealm> useRealms = shuffle(randomSubsetOf(randomIntBetween(1, realms.size()), realms));
final Settings settings = Settings.builder()
.putList("authorizing_realms", useRealms.stream().map(Realm::name).collect(Collectors.toList()))
.build();
final DelegatedAuthorizationSupport das = new DelegatedAuthorizationSupport(realms, buildRealmConfig("r", settings));
final DelegatedAuthorizationSupport das = new DelegatedAuthorizationSupport(realms, buildRealmConfig("r", settings), license);
assertThat(das.hasDelegation(), equalTo(true));
final PlainActionFuture<AuthenticationResult> future = new PlainActionFuture<>();
das.resolve("my_user", future);
Expand All @@ -138,4 +148,26 @@ public void testNoMatchInDelegationList() throws Exception {
collectionToDelimitedString(useRealms.stream().map(Realm::toString).collect(Collectors.toList()), ",") + "]"));
}

public void testLicenseRejection() throws Exception {
final XPackLicenseState license = getLicenseState(false);
final Settings settings = Settings.builder()
.putList("authorizing_realms", realms.get(0).name())
.build();
final DelegatedAuthorizationSupport das = new DelegatedAuthorizationSupport(realms, buildRealmConfig("r", settings), license);
assertThat(das.hasDelegation(), equalTo(true));
final PlainActionFuture<AuthenticationResult> future = new PlainActionFuture<>();
das.resolve("my_user", future);
final AuthenticationResult result = future.get();
assertThat(result.getStatus(), equalTo(AuthenticationResult.Status.CONTINUE));
assertThat(result.getUser(), nullValue());
assertThat(result.getMessage(), equalTo("authorizing_realms are not permitted"));
assertThat(result.getException(), instanceOf(ElasticsearchSecurityException.class));
assertThat(result.getException().getMessage(), equalTo("current license is non-compliant for [authorizing_realms]"));
}

private XPackLicenseState getLicenseState(boolean authzRealmsAllowed) {
final XPackLicenseState license = mock(XPackLicenseState.class);
when(license.isAuthorizingRealmAllowed()).thenReturn(authzRealmsAllowed);
return license;
}
}