Browse Source

Allow User/Password realms to disable authc (#34033)

The "lookupUser" method on a realm facilitates the "run-as" and
"authorization_realms" features.
This commit allows a realm to be used for "lookup only", in which
case the "authenticate" method (and associated token methods) are
disabled.
It does this through the introduction of a new
"authentication.enabled" setting, which defaults to true.
Tim Vernum 7 years ago
parent
commit
63dbd1dce0

+ 23 - 0
docs/reference/settings/security-settings.asciidoc

@@ -204,6 +204,11 @@ cache at any given time. Defaults to 100,000.
 in-memory cached user credentials. For possible values, see <<cache-hash-algo>>. 
 Defaults to `ssha256`.
 
+`authentication.enabled`:: If set to `false`, disables authentication support in
+this realm, so that it only supports user lookups.
+(See the {xpack-ref}/run-as-privilege.html[run as] and
+{stack-ov}/realm-chains.html#authorization_realms[authorization realms] features).
+Defaults to `true`.
 
 [[ref-users-settings]]
 
@@ -228,6 +233,12 @@ Defaults to 100,000.
 (Expert Setting) The hashing algorithm that is used for the in-memory cached
 user credentials. See <<cache-hash-algo>>. Defaults to `ssha256`.
 
+`authentication.enabled`:: If set to `false`, disables authentication support in
+this realm, so that it only supports user lookups.
+(See the {xpack-ref}/run-as-privilege.html[run as] and
+{stack-ov}/realm-chains.html#authorization_realms[authorization realms] features).
+Defaults to `true`.
+
 [[ref-ldap-settings]]
 [float]
 ===== LDAP realm settings
@@ -490,6 +501,12 @@ Defaults to `100000`.
 (Expert Setting) Specifies the hashing algorithm that is used for the
 in-memory cached user credentials. See <<cache-hash-algo>>. Defaults to `ssha256`.
 
+`authentication.enabled`:: If set to `false`, disables authentication support in
+this realm, so that it only supports user lookups.
+(See the {xpack-ref}/run-as-privilege.html[run as] and
+{stack-ov}/realm-chains.html#authorization_realms[authorization realms] features).
+Defaults to `true`.
+
 [[ref-ad-settings]]
 [float]
 ===== Active Directory realm settings
@@ -731,6 +748,12 @@ Defaults to `100000`.
 (Expert Setting) Specifies the hashing algorithm that is used for
 the in-memory cached user credentials. See <<cache-hash-algo>>. Defaults to `ssha256`.
 
+`authentication.enabled`:: If set to `false`, disables authentication support in
+this realm, so that it only supports user lookups.
+(See the {xpack-ref}/run-as-privilege.html[run as] and
+{stack-ov}/realm-chains.html#authorization_realms[authorization realms] features).
+Defaults to `true`.
+
 `follow_referrals`::
 If set to `true` {security} follows referrals returned by the LDAP server. 
 Referrals are URLs returned by the server that are to be used to continue the 

+ 1 - 1
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/esnative/NativeRealmSettings.java

@@ -19,6 +19,6 @@ public final class NativeRealmSettings {
      * @return The {@link Setting setting configuration} for this realm type
      */
     public static Set<Setting<?>> getSettings() {
-        return CachingUsernamePasswordRealmSettings.getCachingSettings();
+        return CachingUsernamePasswordRealmSettings.getSettings();
     }
 }

+ 1 - 1
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/file/FileRealmSettings.java

@@ -19,6 +19,6 @@ public final class FileRealmSettings {
      * @return The {@link Setting setting configuration} for this realm type
      */
     public static Set<Setting<?>> getSettings() {
-        return CachingUsernamePasswordRealmSettings.getCachingSettings();
+        return CachingUsernamePasswordRealmSettings.getSettings();
     }
 }

+ 1 - 1
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/ldap/LdapRealmSettings.java

@@ -29,7 +29,7 @@ public final class LdapRealmSettings {
      */
     public static Set<Setting<?>> getSettings(String type) {
         Set<Setting<?>> settings = new HashSet<>();
-        settings.addAll(CachingUsernamePasswordRealmSettings.getCachingSettings());
+        settings.addAll(CachingUsernamePasswordRealmSettings.getSettings());
         settings.addAll(CompositeRoleMapperSettings.getSettings());
         settings.add(LdapRealmSettings.EXECUTION_TIMEOUT);
         if (AD_TYPE.equals(type)) {

+ 5 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/CachingUsernamePasswordRealmSettings.java

@@ -21,12 +21,15 @@ public final class CachingUsernamePasswordRealmSettings {
     public static final Setting<Integer> CACHE_MAX_USERS_SETTING = Setting.intSetting("cache.max_users", DEFAULT_MAX_USERS,
             Setting.Property.NodeScope);
 
+    public static final Setting<Boolean> AUTHC_ENABLED_SETTING = Setting.boolSetting("authentication.enabled", true,
+        Setting.Property.NodeScope);
+
     private CachingUsernamePasswordRealmSettings() {}
 
     /**
      * Returns the {@link Setting setting configuration} that is common for all caching realms
      */
-    public static Set<Setting<?>> getCachingSettings() {
-        return new HashSet<>(Arrays.asList(CACHE_HASH_ALGO_SETTING, CACHE_TTL_SETTING, CACHE_MAX_USERS_SETTING));
+    public static Set<Setting<?>> getSettings() {
+        return new HashSet<>(Arrays.asList(CACHE_HASH_ALGO_SETTING, CACHE_TTL_SETTING, CACHE_MAX_USERS_SETTING, AUTHC_ENABLED_SETTING));
     }
 }

+ 23 - 1
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/support/CachingUsernamePasswordRealm.java

@@ -11,6 +11,7 @@ import org.elasticsearch.common.cache.CacheBuilder;
 import org.elasticsearch.common.settings.SecureString;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.common.util.concurrent.ListenableFuture;
+import org.elasticsearch.common.util.concurrent.ThreadContext;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.xpack.core.security.authc.AuthenticationResult;
 import org.elasticsearch.xpack.core.security.authc.AuthenticationToken;
@@ -30,6 +31,7 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
 
     private final Cache<String, ListenableFuture<UserWithHash>> cache;
     private final ThreadPool threadPool;
+    private final boolean authenticationEnabled;
     final Hasher cacheHasher;
 
     protected CachingUsernamePasswordRealm(String type, RealmConfig config, ThreadPool threadPool) {
@@ -45,6 +47,7 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
         } else {
             cache = null;
         }
+        this.authenticationEnabled = CachingUsernamePasswordRealmSettings.AUTHC_ENABLED_SETTING.get(config.settings());
     }
 
     @Override
@@ -63,15 +66,34 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
         }
     }
 
+    @Override
+    public UsernamePasswordToken token(ThreadContext threadContext) {
+        if (authenticationEnabled == false) {
+            return null;
+        }
+        return super.token(threadContext);
+    }
+
+    @Override
+    public boolean supports(AuthenticationToken token) {
+        return authenticationEnabled && super.supports(token);
+    }
+
     /**
      * If the user exists in the cache (keyed by the principle name), then the password is validated
      * against a hash also stored in the cache.  Otherwise the subclass authenticates the user via
-     * doAuthenticate
+     * doAuthenticate.
+     * This method will respond with {@link AuthenticationResult#notHandled()} if
+     * {@link CachingUsernamePasswordRealmSettings#AUTHC_ENABLED_SETTING authentication is not enabled}.
      * @param authToken The authentication token
      * @param listener to be called at completion
      */
     @Override
     public final void authenticate(AuthenticationToken authToken, ActionListener<AuthenticationResult> listener) {
+        if (authenticationEnabled == false) {
+            listener.onResponse(AuthenticationResult.notHandled());
+            return;
+        }
         final UsernamePasswordToken token = (UsernamePasswordToken) authToken;
         try {
             if (cache == null) {

+ 29 - 1
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/CachingUsernamePasswordRealmTests.java

@@ -11,6 +11,7 @@ import org.elasticsearch.common.settings.SecureString;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.env.Environment;
 import org.elasticsearch.env.TestEnvironment;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.SecuritySettingsSourceField;
@@ -62,7 +63,7 @@ public class CachingUsernamePasswordRealmTests extends ESTestCase {
         }
     }
 
-    public void testSettings() throws Exception {
+    public void testCacheSettings() throws Exception {
         String cachingHashAlgo = Hasher.values()[randomIntBetween(0, Hasher.values().length - 1)].name().toLowerCase(Locale.ROOT);
         int maxUsers = randomIntBetween(10, 100);
         TimeValue ttl = TimeValue.timeValueMinutes(randomIntBetween(10, 20));
@@ -560,6 +561,33 @@ public class CachingUsernamePasswordRealmTests extends ESTestCase {
         assertEquals(1, lookupCounter.get());
     }
 
+    public void testAuthenticateDisabled() throws Exception {
+        final Settings settings = Settings.builder()
+            .put(CachingUsernamePasswordRealmSettings.AUTHC_ENABLED_SETTING.getKey(), false)
+            .build();
+        final Environment env = TestEnvironment.newEnvironment(globalSettings);
+        final ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
+        final RealmConfig config = new RealmConfig("test_authentication_disabled", settings, globalSettings, env, threadContext);
+        final AlwaysAuthenticateCachingRealm realm = new AlwaysAuthenticateCachingRealm(config, threadPool);
+
+        final UsernamePasswordToken token = new UsernamePasswordToken("phil", new SecureString("tahiti"));
+        UsernamePasswordToken.putTokenHeader(threadContext, token);
+        assertThat(realm.token(threadContext), nullValue());
+        assertThat(realm.supports(token), equalTo(false));
+
+        PlainActionFuture<AuthenticationResult> authFuture = new PlainActionFuture<>();
+        realm.authenticate(token, authFuture);
+        final AuthenticationResult authResult = authFuture.get();
+        assertThat(authResult.isAuthenticated(), equalTo(false));
+        assertThat(authResult.getStatus(), equalTo(AuthenticationResult.Status.CONTINUE));
+
+        PlainActionFuture<User> lookupFuture = new PlainActionFuture<>();
+        realm.lookupUser(token.principal(), lookupFuture);
+        final User user = lookupFuture.get();
+        assertThat(user, notNullValue());
+        assertThat(user.principal(), equalTo(token.principal()));
+    }
+
     static class FailingAuthenticationRealm extends CachingUsernamePasswordRealm {
 
         FailingAuthenticationRealm(Settings settings, Settings global, ThreadPool threadPool) {