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

arawat pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/impala.git


The following commit(s) were added to refs/heads/master by this push:
     new 8fea75cb5 IMPALA-13312: Use client address from X-Forwarded-For Header 
in Ranger Audit Logs
8fea75cb5 is described below

commit 8fea75cb5ce206ad071859bb331fa4811573cf4b
Author: Abhishek Rawat <[email protected]>
AuthorDate: Tue Sep 10 15:50:06 2024 -0700

    IMPALA-13312: Use client address from X-Forwarded-For Header in Ranger 
Audit Logs
    
    Added backend flag 'use_xff_address_as_origin' for using the client IP
    address from 'X-Forwarded-For' HTTP header as the origin of HTTP
    connection. The origin IP address in the SessionState is used by the
    ranger client for both authorization (RangerAccessRequestImpl) and
    auditing (RangerBufferAuditHandler). Impala does not do any verification
    or sanitization of this IP address, so its value should only be trusted
    if the deployment environment protects against spoofing.
    
    Also, added a new function 'GetXFFOriginClientAddress' for parsing XFF
    header with comma separated IP addresses, which is the most common form
    of XFF header representing client and intermediate proxies:
    X-Forwarded-For: <client>, <proxy1>, <proxy2>
    
    'GetXFFOriginClientAddress' is now also used for getting the client IP
    from XFF header in existing use cases such as trusted domain based
    authentication for both HS2 HTTP server and web server.
    
    Testing:
    - Added unit tests for the new GetXFFOriginClientAddress function for
    parsing comma separated IP addresses in XFF header
    - Updated existing tests for trusted domain authentication to use
    XFF with comma separated IP addresses
    - Added custom cluster test which ensures that client IP address from
    XFF header is included in the ranger audit logs.
    
    Change-Id: Ib784ad805c649e9576ef34f125509c904b7773ab
    Reviewed-on: http://gerrit.cloudera.org:8080/21780
    Reviewed-by: Abhishek Rawat <[email protected]>
    Tested-by: Impala Public Jenkins <[email protected]>
---
 be/src/rpc/authentication-test.cc                  | 36 ++++++++++
 be/src/rpc/authentication-util.cc                  | 18 +++++
 be/src/rpc/authentication-util.h                   |  5 ++
 be/src/rpc/authentication.cc                       | 23 +++++-
 be/src/transport/THttpServer.cpp                   | 11 ++-
 be/src/util/webserver.cc                           | 13 ++--
 .../apache/impala/customcluster/LdapHS2Test.java   |  4 +-
 .../impala/customcluster/LdapWebserverTest.java    |  2 +-
 tests/authorization/test_ranger.py                 | 83 +++++++++++++++++++++
 tests/custom_cluster/test_shell_jwt_auth.py        | 84 ++++++++--------------
 10 files changed, 212 insertions(+), 67 deletions(-)

diff --git a/be/src/rpc/authentication-test.cc 
b/be/src/rpc/authentication-test.cc
index b2c78cfcd..26b2db72e 100644
--- a/be/src/rpc/authentication-test.cc
+++ b/be/src/rpc/authentication-test.cc
@@ -20,6 +20,7 @@
 #include "common/logging.h"
 #include "kudu/security/test/mini_kdc.h"
 #include "rpc/authentication.h"
+#include "rpc/authentication-util.h"
 #include "rpc/thrift-server.h"
 #include "util/auth-util.h"
 #include "util/kudu-status-util.h"
@@ -280,6 +281,41 @@ TEST(Auth, LdapKerbAuthCustomFiltersNotAllowed) {
   ASSERT_FALSE(ap->is_secure());
 }
 
