You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by pr...@apache.org on 2026/08/13 11:05:52 UTC

(ranger) 01/02: RANGER-5740: Align Elasticsearch plugin caller identity with X-Pack Security

This is an automated email from the ASF dual-hosted git repository.

pradeepagrawal8184 pushed a commit to branch RANGER-5740_esplugin
in repository https://gitbox.apache.org/repos/asf/ranger.git

commit 5bedf7536f50e79e05735ae080ec5f412b59a365
Author: Pradeep Agrawal <pa...@cloudera.com>
AuthorDate: Wed Aug 12 12:53:20 2026 +0530

    RANGER-5740: Align Elasticsearch plugin caller identity with X-Pack Security
---
 ranger-elasticsearch-plugin-shim/pom.xml           | 36 ++++++++++
 .../plugin/RangerElasticsearchPlugin.java          |  4 +-
 .../action/filter/RangerSecurityActionFilter.java  | 23 ++++++-
 .../ElasticsearchAuthenticatedUserResolver.java    | 56 +++++++++++++++
 .../plugin/authc/user/UsernamePasswordToken.java   |  8 +++
 .../rest/filter/RangerSecurityRestFilter.java      | 27 +++++---
 ...TestElasticsearchAuthenticatedUserResolver.java | 65 ++++++++++++++++++
 .../rest/filter/TestRangerSecurityRestFilter.java  | 79 ++++++++++++++++++++++
 8 files changed, 282 insertions(+), 16 deletions(-)

diff --git a/ranger-elasticsearch-plugin-shim/pom.xml b/ranger-elasticsearch-plugin-shim/pom.xml
index 9dedb37d1..eaa25d71e 100644
--- a/ranger-elasticsearch-plugin-shim/pom.xml
+++ b/ranger-elasticsearch-plugin-shim/pom.xml
@@ -70,5 +70,41 @@
                 </exclusion>
             </exclusions>
         </dependency>
