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 @@ -10,17 +10,20 @@
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
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;
import org.elasticsearch.xpack.core.security.authc.RealmSettings;
import org.elasticsearch.xpack.core.security.authc.support.DelegatedAuthorizationSettings;
import org.elasticsearch.xpack.core.security.user.User;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static org.elasticsearch.common.Strings.collectionToDelimitedString;

Expand All @@ -40,22 +43,25 @@ public class DelegatedAuthorizationSupport {

/**
* Resolves the {@link DelegatedAuthorizationSettings#AUTHZ_REALMS} setting from {@code config} and calls
* {@link #DelegatedAuthorizationSupport(Iterable, List, ThreadContext, XPackLicenseState)}
* {@link #DelegatedAuthorizationSupport(Iterable, List, Settings, ThreadContext, XPackLicenseState)}
*/
public DelegatedAuthorizationSupport(Iterable<? extends Realm> allRealms, RealmConfig config, XPackLicenseState licenseState) {
this(allRealms, DelegatedAuthorizationSettings.AUTHZ_REALMS.get(config.settings()), config.threadContext(), licenseState);
this(allRealms, DelegatedAuthorizationSettings.AUTHZ_REALMS.get(config.settings()), config.globalSettings(), 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,
XPackLicenseState licenseState) {
this.lookup = new RealmUserLookup(resolveRealms(allRealms, lookupRealms), threadContext);
this.logger = Loggers.getLogger(getClass());
this.licenseState = licenseState;
protected DelegatedAuthorizationSupport(Iterable<? extends Realm> allRealms, List<String> lookupRealms, Settings settings,
ThreadContext threadContext, XPackLicenseState licenseState) {
final List<Realm> resolvedLookupRealms = resolveRealms(allRealms, lookupRealms);
checkForRealmChains(resolvedLookupRealms, settings);
this.lookup = new RealmUserLookup(resolvedLookupRealms, threadContext);
this.logger = Loggers.getLogger(getClass());
this.licenseState = licenseState;
}

/**
Expand Down Expand Up @@ -108,13 +114,33 @@ private List<Realm> resolveRealms(Iterable<? extends Realm> allRealms, List<Stri
return result;
}

/**
* Checks for (and rejects) chains of delegation in the provided realms.
* A chain occurs when "realmA" delegates authorization to "realmB", and realmB also delegates authorization (to any realm).
* Since "realmB" does not handle its own authorization, it is not a valid target for delegated authorization.
* @param delegatedRealms The list of realms that are going to be used for authorization. If is an error if any of these realms are
* also configured to delegate their authorization.
* @throws IllegalArgumentException if a chain is detected
*/
private void checkForRealmChains(Iterable<Realm> delegatedRealms, Settings globalSettings) {
final Map<String, Settings> settingsByRealm = RealmSettings.getRealmSettings(globalSettings);
for (Realm realm : delegatedRealms) {
final Settings realmSettings = settingsByRealm.get(realm.name());
if (realmSettings != null && DelegatedAuthorizationSettings.AUTHZ_REALMS.exists(realmSettings)) {
throw new IllegalArgumentException("cannot use realm [" + realm +
"] as an authorization realm - it is already delegating authorization to [" +
DelegatedAuthorizationSettings.AUTHZ_REALMS.get(realmSettings) + "]");
}
}
}

private Realm findRealm(String name, Iterable<? extends Realm> allRealms) {
for (Realm realm : allRealms) {
if (name.equals(realm.name())) {
return realm;
}
}
throw new IllegalArgumentException("configured authorizing realm [" + name + "] does not exist (or is not enabled)");
throw new IllegalArgumentException("configured authorization realm [" + name + "] does not exist (or is not enabled)");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,23 @@ public void testMissingRealmInDelegationList() {
final IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () ->
new DelegatedAuthorizationSupport(realms, buildRealmConfig("r", settings), license)
);
assertThat(ex.getMessage(), equalTo("configured authorizing realm [no-such-realm] does not exist (or is not enabled)"));
assertThat(ex.getMessage(), equalTo("configured authorization realm [no-such-realm] does not exist (or is not enabled)"));
}

public void testDelegationChainsAreRejected() {
final XPackLicenseState license = getLicenseState(true);
final Settings settings = Settings.builder()
.putList("authorization_realms", "lookup-1", "lookup-2", "lookup-3")
.build();
globalSettings = Settings.builder()
.put(globalSettings)
.putList("xpack.security.authc.realms.lookup-2.authorization_realms", "lookup-1")
.build();
final IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () ->
new DelegatedAuthorizationSupport(realms, buildRealmConfig("realm1", settings), license)
);
assertThat(ex.getMessage(),
equalTo("cannot use realm [mock/lookup-2] as an authorization realm - it is already delegating authorization to [[lookup-1]]"));
}

public void testMatchInDelegationList() throws Exception {
Expand Down