+TEST(Auth, GetXFFOriginClientAddress) {
+  // Empty XFF address
+  std::string xff_addresses;
+  std::string origin;
+  Status status = GetXFFOriginClientAddress(xff_addresses, origin);
+  ASSERT_EQ(status.msg().msg(), "Empty XFF header");
+
+  // Empty ',' separated XFF address
+  xff_addresses = " ,";
+  status = GetXFFOriginClientAddress(xff_addresses, origin);
+  ASSERT_EQ(status.msg().msg(), "Empty XFF header");
+
+  // Multiple XFF addresses
+  xff_addresses = "  10.96.11.12, 172.45.88.78, 176.45.88.65";
+  status = GetXFFOriginClientAddress(xff_addresses, origin);
+  ASSERT_EQ(origin, "10.96.11.12");
+  ASSERT_TRUE(status.ok());
+
+  // Single XFF address
+  xff_addresses = "10.95.66.23";
+  status = GetXFFOriginClientAddress(xff_addresses, origin);
+  ASSERT_EQ(origin, "10.95.66.23");
+  ASSERT_TRUE(status.ok());
+
+  xff_addresses = "      10.90.11.23, ";
+  status = GetXFFOriginClientAddress(xff_addresses, origin);
+  ASSERT_EQ(origin, "10.90.11.23");
+  ASSERT_TRUE(status.ok());
+
+  xff_addresses = " 10.90.11.23 , ";
+  status = GetXFFOriginClientAddress(xff_addresses, origin);
+  ASSERT_EQ(origin, "10.90.11.23");
+  ASSERT_TRUE(status.ok());
+}
+
 }
 int main(int argc, char** argv) {
   ::testing::InitGoogleTest(&argc, argv);
diff --git a/be/src/rpc/authentication-util.cc 
b/be/src/rpc/authentication-util.cc
index 6a9ec41c7..05ddc0982 100644
--- a/be/src/rpc/authentication-util.cc
+++ b/be/src/rpc/authentication-util.cc
@@ -211,6 +211,24 @@ bool IsTrustedDomain(const std::string& origin, const 
std::string& trusted_domai
   return HasSuffixString(host_name, trusted_domain);
 }
 
+Status GetXFFOriginClientAddress(const std::string_view& xff_addresses,
+    std::string& origin) {
+  if (xff_addresses.empty()) {
+    return Status::Expected("Empty XFF header");
+  }
+  const int pos = xff_addresses.find_first_of(',');
+  if (pos == xff_addresses.npos) {
+    origin = xff_addresses;
+  } else {
+    origin = xff_addresses.substr(0, pos);
+  }
+  StripWhiteSpace(&origin);
+  if (origin.empty()) {
+    return Status::Expected("Empty XFF header");
+  }
+  return Status::OK();
+}
+
 Status BasicAuthExtractCredentials(
     const string& token, string& username, string& password) {
   if (token.empty()) {
diff --git a/be/src/rpc/authentication-util.h b/be/src/rpc/authentication-util.h
index efd3ae5da..eec497612 100644
--- a/be/src/rpc/authentication-util.h
+++ b/be/src/rpc/authentication-util.h
@@ -51,6 +51,11 @@ std::string GetDeleteCookie();
 bool IsTrustedDomain(const std::string& origin, const std::string& 
trusted_domain,
     bool strict_localhost);
 
+// Returns the origin client address from a comma separated list of addresses. 
The first
+// address in the comma separated list is considered the origin client address.
+Status GetXFFOriginClientAddress(const std::string_view& xff_addresses,
+    std::string& origin);
+
 // Takes in the base64 encoded token and returns the username and password via 
the input
 // arguments. Returns an OK status if the token is a valid base64 encoded 
string of the
 // form <username>:<password>, an error status otherwise.
diff --git a/be/src/rpc/authentication.cc b/be/src/rpc/authentication.cc
index 21031e52b..c27187d77 100644
--- a/be/src/rpc/authentication.cc
+++ b/be/src/rpc/authentication.cc
@@ -127,17 +127,17 @@ DEFINE_string(trusted_domain, "",
     "<username>:<password> where the password is not used and can be left 
blank.");
 
 DEFINE_bool(trusted_domain_use_xff_header, false,
-    "If set to true, this uses the 'X-Forwarded-For' HTML header to check for 
origin "
+    "If set to true, this uses the 'X-Forwarded-For' HTTP header to check for 
origin "
     "while attempting to verify if the connection request originated from a 
trusted "
     "domain. Only used if '--trusted_domain' is specified. Warning: Only use 
this if you "
     "trust the incoming connection to have this set correctly.");
 
 DEFINE_bool(trusted_domain_empty_xff_header_use_origin, false,
-    "If set to true and the 'X-Forwarded-For' HTML header value is empty in 
the request, "
+    "If set to true and the 'X-Forwarded-For' HTTP header value is empty in 
the request, "
     "then the origin of the the underlying transport is used while attempting 
to "
     "verify if the connection request originated from a trusted domain. Only 
used "
     "if '--trusted_domain' and '--trusted_domain_use_xff_header' flags are 
specified. "
-    "Warning: In case the 'X-Forwarded-For' HTML header is empty or not in the 
request, "
+    "Warning: In case the 'X-Forwarded-For' HTTP header is empty or not in the 
request, "
     "this flag allows a fallback to the default behavior in trusted domain 
check "
     "(where '--trusted_domain' flag is specified, but 
'--trusted_domain_use_xff_header' "
     "flag is not set).");
@@ -148,6 +148,14 @@ DEFINE_bool(trusted_domain_strict_localhost, true,
     "important for security, because reverse DNS can resolve other non-local 
addresses "
     "to localhost.");
 
+DEFINE_bool(use_xff_address_as_origin, false,
+    "If set to true use the address in 'X-Forwarded-For' HTTP header as origin 
of the "
+    "connection. If XFF header is not set then the Peer Address on the 
underlying socket "
+    "is used as the origin. If XFF header has multiple addresses corresponding 
to the "
+    "various intermediate proxies, then the first address is used as the 
origin. XFF "
+    "header with multiple IP addresses must be comma separated. Only use this 
if you "
+    "trust the incoming connection to have the XFF header set correctly.");
+
 // This flag must be used with caution to avoid security risks.
 DEFINE_string(trusted_auth_header, "",
     "If set as non empty string, Impala will look for this header in the HTTP 
headers. "
@@ -639,6 +647,15 @@ bool GetUsernameFromBasicAuthHeader(
 bool SetOrigin(
     ThriftServer::ConnectionContext* connection_context, const std::string& 
origin) {
   connection_context->http_origin = origin;
+  if (FLAGS_use_xff_address_as_origin) {
+    std::string origin_client;
+    Status status = GetXFFOriginClientAddress(origin, origin_client);
+    if (!status.ok()) {
+      LOG(ERROR) << "Error parsing XFF header: " << status;
+    } else {
+      connection_context->network_address = MakeNetworkAddress(origin_client);
+    }
+  }
   return false;
 }
 
diff --git a/be/src/transport/THttpServer.cpp b/be/src/transport/THttpServer.cpp
index 26481e781..482ec79f8 100644
--- a/be/src/transport/THttpServer.cpp
+++ b/be/src/transport/THttpServer.cpp
@@ -34,6 +34,8 @@
 #include "gen-cpp/Frontend_types.h"
 #include "util/metrics.h"
 #include "common/logging.h"
+#include "common/status.h"
+#include "rpc/authentication-util.h"
 
 DECLARE_bool(trusted_domain_use_xff_header);
 DECLARE_bool(trusted_domain_empty_xff_header_use_origin);
@@ -374,8 +376,13 @@ void THttpServer::headersDone() {
   // the client started the SAML workflow then it doesn't expect Impala to 
succeed with
   // another mechanism.
   if (!authorized && check_trusted_domain_) {
-    string origin =
-        FLAGS_trusted_domain_use_xff_header ? origin_ : 
transport_->getOrigin();
+    string origin;
+    if (FLAGS_trusted_domain_use_xff_header) {
+      impala::Status status = impala::GetXFFOriginClientAddress(origin_, 
origin);
+      if (!status.ok()) LOG(WARNING) << status.GetDetail();
+    } else {
+      origin = transport_->getOrigin();
+    }
     StripWhiteSpace(&origin);
     if (origin.empty() && FLAGS_trusted_domain_use_xff_header &&
         FLAGS_trusted_domain_empty_xff_header_use_origin) {
diff --git a/be/src/util/webserver.cc b/be/src/util/webserver.cc
index daa9a3f78..543a6e1da 100644
--- a/be/src/util/webserver.cc
+++ b/be/src/util/webserver.cc
@@ -742,10 +742,15 @@ sq_callback_result_t 
Webserver::BeginRequestCallback(struct sq_connection* conne
   // unpredictably costly.
   if (!authenticated && check_trusted_domain_) {
     const char* xff_origin = sq_get_header(connection, "X-Forwarded-For");
-    string xff_origin_string = !xff_origin ? "" : string(xff_origin);
-    string origin = FLAGS_trusted_domain_use_xff_header ?
-        xff_origin_string :
-        GetRemoteAddress(request_info).ToString();
+    std::string_view xff_origin_sv = !xff_origin ? "" : xff_origin;
+    string origin;
+    if (FLAGS_trusted_domain_use_xff_header) {
+      Status status = GetXFFOriginClientAddress(xff_origin_sv, origin);
+      if (!status.ok()) LOG(WARNING) << status.GetDetail();
+    } else {
+      origin = GetRemoteAddress(request_info).ToString();
+    }
+
     StripWhiteSpace(&origin);
     if (origin.empty() && FLAGS_trusted_domain_use_xff_header &&
         FLAGS_trusted_domain_empty_xff_header_use_origin) {
diff --git a/fe/src/test/java/org/apache/impala/customcluster/LdapHS2Test.java 
b/fe/src/test/java/org/apache/impala/customcluster/LdapHS2Test.java
index 29bff7d85..3cff62b22 100644
--- a/fe/src/test/java/org/apache/impala/customcluster/LdapHS2Test.java
+++ b/fe/src/test/java/org/apache/impala/customcluster/LdapHS2Test.java
@@ -511,7 +511,7 @@ public class LdapHS2Test {
 
     // Case 1: Authenticate as 'Test1Ldap' without password, send 
X-Forwarded-For header
     headers.put("Authorization", "Basic VGVzdDFMZGFwOg==");
-    headers.put("X-Forwarded-For", "127.0.0.1");
+    headers.put("X-Forwarded-For", "127.0.0.1, 120.76.80.91");
     transport.setCustomHeaders(headers);
     transport.open();
     TCLIService.Iface client = new TCLIService.Client(new 
TBinaryProtocol(transport));
@@ -546,7 +546,7 @@ public class LdapHS2Test {
     // Case 3: Authenticate as 'Test1Ldap' without password, send 
X-Forwarded-For header
     // that does not match trusted_domain
     headers.put("Authorization", "Basic VGVzdDFMZGFwOg==");
-    headers.put("X-Forwarded-For", "126.0.23.1");
+    headers.put("X-Forwarded-For", "126.0.23.1, 127.0.0.1, 127.0.0.6");
     transport.setCustomHeaders(headers);
     try {
       openResp = client.OpenSession(openReq);
diff --git 
a/fe/src/test/java/org/apache/impala/customcluster/LdapWebserverTest.java 
b/fe/src/test/java/org/apache/impala/customcluster/LdapWebserverTest.java
index 4f6e4dddf..abbaceb18 100644
--- a/fe/src/test/java/org/apache/impala/customcluster/LdapWebserverTest.java
+++ b/fe/src/test/java/org/apache/impala/customcluster/LdapWebserverTest.java
@@ -272,7 +272,7 @@ public class LdapWebserverTest {
     verifyTrustedDomainMetrics(Range.closed(1L, 1L));
 
     // Case 2: Authenticate as 'Test1Ldap' without password
-    attemptConnection("Basic VGVzdDFMZGFwOg==", "127.0.0.1", false);
+    attemptConnection("Basic VGVzdDFMZGFwOg==", "127.0.0.1, 126.5.6.7", false);
     verifyTrustedDomainMetrics(Range.closed(2L, 2L));
 
     // Case 3: Authenticate as 'Test1Ldap' with the right password
diff --git a/tests/authorization/test_ranger.py 
b/tests/authorization/test_ranger.py
index 45b3515ea..17de9c31c 100644
--- a/tests/authorization/test_ranger.py
+++ b/tests/authorization/test_ranger.py
@@ -26,6 +26,8 @@ import pytest
 import logging
 import requests
 from subprocess import check_call
+import tempfile
+from time import sleep
 
 from getpass import getuser
 from tests.common.custom_cluster_test_suite import CustomClusterTestSuite
@@ -33,6 +35,8 @@ from tests.common.file_utils import copy_files_to_hdfs_dir
 from tests.common.skip import SkipIfFS, SkipIfHive2, SkipIf
 from tests.common.test_dimensions import (create_client_protocol_dimension,
     create_exec_option_dimension, create_orc_dimension)
+from tests.common.test_vector import ImpalaTestVector
+from tests.shell.util import run_impala_shell_cmd
 from tests.util.hdfs_util import NAMENODE
 from tests.util.calculation_util import get_random_id
 from tests.util.filesystem_utils import WAREHOUSE_PREFIX, WAREHOUSE
@@ -69,6 +73,85 @@ class TestRanger(CustomClusterTestSuite):
   def get_workload(cls):
     return 'functional-query'
 
+  @pytest.mark.execute_serially
+  @CustomClusterTestSuite.with_args(
+    impala_log_dir=tempfile.mkdtemp(prefix="ranger_audit_xff", 
dir=os.getenv("LOG_DIR")),
+    impalad_args=(IMPALAD_ARGS + " --use_xff_address_as_origin=true"),
+    catalogd_args=CATALOGD_ARGS)
+  def test_xff_ranger_audit(self):
+    """
+    Tests XFF client IP is included in ranger audit logs when using hs2-http 
protocol
+    """
+    # Iterate over test vector within test function to avoid restarting 
cluster.
+    for vector in\
+        [ImpalaTestVector([value]) for value in 
create_client_protocol_dimension()]:
+      protocol = vector.get_value("protocol")
+      if protocol != "hs2-http":
+        continue
+
+      # Query with XFF header in client request
+      args = ['--protocol=hs2-http',
+              '--hs2_x_forward= 10.20.30.40 ',
+              '-q', 'select count(1) from functional.alltypes']
+      run_impala_shell_cmd(vector, args)
+
+      # Query with XFF header in client request
+      args = ['--protocol=hs2-http',
+              '--hs2_x_forward=10.20.30.40, 1.1.2.3, 127.0.0.6',
+              '-q', 'select count(1) from functional.alltypes']
+      run_impala_shell_cmd(vector, args)
+
+      # Query with XFF header in client request
+      args = ['--protocol=hs2-http',
+              '--hs2_x_forward=10.20.30.40,1.1.2.3,127.0.0.6',
+              '-q', 'select count(1) from functional.alltypes']
+      run_impala_shell_cmd(vector, args)
+
+      # Query without XFF header in client request
+      args = ['--protocol=hs2-http',
+              '-q', 'select count(2) from functional.alltypes']
+      run_impala_shell_cmd(vector, args)
+
+      # Query with empty XFF header in client request
+      args = ['--protocol=hs2-http',
+              '--hs2_x_forward=    ',
+              '-q', 'select count(2) from functional.alltypes']
+      run_impala_shell_cmd(vector, args)
+
+      # Query with invalid XFF header in client request
+      args = ['--protocol=hs2-http',
+              '--hs2_x_forward=foobar',
+              '-q', 'select count(3) from functional.alltypes']
+      run_impala_shell_cmd(vector, args)
+
+      # Shut down cluster to ensure logs flush to disk.
+      sleep(5)
+      self._stop_impala_cluster()
+
+      # Expected audit log string
+      expected_string_valid_xff = (
+        '"access":"select",'
+        '"resource":"functional/alltypes",'
+        '"resType":"@table",'
+        '"action":"select",'
+        '"result":1,'
+        '"agent":"impala",'
+        r'"policy":\d,'
+        '"enforcer":"ranger-acl",'
+        '"cliIP":"%s",'
+        '"reqData":"%s",'
+        '".+":".+","logType":"RangerAudit"'
+      )
+
+      # Ensure audit logs were logged in coordinator logs
+      self.assert_impalad_log_contains("INFO", expected_string_valid_xff %
+          ("10.20.30.40", r"select count\(1\) from functional.alltypes"),
+          expected_count=3)
+      self.assert_impalad_log_contains("INFO", expected_string_valid_xff %
+          ("127.0.0.1", r"select count\(2\) from functional.alltypes"), 
expected_count=2)
+      self.assert_impalad_log_contains("INFO", expected_string_valid_xff %
+          ("foobar", r"select count\(3\) from functional.alltypes"), 
expected_count=1)
+
   @pytest.mark.execute_serially
   @CustomClusterTestSuite.with_args(
     impalad_args=IMPALAD_ARGS, catalogd_args=CATALOGD_ARGS)
diff --git a/tests/custom_cluster/test_shell_jwt_auth.py 
b/tests/custom_cluster/test_shell_jwt_auth.py
index 7999b1c3a..fe62cb1c9 100644
--- a/tests/custom_cluster/test_shell_jwt_auth.py
+++ b/tests/custom_cluster/test_shell_jwt_auth.py
@@ -61,9 +61,10 @@ class TestImpalaShellJWTAuth(CustomClusterTestSuite):
 
   @pytest.mark.execute_serially
   @CustomClusterTestSuite.with_args(
-    "-log_dir={0} -v 2 -jwks_file_path={1} -jwt_custom_claim_username=sub "
+    impala_log_dir=LOG_DIR_JWT_AUTH_SUCCESS,
+    impalad_args="-v 2 -jwks_file_path={0} -jwt_custom_claim_username=sub "
     "-jwt_token_auth=true -jwt_allow_without_tls=true"
-    .format(LOG_DIR_JWT_AUTH_SUCCESS, JWKS_JSON_PATH))
+    .format(JWKS_JSON_PATH))
   def test_jwt_auth_valid(self, vector):
     """Asserts the Impala shell can authenticate to Impala using JWT 
authentication.
     Also executes a query to ensure the authentication was successful."""
@@ -82,15 +83,13 @@ class TestImpalaShellJWTAuth(CustomClusterTestSuite):
 
     # Ensure JWT auth was enabled by checking the coordinator startup flags 
logged
     # in the coordinator's INFO logfile
-    expected_strings = [
-      '--jwks_file_path={0}'.format(self.JWKS_JSON_PATH),
-      'effective username: test-user',
-      'connected_user (string) = "test-user"',
-    ]
-
+    self.assert_impalad_log_contains("INFO",
+        '--jwks_file_path={0}'.format(self.JWKS_JSON_PATH), expected_count=1)
     # Ensure JWT auth was successful by checking impala coordinator logs
-    self.__assert_log_file(self.LOG_DIR_JWT_AUTH_SUCCESS,
-                           "impalad.INFO", expected_strings)
+    self.assert_impalad_log_contains("INFO",
+        'effective username: test-user', expected_count=1)
+    self.assert_impalad_log_contains("INFO",
+        r'connected_user \(string\) = "test-user"', expected_count=1)
 
     # Ensure the query ran successfully.
     assert "version()" in result.stdout
@@ -98,9 +97,10 @@ class TestImpalaShellJWTAuth(CustomClusterTestSuite):
 
   @pytest.mark.execute_serially
   @CustomClusterTestSuite.with_args(
-    "-log_dir={0} -v 2 -jwks_file_path={1} -jwt_custom_claim_username=sub "
+    impala_log_dir=LOG_DIR_JWT_AUTH_FAIL,
+    impalad_args="-v 2 -jwks_file_path={0} -jwt_custom_claim_username=sub "
     "-jwt_token_auth=true -jwt_allow_without_tls=true"
-    .format(LOG_DIR_JWT_AUTH_FAIL, JWKS_JSON_PATH))
+    .format(JWKS_JSON_PATH))
   def test_jwt_auth_expired(self, vector):
     """Asserts the Impala shell fails to authenticate when it presents a JWT 
that has a
     valid signature but is expired."""
@@ -119,17 +119,16 @@ class TestImpalaShellJWTAuth(CustomClusterTestSuite):
 
     # Ensure JWT auth was enabled by checking the coordinator startup flags 
logged
     # in the coordinator's INFO logfile
-    expected_strings = ['--jwks_file_path={0}'.format(self.JWKS_JSON_PATH)]
-    self.__assert_log_file(self.LOG_DIR_JWT_AUTH_FAIL,
-                           "impalad.INFO", expected_strings)
+    expected_string = '--jwks_file_path={0}'.format(self.JWKS_JSON_PATH)
+    self.assert_impalad_log_contains("INFO", expected_string)
 
     # Ensure JWT auth failed by checking impala coordinator logs
-    expected_strings = [
-      'Error verifying JWT token',
+    expected_string = (
+      'Error verifying JWT token'
+      '.*'
       'Error verifying JWT Token: Verification failed, error: token expired'
-    ]
-    self.__assert_log_file(self.LOG_DIR_JWT_AUTH_FAIL,
-                           "impalad.ERROR", expected_strings)
+    )
+    self.assert_impalad_log_contains("ERROR", expected_string, 
expected_count=-1)
 
     # Ensure the shell login failed.
     assert "Error connecting: HttpError" in result.stderr
@@ -138,9 +137,10 @@ class TestImpalaShellJWTAuth(CustomClusterTestSuite):
 
   @pytest.mark.execute_serially
   @CustomClusterTestSuite.with_args(
-    "-log_dir={0} -v 2 -jwks_file_path={1} -jwt_custom_claim_username=sub "
+    impala_log_dir=LOG_DIR_JWT_AUTH_INVALID_JWK,
+    impalad_args="-v 2 -jwks_file_path={0} -jwt_custom_claim_username=sub "
     "-jwt_token_auth=true -jwt_allow_without_tls=true"
-    .format(LOG_DIR_JWT_AUTH_INVALID_JWK, JWKS_JSON_PATH))
+    .format(JWKS_JSON_PATH))
   def test_jwt_auth_invalid_jwk(self, vector):
     """Asserts the Impala shell fails to authenticate when it presents a JWT 
that has a
     valid signature but is expired."""
@@ -159,48 +159,22 @@ class TestImpalaShellJWTAuth(CustomClusterTestSuite):
 
     # Ensure JWT auth was enabled by checking the coordinator startup flags 
logged
     # in the coordinator's INFO logfile
-    expected_strings = ['--jwks_file_path={0}'.format(self.JWKS_JSON_PATH)]
-    self.__assert_log_file(self.LOG_DIR_JWT_AUTH_INVALID_JWK,
-                           "impalad.INFO", expected_strings)
+    expected_string = '--jwks_file_path={0}'.format(self.JWKS_JSON_PATH)
+    self.assert_impalad_log_contains("INFO", expected_string)
 
     # Ensure JWT auth failed by checking impala coordinator logs
-    expected_strings = [
-      'Error verifying JWT token',
+    expected_string = (
+      'Error verifying JWT token'
+      '.*'
       'Error verifying JWT Token: Invalid JWK ID in the JWT token'
-    ]
-    self.__assert_log_file(self.LOG_DIR_JWT_AUTH_INVALID_JWK,
-                           "impalad.ERROR", expected_strings)
+    )
+    self.assert_impalad_log_contains("ERROR", expected_string, 
expected_count=-1)
 
     # Ensure the shell login failed.
     assert "Error connecting: HttpError" in result.stderr
     assert "HTTP code 401: Unauthorized" in result.stderr
     assert "Not connected to Impala, could not execute queries." in 
result.stderr
 
-  def __assert_log_file(self, log_dir, log_file, expected_strings):
-    """Given a list of strings, searches the specified log file for each of 
those
-    strings ensuring that at least one instance of each string exists within a
-    line of the log file
-
-    log_dir - path to the directory where the log file exists
-    log_file - name of the file within the specified directory that will be 
searched
-    expected_strings - list of strings to search for within the log file
-    """
-
-    counter_dict = {}
-    for item in expected_strings:
-      counter_dict[item] = 0
-
-    log_path = os.path.join(log_dir, log_file)
-    with open(log_path) as file:
-      for line in file:
-        for key in counter_dict:
-          if line.find(key) >= 0:
-            counter_dict[key] += 1
-
-    for line, count in counter_dict.items():
-      assert count > 0, "Did not find expected string '{0}' in log file '{1}'" 
\
-                        .format(line, log_path)
-
   def __assert_success_fail_metric(self, success_count_min=0, 
success_count_max=0,
                                    failure_count_min=0, failure_count_max=0):
     """Impala emits metrics that count the number of successful and failed JWT

Reply via email to