+        <dependency>
+            <groupId>org.elasticsearch.plugin</groupId>
+            <artifactId>x-pack-core</artifactId>
+            <version>${elasticsearch.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-api</artifactId>
+            <version>${log4j2.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-core</artifactId>
+            <version>${log4j2.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>${junit.jupiter.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>${mockito.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-junit-jupiter</artifactId>
+            <version>${mockito.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/RangerElasticsearchPlugin.java b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/RangerElasticsearchPlugin.java
index aa1c10a30..e3b740f00 100644
--- a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/RangerElasticsearchPlugin.java
+++ b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/RangerElasticsearchPlugin.java
@@ -72,7 +72,7 @@ public List<ActionFilter> getActionFilters() {
 
     @Override
     public UnaryOperator<RestHandler> getRestHandlerWrapper(ThreadContext threadContext) {
-        return handler -> new RangerSecurityRestFilter(threadContext, handler);
+        return handler -> new RangerSecurityRestFilter(settings, threadContext, handler);
     }
 
     @Override
@@ -81,7 +81,7 @@ public Collection<Object> createComponents(final Client client, final ClusterSer
             final NamedWriteableRegistry namedWriteableRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Supplier<RepositoriesService> repositoriesServiceSupplier) {
         addPluginConfig2Classpath(environment);
 
-        rangerSecurityActionFilter = new RangerSecurityActionFilter(threadPool.getThreadContext());
+        rangerSecurityActionFilter = new RangerSecurityActionFilter(settings, threadPool.getThreadContext());
 
         return Collections.singletonList(rangerSecurityActionFilter);
     }
diff --git a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/action/filter/RangerSecurityActionFilter.java b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/action/filter/RangerSecurityActionFilter.java
index 31af2e3a4..1d70e3b0a 100644
--- a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/action/filter/RangerSecurityActionFilter.java
+++ b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/action/filter/RangerSecurityActionFilter.java
@@ -19,6 +19,7 @@
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.ranger.authorization.elasticsearch.authorizer.RangerElasticsearchAuthorizer;
+import org.apache.ranger.authorization.elasticsearch.plugin.authc.ElasticsearchAuthenticatedUserResolver;
 import org.apache.ranger.authorization.elasticsearch.plugin.authc.user.UsernamePasswordToken;
 import org.apache.ranger.authorization.elasticsearch.plugin.utils.RequestUtils;
 import org.elasticsearch.ElasticsearchStatusException;
@@ -28,6 +29,7 @@
 import org.elasticsearch.action.support.ActionFilter;
 import org.elasticsearch.action.support.ActionFilterChain;
 import org.elasticsearch.common.component.AbstractLifecycleComponent;
+import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.util.concurrent.ThreadContext;
 import org.elasticsearch.rest.RestStatus;
 import org.elasticsearch.tasks.Task;
@@ -39,12 +41,14 @@
 public class RangerSecurityActionFilter extends AbstractLifecycleComponent implements ActionFilter {
     private static final Logger LOG = LoggerFactory.getLogger(RangerSecurityActionFilter.class);
 
+    private final Settings                      settings;
     private final ThreadContext                 threadContext;
     private final RangerElasticsearchAuthorizer rangerElasticsearchAuthorizer = new RangerElasticsearchAuthorizer();
 
-    public RangerSecurityActionFilter(ThreadContext threadContext) {
+    public RangerSecurityActionFilter(Settings settings, ThreadContext threadContext) {
         super();
 
+        this.settings      = settings;
         this.threadContext = threadContext;
     }
 
@@ -57,7 +61,18 @@ public int order() {
     public <Request extends ActionRequest, Response extends ActionResponse> void apply(Task task, String action, Request request, ActionListener<Response> listener, ActionFilterChain<Request, Response> chain) {
         String user = threadContext.getTransient(UsernamePasswordToken.USERNAME);
 
-        // If user is not null, then should check permission of the outside caller.
+        if (StringUtils.isEmpty(user)) {
+            ElasticsearchAuthenticatedUserResolver authResolver = new ElasticsearchAuthenticatedUserResolver(settings, threadContext);
+
+            if (authResolver.requiresAuthenticatedUser()) {
+                user = authResolver.resolveUsername();
+
+                if (StringUtils.isNotEmpty(user)) {
+                    threadContext.putTransient(UsernamePasswordToken.USERNAME, user);
+                }
+            }
+        }
+
         if (StringUtils.isNotEmpty(user)) {
             List<String> indexs          = RequestUtils.getIndexFromRequest(request);
             String       clientIPAddress = threadContext.getTransient(RequestUtils.CLIENT_IP_ADDRESS);
@@ -71,8 +86,10 @@ public <Request extends ActionRequest, Response extends ActionResponse> void app
                     throw new ElasticsearchStatusException(errorMsg, RestStatus.FORBIDDEN, user, action, index);
                 }
             }
+        } else if (threadContext.isSystemContext()) {
+            LOG.debug("System context request, skipping Ranger permission check for action[{}].", action);
         } else {
-            LOG.debug("User is null, no check permission for elasticsearch do action[{}] with request[{}]", action, request);
+            throw new ElasticsearchStatusException("Error: Request requires authenticated user.", RestStatus.UNAUTHORIZED);
         }
 
         chain.proceed(task, action, request, listener);
diff --git a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/authc/ElasticsearchAuthenticatedUserResolver.java b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/authc/ElasticsearchAuthenticatedUserResolver.java
new file mode 100644
index 000000000..823d9502e
--- /dev/null
+++ b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/authc/ElasticsearchAuthenticatedUserResolver.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ranger.authorization.elasticsearch.plugin.authc;
+
+import org.apache.commons.lang3.StringUtils;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.xpack.core.security.SecurityContext;
+import org.elasticsearch.xpack.core.security.user.User;
+
+/**
+ * Resolves the Elasticsearch-verified user for the current request.
+ * Caller identity must come from X-Pack Security, not from client-supplied headers.
+ */
+public class ElasticsearchAuthenticatedUserResolver {
+    private final SecurityContext securityContext;
+    private final ThreadContext   threadContext;
+
+    public ElasticsearchAuthenticatedUserResolver(Settings settings, ThreadContext threadContext) {
+        this.securityContext = new SecurityContext(settings, threadContext);
+        this.threadContext   = threadContext;
+    }
+
+    public boolean requiresAuthenticatedUser() {
+        return !threadContext.isSystemContext();
+    }
+
+    public String resolveUsername() {
+        String username = null;
+
+        if (requiresAuthenticatedUser()) {
+            User user = securityContext.getUser();
+
+            if (user != null) {
+                username = user.principal();
+            }
+        }
+
+        return StringUtils.isEmpty(username) ? null : username;
+    }
+}
diff --git a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/authc/user/UsernamePasswordToken.java b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/authc/user/UsernamePasswordToken.java
index 958cda381..4b964f6a3 100644
--- a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/authc/user/UsernamePasswordToken.java
+++ b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/authc/user/UsernamePasswordToken.java
@@ -28,6 +28,10 @@
 import java.util.List;
 import java.util.Map;
 
+/**
+ * Legacy helper for parsing Basic credentials from a REST request.
+ * Parsed credentials must not be used as an authenticated identity.
+ */
 public class UsernamePasswordToken {
     public static final String USERNAME          = "username";
     public static final String BASIC_AUTH_PREFIX = "Basic ";
@@ -41,6 +45,10 @@ public UsernamePasswordToken(String username, String password) {
         this.password = password;
     }
 
+    /**
+     * @deprecated Do not use parsed Basic credentials for authorization decisions.
+     */
+    @Deprecated
     public static UsernamePasswordToken parseToken(RestRequest request) {
         Map<String, List<String>> headers = request.getHeaders();
 
diff --git a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/rest/filter/RangerSecurityRestFilter.java b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/rest/filter/RangerSecurityRestFilter.java
index 09b15cdc2..97a0234c4 100644
--- a/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/rest/filter/RangerSecurityRestFilter.java
+++ b/ranger-elasticsearch-plugin-shim/src/main/java/org/apache/ranger/authorization/elasticsearch/plugin/rest/filter/RangerSecurityRestFilter.java
@@ -18,11 +18,13 @@
 package org.apache.ranger.authorization.elasticsearch.plugin.rest.filter;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.ranger.authorization.elasticsearch.plugin.authc.ElasticsearchAuthenticatedUserResolver;
 import org.apache.ranger.authorization.elasticsearch.plugin.authc.user.UsernamePasswordToken;
 import org.apache.ranger.authorization.elasticsearch.plugin.utils.RequestUtils;
 import org.elasticsearch.ElasticsearchStatusException;
 import org.elasticsearch.client.node.NodeClient;
 import org.elasticsearch.common.component.AbstractLifecycleComponent;
+import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.util.concurrent.ThreadContext;
 import org.elasticsearch.rest.RestChannel;
 import org.elasticsearch.rest.RestHandler;
@@ -34,29 +36,32 @@
 public class RangerSecurityRestFilter extends AbstractLifecycleComponent implements RestHandler {
     private static final Logger LOG = LoggerFactory.getLogger(RangerSecurityRestFilter.class);
 
+    private final Settings      settings;
     private final RestHandler   restHandler;
     private final ThreadContext threadContext;
 
-    public RangerSecurityRestFilter(final ThreadContext threadContext, final RestHandler restHandler) {
+    public RangerSecurityRestFilter(final Settings settings, final ThreadContext threadContext, final RestHandler restHandler) {
         super();
 
+        this.settings      = settings;
         this.restHandler   = restHandler;
         this.threadContext = threadContext;
     }
 
     @Override
     public void handleRequest(final RestRequest request, final RestChannel channel, final NodeClient client) throws Exception {
-        // Now only support to get user from request,
-        // it should work with other elasticsearch identity authentication plugins in fact.
-        UsernamePasswordToken user = UsernamePasswordToken.parseToken(request);
-
-        if (user == null) {
-            throw new ElasticsearchStatusException("Error: User is null, the request requires user authentication.", RestStatus.UNAUTHORIZED);
-        } else {
-            LOG.debug("Success to parse user[{}] from request[{}].", user, request);
-        }
+        ElasticsearchAuthenticatedUserResolver authResolver = new ElasticsearchAuthenticatedUserResolver(settings, threadContext);
+
+        if (authResolver.requiresAuthenticatedUser()) {
+            String username = authResolver.resolveUsername();
 
-        threadContext.putTransient(UsernamePasswordToken.USERNAME, user.getUsername());
+            if (StringUtils.isEmpty(username)) {
+                throw new ElasticsearchStatusException("Error: Request requires authenticated user.", RestStatus.UNAUTHORIZED);
+            }
+
+            threadContext.putTransient(UsernamePasswordToken.USERNAME, username);
+            LOG.debug("Using Elasticsearch-verified user[{}] for request[{}].", username, request);
+        }
 
         String clientIPAddress = RequestUtils.getClientIPAddress(request);
 
diff --git a/ranger-elasticsearch-plugin-shim/src/test/java/org/apache/ranger/authorization/elasticsearch/plugin/authc/TestElasticsearchAuthenticatedUserResolver.java b/ranger-elasticsearch-plugin-shim/src/test/java/org/apache/ranger/authorization/elasticsearch/plugin/authc/TestElasticsearchAuthenticatedUserResolver.java
new file mode 100644
index 000000000..c3d0f9a42
--- /dev/null
+++ b/ranger-elasticsearch-plugin-shim/src/test/java/org/apache/ranger/authorization/elasticsearch/plugin/authc/TestElasticsearchAuthenticatedUserResolver.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ranger.authorization.elasticsearch.plugin.authc;
+
+import org.elasticsearch.Version;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.xpack.core.security.SecurityContext;
+import org.elasticsearch.xpack.core.security.user.User;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class TestElasticsearchAuthenticatedUserResolver {
+    @Test
+    void resolveUsernameReturnsVerifiedUser() {
+        Settings      settings      = Settings.builder().build();
+        ThreadContext threadContext = new ThreadContext(settings);
+        SecurityContext securityContext = new SecurityContext(settings, threadContext);
+
+        securityContext.setUser(new User("admin"), Version.CURRENT);
+
+        ElasticsearchAuthenticatedUserResolver resolver = new ElasticsearchAuthenticatedUserResolver(settings, threadContext);
+
+        Assertions.assertTrue(resolver.requiresAuthenticatedUser());
+        Assertions.assertEquals("admin", resolver.resolveUsername());
+    }
+
+    @Test
+    void resolveUsernameReturnsNullWithoutVerifiedUser() {
+        Settings      settings      = Settings.builder().build();
+        ThreadContext threadContext = new ThreadContext(settings);
+        ElasticsearchAuthenticatedUserResolver resolver = new ElasticsearchAuthenticatedUserResolver(settings, threadContext);
+
+        Assertions.assertTrue(resolver.requiresAuthenticatedUser());
+        Assertions.assertNull(resolver.resolveUsername());
+    }
+
+    @Test
+    void systemContextDoesNotRequireAuthenticatedUser() {
+        Settings      settings      = Settings.builder().build();
+        ThreadContext threadContext = new ThreadContext(settings);
+
+        threadContext.markAsSystemContext();
+
+        ElasticsearchAuthenticatedUserResolver resolver = new ElasticsearchAuthenticatedUserResolver(settings, threadContext);
+
+        Assertions.assertFalse(resolver.requiresAuthenticatedUser());
+        Assertions.assertNull(resolver.resolveUsername());
+    }
+}
diff --git a/ranger-elasticsearch-plugin-shim/src/test/java/org/apache/ranger/authorization/elasticsearch/plugin/rest/filter/TestRangerSecurityRestFilter.java b/ranger-elasticsearch-plugin-shim/src/test/java/org/apache/ranger/authorization/elasticsearch/plugin/rest/filter/TestRangerSecurityRestFilter.java
new file mode 100644
index 000000000..171861bc9
--- /dev/null
+++ b/ranger-elasticsearch-plugin-shim/src/test/java/org/apache/ranger/authorization/elasticsearch/plugin/rest/filter/TestRangerSecurityRestFilter.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ranger.authorization.elasticsearch.plugin.rest.filter;
+
+import org.elasticsearch.ElasticsearchStatusException;
+import org.elasticsearch.Version;
+import org.elasticsearch.client.node.NodeClient;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.http.HttpChannel;
+import org.elasticsearch.rest.RestChannel;
+import org.elasticsearch.rest.RestHandler;
+import org.elasticsearch.rest.RestRequest;
+import org.elasticsearch.rest.RestStatus;
+import org.elasticsearch.xpack.core.security.SecurityContext;
+import org.elasticsearch.xpack.core.security.user.User;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+class TestRangerSecurityRestFilter {
+    @Test
+    void rejectsUnverifiedBasicCredentials() {
+        Settings      settings      = Settings.builder().build();
+        ThreadContext threadContext = new ThreadContext(settings);
+        RestHandler   restHandler   = Mockito.mock(RestHandler.class);
+        RestRequest   request       = Mockito.mock(RestRequest.class);
+        RestChannel   channel       = Mockito.mock(RestChannel.class);
+        NodeClient    client        = Mockito.mock(NodeClient.class);
+        RangerSecurityRestFilter filter = new RangerSecurityRestFilter(settings, threadContext, restHandler);
+
+        Mockito.when(request.getHeaders()).thenReturn(java.util.Collections.singletonMap(
+                "Authorization", java.util.Collections.singletonList("Basic YWRtaW46d3JvbmdwYXNzd29yZA==")));
+
+        ElasticsearchStatusException exception = Assertions.assertThrows(
+                ElasticsearchStatusException.class,
+                () -> filter.handleRequest(request, channel, client));
+
+        Assertions.assertEquals(RestStatus.UNAUTHORIZED, exception.status());
+        Mockito.verifyNoInteractions(restHandler);
+    }
+
+    @Test
+    void acceptsElasticsearchVerifiedUser() throws Exception {
+        Settings        settings        = Settings.builder().build();
+        ThreadContext   threadContext   = new ThreadContext(settings);
+        SecurityContext securityContext = new SecurityContext(settings, threadContext);
+        RestHandler     restHandler     = Mockito.mock(RestHandler.class);
+        RestRequest     request         = Mockito.mock(RestRequest.class);
+        RestChannel     channel         = Mockito.mock(RestChannel.class);
+        NodeClient      client          = Mockito.mock(NodeClient.class);
+        HttpChannel     httpChannel     = Mockito.mock(HttpChannel.class);
+        RangerSecurityRestFilter filter = new RangerSecurityRestFilter(settings, threadContext, restHandler);
+
+        Mockito.when(request.getHttpChannel()).thenReturn(httpChannel);
+        Mockito.when(httpChannel.getRemoteAddress()).thenReturn(null);
+
+        securityContext.setUser(new User("admin"), Version.CURRENT);
+
+        filter.handleRequest(request, channel, client);
+
+        Mockito.verify(restHandler).handleRequest(request, channel, client);
+    }
+}