Ver Fonte

Check for disabling own user in Put User API (#112262)

Prior to this commit, the Put User API could be used to disable one's own user in the native realm, despite this being prevented in other APIs.

This commit adds a check to the Put User API to prevent native-realm users from disabling their own accounts.
Athena Brown há 1 ano atrás
pai
commit
fb14e07d83

+ 6 - 0
docs/changelog/112262.yaml

@@ -0,0 +1,6 @@
+pr: 112262
+summary: Check for disabling own user in Put User API
+area: Authentication
+type: bug
+issues:
+ - 90205

+ 18 - 0
x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java

@@ -8,6 +8,7 @@ package org.elasticsearch.xpack.security.authc.esnative;
 
 import org.apache.lucene.util.CollectionUtil;
 import org.elasticsearch.ElasticsearchSecurityException;
+import org.elasticsearch.action.ActionRequestValidationException;
 import org.elasticsearch.action.ActionResponse;
 import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
 import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse;
@@ -147,6 +148,23 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
         assertFalse("role shouldn't be found", resp2.found());
     }
 
+    public void testDisablingOwnUser() throws Exception {
+        preparePutUser("joe", "s3krit-password", hasher, SecuritySettingsSource.TEST_ROLE).get();
+        GetUsersResponse resp = new GetUsersRequestBuilder(client()).usernames("joe").get();
+        assertTrue("user should exist", resp.hasUsers());
+        String token = basicAuthHeaderValue("joe", new SecureString("s3krit-password"));
+        var joeClient = client().filterWithHeader(Collections.singletonMap("Authorization", token));
+        var joeSelfDisableRequest = new PutUserRequestBuilder(joeClient).username("joe")
+            .password("s3krit-password".toCharArray(), hasher)
+            .roles(SecuritySettingsSource.TEST_ROLE)
+            .enabled(false);
+        ActionRequestValidationException ex = expectThrows(ActionRequestValidationException.class, joeSelfDisableRequest::get);
+        assertThat(
+            ex.getMessage(),
+            containsString("native and reserved realm users may not update the enabled status of their own account")
+        );
+    }
+
     public void testGettingUserThatDoesntExist() throws Exception {
         GetUsersResponse resp = new GetUsersRequestBuilder(client()).usernames("joe").get();
         assertFalse("user should not exist", resp.hasUsers());

+ 23 - 0
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/user/TransportPutUserAction.java

@@ -16,10 +16,12 @@ import org.elasticsearch.common.util.concurrent.EsExecutors;
 import org.elasticsearch.injection.guice.Inject;
 import org.elasticsearch.tasks.Task;
 import org.elasticsearch.transport.TransportService;
+import org.elasticsearch.xpack.core.security.SecurityContext;
 import org.elasticsearch.xpack.core.security.action.user.PutUserAction;
 import org.elasticsearch.xpack.core.security.action.user.PutUserRequest;
 import org.elasticsearch.xpack.core.security.action.user.PutUserResponse;
 import org.elasticsearch.xpack.core.security.authc.esnative.ClientReservedRealm;
+import org.elasticsearch.xpack.core.security.authc.esnative.NativeRealmSettings;
 import org.elasticsearch.xpack.core.security.support.NativeRealmValidationUtil;
 import org.elasticsearch.xpack.core.security.support.Validation;
 import org.elasticsearch.xpack.core.security.user.AnonymousUser;
@@ -31,17 +33,20 @@ public class TransportPutUserAction extends HandledTransportAction<PutUserReques
 
     private final Settings settings;
     private final NativeUsersStore usersStore;
+    private final SecurityContext securityContext;
 
     @Inject
     public TransportPutUserAction(
         Settings settings,
         ActionFilters actionFilters,
         NativeUsersStore usersStore,
+        SecurityContext securityContext,
         TransportService transportService
     ) {
         super(PutUserAction.NAME, transportService, actionFilters, PutUserRequest::new, EsExecutors.DIRECT_EXECUTOR_SERVICE);
         this.settings = settings;
         this.usersStore = usersStore;
+        this.securityContext = securityContext;
     }
 
     @Override
@@ -73,6 +78,12 @@ public class TransportPutUserAction extends HandledTransportAction<PutUserReques
     private ActionRequestValidationException validateRequest(PutUserRequest request) {
         ActionRequestValidationException validationException = null;
         final String username = request.username();
+        if (isDisablingOwnUser(request)) {
+            validationException = addValidationError(
+                "native and reserved realm users may not update the enabled status of their own account",
+                validationException
+            );
+        }
         if (ClientReservedRealm.isReserved(username, settings)) {
             if (AnonymousUser.isAnonymousUsername(username, settings)) {
                 validationException = addValidationError(
@@ -102,4 +113,16 @@ public class TransportPutUserAction extends HandledTransportAction<PutUserReques
         }
         return validationException;
     }
+
+    private boolean isDisablingOwnUser(PutUserRequest request) {
+        if (request.enabled() == false) {
+            final var effectiveSubject = securityContext.getAuthentication().getEffectiveSubject();
+            final var realmType = effectiveSubject.getRealm().getType();
+            // Only native or reserved realm users can be disabled via the API. If the realm of the effective subject is neither,
+            // the target must be a different user
+            return (NativeRealmSettings.TYPE.equals(realmType)) && effectiveSubject.getUser().principal().equals(request.username());
+        }
+        return false;
+    }
+
 }

+ 46 - 5
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/user/TransportPutUserActionTests.java

@@ -21,6 +21,7 @@ import org.elasticsearch.test.SecuritySettingsSourceField;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.Transport;
 import org.elasticsearch.transport.TransportService;
+import org.elasticsearch.xpack.core.security.SecurityContext;
 import org.elasticsearch.xpack.core.security.action.user.PutUserRequest;
 import org.elasticsearch.xpack.core.security.action.user.PutUserResponse;
 import org.elasticsearch.xpack.core.security.authc.AuthenticationTestHelper;
@@ -69,7 +70,15 @@ public class TransportPutUserActionTests extends ESTestCase {
             null,
             Collections.emptySet()
         );
-        TransportPutUserAction action = new TransportPutUserAction(settings, mock(ActionFilters.class), usersStore, transportService);
+        final ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
+        final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
+        TransportPutUserAction action = new TransportPutUserAction(
+            settings,
+            mock(ActionFilters.class),
+            usersStore,
+            securityContext,
+            transportService
+        );
 
         PutUserRequest request = new PutUserRequest();
         request.username(anonymousUser.principal());
@@ -119,7 +128,15 @@ public class TransportPutUserActionTests extends ESTestCase {
             null,
             Collections.emptySet()
         );
-        TransportPutUserAction action = new TransportPutUserAction(Settings.EMPTY, mock(ActionFilters.class), usersStore, transportService);
+        final ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
+        final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
+        TransportPutUserAction action = new TransportPutUserAction(
+            Settings.EMPTY,
+            mock(ActionFilters.class),
+            usersStore,
+            securityContext,
+            transportService
+        );
 
         PutUserRequest request = new PutUserRequest();
         request.username(reserved.principal());
@@ -166,7 +183,15 @@ public class TransportPutUserActionTests extends ESTestCase {
             null,
             Collections.emptySet()
         );
-        TransportPutUserAction action = new TransportPutUserAction(Settings.EMPTY, mock(ActionFilters.class), usersStore, transportService);
+        final ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
+        final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
+        TransportPutUserAction action = new TransportPutUserAction(
+            Settings.EMPTY,
+            mock(ActionFilters.class),
+            usersStore,
+            securityContext,
+            transportService
+        );
 
         final boolean isCreate = randomBoolean();
         final PutUserRequest request = new PutUserRequest();
@@ -230,7 +255,15 @@ public class TransportPutUserActionTests extends ESTestCase {
             null,
             Collections.emptySet()
         );
-        TransportPutUserAction action = new TransportPutUserAction(Settings.EMPTY, mock(ActionFilters.class), usersStore, transportService);
+        final ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
+        final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
+        TransportPutUserAction action = new TransportPutUserAction(
+            Settings.EMPTY,
+            mock(ActionFilters.class),
+            usersStore,
+            securityContext,
+            transportService
+        );
 
         final PutUserRequest request = new PutUserRequest();
         request.username(user.principal());
@@ -291,7 +324,15 @@ public class TransportPutUserActionTests extends ESTestCase {
             null,
             Collections.emptySet()
         );
-        TransportPutUserAction action = new TransportPutUserAction(Settings.EMPTY, mock(ActionFilters.class), usersStore, transportService);
+        final ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
+        final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
+        TransportPutUserAction action = new TransportPutUserAction(
+            Settings.EMPTY,
+            mock(ActionFilters.class),
+            usersStore,
+            securityContext,
+            transportService
+        );
 
         final PutUserRequest request = new PutUserRequest();
         request.username(user.principal());