Repository: cassandra-dtest
Updated Branches:
  refs/heads/master 5afbb7445 -> 0e9388d77


add tests for network auth (CASSANDRA-13985)


Project: http://git-wip-us.apache.org/repos/asf/cassandra-dtest/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra-dtest/commit/0e9388d7
Tree: http://git-wip-us.apache.org/repos/asf/cassandra-dtest/tree/0e9388d7
Diff: http://git-wip-us.apache.org/repos/asf/cassandra-dtest/diff/0e9388d7

Branch: refs/heads/master
Commit: 0e9388d7783859084925fe6825215374a66206de
Parents: 5afbb74
Author: Blake Eggleston <bdeggles...@gmail.com>
Authored: Wed Apr 18 14:16:22 2018 -0700
Committer: Blake Eggleston <bdeggles...@gmail.com>
Committed: Mon Apr 23 16:33:43 2018 -0700

----------------------------------------------------------------------
 auth_test.py | 178 ++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 152 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra-dtest/blob/0e9388d7/auth_test.py
----------------------------------------------------------------------
diff --git a/auth_test.py b/auth_test.py
index 34f7212..e7aef05 100644
--- a/auth_test.py
+++ b/auth_test.py
@@ -1,3 +1,5 @@
+import random
+import string
 import time
 from collections import namedtuple
 from datetime import datetime, timedelta
@@ -395,20 +397,20 @@ class TestAuth(Tester):
         self.prepare()
         session = self.get_session(user='cassandra', password='cassandra')
 
-        assert_one(session, "LIST USERS", ['cassandra', True])
+        assert_one(session, "LIST USERS", ['cassandra', True] + all_dcs)
 
         session.execute("CREATE USER IF NOT EXISTS aleksey WITH PASSWORD 
'sup'")
         session.execute("CREATE USER IF NOT EXISTS aleksey WITH PASSWORD 
'ignored'")
 
         self.get_session(user='aleksey', password='sup')
 
-        assert_all(session, "LIST USERS", [['aleksey', False], ['cassandra', 
True]])
+        assert_all(session, "LIST USERS", [['aleksey', False] + all_dcs, 
['cassandra', True] + all_dcs])
 
         session.execute("DROP USER IF EXISTS aleksey")
-        assert_one(session, "LIST USERS", ['cassandra', True])
+        assert_one(session, "LIST USERS", ['cassandra', True] + all_dcs)
 
         session.execute("DROP USER IF EXISTS aleksey")
-        assert_one(session, "LIST USERS", ['cassandra', True])
+        assert_one(session, "LIST USERS", ['cassandra', True] + all_dcs)
 
     def test_create_ks_auth(self):
         """
@@ -1008,13 +1010,15 @@ class TestAuth(Tester):
 
         self.cluster.stop()
         config = {'authenticator': 
'org.apache.cassandra.auth.AllowAllAuthenticator',
-                  'authorizer': 'org.apache.cassandra.auth.AllowAllAuthorizer'}
+                  'authorizer': 'org.apache.cassandra.auth.AllowAllAuthorizer',
+                  'network_authorizer': 
'org.apache.cassandra.auth.AllowAllNetworkAuthorizer'}
         self.cluster.set_configuration_options(values=config)
         self.cluster.start(wait_for_binary_proto=True)
 
         self.cluster.stop()
         config = {'authenticator': 
'org.apache.cassandra.auth.PasswordAuthenticator',
-                  'authorizer': 
'org.apache.cassandra.auth.CassandraAuthorizer'}
+                  'authorizer': 
'org.apache.cassandra.auth.CassandraAuthorizer',
+                  'network_authorizer': 
'org.apache.cassandra.auth.CassandraNetworkAuthorizer'}
         self.cluster.set_configuration_options(values=config)
         self.cluster.start(wait_for_binary_proto=True)
 
@@ -1073,6 +1077,7 @@ class TestAuth(Tester):
         """
         config = {'authenticator': 
'org.apache.cassandra.auth.PasswordAuthenticator',
                   'authorizer': 
'org.apache.cassandra.auth.CassandraAuthorizer',
+                  'network_authorizer': 
'org.apache.cassandra.auth.CassandraNetworkAuthorizer',
                   'permissions_validity_in_ms': permissions_validity}
         self.cluster.set_configuration_options(values=config)
         self.cluster.populate(nodes).start()
@@ -1129,12 +1134,15 @@ def data_resource_creator_permissions(creator, 
resource):
 # Third value is login status
 # Fourth value is role options
 # See CASSANDRA-7653 for explanations of these
-Role = namedtuple('Role', ['name', 'superuser', 'login', 'options'])
+dcs_field = [] if CASSANDRA_VERSION_FROM_BUILD < '4.0' else ['dcs']
+Role = namedtuple('Role', ['name', 'superuser', 'login', 'options'] + 
dcs_field)
 
-mike_role = Role('mike', False, True, {})
-role1_role = Role('role1', False, False, {})
-role2_role = Role('role2', False, False, {})
-cassandra_role = Role('cassandra', True, True, {})
+all_dcs = [] if CASSANDRA_VERSION_FROM_BUILD < '4.0' else ['ALL']
+na_dcs = [] if CASSANDRA_VERSION_FROM_BUILD < '4.0' else ['n/a']
+mike_role = Role('mike', False, True, {}, *all_dcs)
+role1_role = Role('role1', False, False, {}, *na_dcs)
+role2_role = Role('role2', False, False, {}, *na_dcs)
+cassandra_role = Role('cassandra', True, True, {}, *all_dcs)
 
 
 @since('2.2')
@@ -1275,9 +1283,9 @@ class TestAuthRoles(Tester):
 
         # roles with roleadmin can drop roles
         mike.execute("DROP ROLE role1")
-        assert_all(cassandra, "LIST ROLES", [['administrator', False, False, 
{}],
+        assert_all(cassandra, "LIST ROLES", [['administrator', False, False, 
{}] + na_dcs,
                                              list(cassandra_role),
-                                             ['klaus', False, True, {}],
+                                             ['klaus', False, True, {}] + 
all_dcs,
                                              list(mike_role)])
 
         # revoking role admin removes its privileges
@@ -1353,9 +1361,9 @@ class TestAuthRoles(Tester):
         mike.execute("GRANT another_superuser TO role1")
         assert_unauthorized(mike, "CREATE ROLE role2 WITH SUPERUSER = true",
                             "Only superusers can create a role with superuser 
status")
-        assert_all(cassandra, "LIST ROLES OF role1", [['another_superuser', 
True, False, {}],
-                                                      ['non_superuser', False, 
False, {}],
-                                                      ['role1', False, False, 
{}]])
+        assert_all(cassandra, "LIST ROLES OF role1", [['another_superuser', 
True, False, {}] + na_dcs,
+                                                      ['non_superuser', False, 
False, {}] + na_dcs,
+                                                      ['role1', False, False, 
{}] + na_dcs])
 
     def test_drop_and_revoke_roles_with_superuser_status(self):
         """
@@ -1928,7 +1936,7 @@ class TestAuthRoles(Tester):
         assert_one(cassandra, "LIST ROLES OF mike", list(mike_role))
 
         cassandra.execute("CREATE USER super_user WITH PASSWORD '12345' 
SUPERUSER")
-        assert_one(cassandra, "LIST ROLES OF super_user", ["super_user", True, 
True, {}])
+        assert_one(cassandra, "LIST ROLES OF super_user", ["super_user", True, 
True, {}] + all_dcs)
 
     def test_role_name(self):
         """
@@ -1985,11 +1993,11 @@ class TestAuthRoles(Tester):
         self.get_session(user='mike', password='12345')
 
         cassandra.execute("ALTER ROLE mike WITH LOGIN = false")
-        assert_one(cassandra, "LIST ROLES OF mike", ["mike", False, False, {}])
+        assert_one(cassandra, "LIST ROLES OF mike", ["mike", False, False, {}] 
+ na_dcs)
         self.assert_login_not_allowed('mike', '12345')
 
         cassandra.execute("ALTER ROLE mike WITH LOGIN = true")
-        assert_one(cassandra, "LIST ROLES OF mike", ["mike", False, True, {}])
+        assert_one(cassandra, "LIST ROLES OF mike", ["mike", False, True, {}] 
+ all_dcs)
         self.get_session(user='mike', password='12345')
 
     def test_roles_do_not_inherit_login_privilege(self):
@@ -2006,9 +2014,9 @@ class TestAuthRoles(Tester):
         cassandra.execute("CREATE ROLE with_login WITH PASSWORD = '54321' AND 
SUPERUSER = false AND LOGIN = true")
         cassandra.execute("GRANT with_login to mike")
 
-        assert_all(cassandra, "LIST ROLES OF mike", [["mike", False, False, 
{}],
-                                                     ["with_login", False, 
True, {}]])
-        assert_one(cassandra, "LIST ROLES OF with_login", ["with_login", 
False, True, {}])
+        assert_all(cassandra, "LIST ROLES OF mike", [["mike", False, False, 
{}] + na_dcs,
+                                                     ["with_login", False, 
True, {}] + all_dcs])
+        assert_one(cassandra, "LIST ROLES OF with_login", ["with_login", 
False, True, {}] + all_dcs)
 
         self.assert_login_not_allowed("mike", "12345")
 
@@ -2053,9 +2061,9 @@ class TestAuthRoles(Tester):
 
         cassandra.execute("GRANT db_admin TO mike")
         mike.execute("CREATE ROLE another_role WITH SUPERUSER = false AND 
LOGIN = false")
-        assert_all(mike, "LIST ROLES", [["another_role", False, False, {}],
+        assert_all(mike, "LIST ROLES", [["another_role", False, False, {}] + 
na_dcs,
                                         list(cassandra_role),
-                                        ["db_admin", True, False, {}],
+                                        ["db_admin", True, False, {}] + na_dcs,
                                         list(mike_role)])
 
     def test_list_users_considers_inherited_superuser_status(self):
@@ -2071,8 +2079,8 @@ class TestAuthRoles(Tester):
         cassandra.execute("CREATE ROLE db_admin WITH SUPERUSER = true")
         cassandra.execute("CREATE ROLE mike WITH PASSWORD = '12345' AND 
SUPERUSER = false AND LOGIN = true")
         cassandra.execute("GRANT db_admin TO mike")
-        assert_all(cassandra, "LIST USERS", [['cassandra', True],
-                                             ["mike", True]])
+        assert_all(cassandra, "LIST USERS", [['cassandra', True] + all_dcs,
+                                             ["mike", True] + all_dcs])
 
     # UDF permissions tests # TODO move to separate fixture & refactor this + 
auth_test.py
     def test_grant_revoke_udf_permissions(self):
@@ -2683,6 +2691,7 @@ class TestAuthRoles(Tester):
         config = {'authenticator': 
'org.apache.cassandra.auth.PasswordAuthenticator',
                   'authorizer': 
'org.apache.cassandra.auth.CassandraAuthorizer',
                   'role_manager': 
'org.apache.cassandra.auth.CassandraRoleManager',
+                  'network_authorizer': 
'org.apache.cassandra.auth.CassandraNetworkAuthorizer',
                   'permissions_validity_in_ms': 0,
                   'roles_validity_in_ms': roles_expiry}
         self.cluster.set_configuration_options(values=config)
@@ -2699,6 +2708,123 @@ class TestAuthRoles(Tester):
         assert list(session.execute(query)) == []
 
 
+@since('4.0')
+class TestNetworkAuth(Tester):
+
+    @pytest.fixture(autouse=True)
+    def fixture_setup_auth(self, fixture_dtest_setup):
+        fixture_dtest_setup.cluster.set_configuration_options(values={
+            'authenticator': 'org.apache.cassandra.auth.PasswordAuthenticator',
+            'authorizer': 'org.apache.cassandra.auth.CassandraAuthorizer',
+            'role_manager': 'org.apache.cassandra.auth.CassandraRoleManager',
+            'network_authorizer': 
'org.apache.cassandra.auth.CassandraNetworkAuthorizer',
+            'num_tokens': 1
+        })
+        fixture_dtest_setup.cluster.populate([1, 1], 
debug=True).start(wait_for_binary_proto=True, 
jvm_args=['-XX:-PerfDisableSharedMem'])
+        fixture_dtest_setup.dc1_node, fixture_dtest_setup.dc2_node = 
fixture_dtest_setup.cluster.nodelist()
+        fixture_dtest_setup.superuser = 
fixture_dtest_setup.patient_exclusive_cql_connection(fixture_dtest_setup.dc1_node,
 user='cassandra', password='cassandra')
+
+        fixture_dtest_setup.superuser.execute("ALTER KEYSPACE system_auth WITH 
REPLICATION={'class': 'NetworkTopologyStrategy', 'dc1': 1, 'dc2': 1}")
+        fixture_dtest_setup.superuser.execute("CREATE KEYSPACE ks WITH 
REPLICATION={'class': 'NetworkTopologyStrategy', 'dc1': 1, 'dc2': 1}")
+        fixture_dtest_setup.superuser.execute("CREATE TABLE ks.tbl (k int 
primary key, v int)")
+
+    def username(self):
+        return ''.join(random.choice(string.ascii_lowercase) for _ in 
range(8));
+
+
+    def create_user(self, query_fmt, username):
+        """
+        formats and runs the given auth query and grants permissions to the 
created user
+        """
+        self.superuser.execute(query_fmt % username)
+        self.superuser.execute("GRANT ALL PERMISSIONS ON ks.tbl TO %s" % 
username)
+
+    def assertConnectsTo(self, username, node):
+        session = self.exclusive_cql_connection(node, user=username, 
password='password')
+        session.execute("SELECT * FROM ks.tbl")
+
+    def assertUnauthorized(self, func):
+        try:
+            func()
+            pytest.fail("Expecting Unauthorized exception")
+        except Unauthorized as _:
+            pass
+        except NoHostAvailable as e:
+            cause = list(e.errors.values())[0]
+            assert isinstance(cause, Unauthorized)
+
+    def assertWontConnectTo(self, username, node):
+        self.assertUnauthorized(lambda: self.exclusive_cql_connection(node, 
user=username, password='password'))
+
+    def clear_network_auth_cache(self, node):
+        mbean = make_mbean('auth', type='NetworkAuthCache')
+        with JolokiaAgent(node) as jmx:
+            jmx.execute_method(mbean, 'invalidate')
+
+    def test_full_dc_access(self):
+        username = self.username()
+        self.create_user("CREATE ROLE %s WITH password = 'password' AND LOGIN 
= true", username)
+        self.assertConnectsTo(username, self.dc1_node)
+        self.assertConnectsTo(username, self.dc2_node)
+
+    def test_single_dc_access(self):
+        username = self.username()
+        self.create_user("CREATE ROLE %s WITH password = 'password' AND LOGIN 
= true AND ACCESS TO DATACENTERS {'dc1'}", username)
+        self.assertConnectsTo(username, self.dc1_node)
+        self.assertWontConnectTo(username, self.dc2_node)
+
+    def test_revoked_dc_access(self):
+        """
+        if a user's access to a dc is revoked while they're connected,
+        all of their requests should fail once the cache is cleared
+        """
+        username = self.username()
+        self.create_user("CREATE ROLE %s WITH password = 'password' AND LOGIN 
= true", username)
+        self.assertConnectsTo(username, self.dc1_node)
+        self.assertConnectsTo(username, self.dc2_node)
+
+        # connect to the dc2 node, then remove permission for it
+        session = self.exclusive_cql_connection(self.dc2_node, user=username, 
password='password')
+        self.superuser.execute("ALTER ROLE %s WITH ACCESS TO DATACENTERS 
{'dc1'}" % username)
+        self.clear_network_auth_cache(self.dc2_node)
+        self.assertUnauthorized(lambda: session.execute("SELECT * FROM 
ks.tbl"))
+
+    def test_create_dc_validation(self):
+        """
+        trying to give a user access to a dc that doesn't exist should fail
+        """
+        username = self.username()
+        with pytest.raises(InvalidRequest):
+            self.create_user("CREATE ROLE %s WITH password = 'password' AND 
LOGIN = true AND ACCESS TO DATACENTERS {'dc1000'}", username)
+
+    def test_alter_dc_validation(self):
+        """
+        trying to give a user access to a dc that doesn't exist should fail
+        """
+        username = self.username()
+        self.create_user("CREATE ROLE %s WITH password = 'password' AND LOGIN 
= true", username)
+        with pytest.raises(InvalidRequest):
+            self.create_user("ALTER ROLE %s WITH ACCESS TO DATACENTERS 
{'dc1000'}", username)
+
+    def test_revoked_login(self):
+        """
+        If the login flag is set to false for a user with a current connection,
+        all their requests should fail once the cache is cleared. Here because 
it has
+        more in common with these tests that the other auth tests
+        """
+        username = self.username()
+        superuser = self.patient_exclusive_cql_connection(self.dc1_node, 
user='cassandra', password='cassandra')
+        self.create_user("CREATE ROLE %s WITH password = 'password' AND LOGIN 
= true", username)
+        self.assertConnectsTo(username, self.dc1_node)
+        self.assertConnectsTo(username, self.dc2_node)
+
+        # connect to the dc2 node, then remove permission for it
+        session = self.exclusive_cql_connection(self.dc2_node, user=username, 
password='password')
+        superuser.execute("ALTER ROLE %s WITH LOGIN=false" % username)
+        self.clear_network_auth_cache(self.dc2_node)
+        self.assertUnauthorized(lambda: session.execute("SELECT * FROM 
ks.tbl"))
+
+
 def role_creator_permissions(creator, role):
     return [(creator, role, perm) for perm in ('ALTER', 'DROP', 'AUTHORIZE')]
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org
For additional commands, e-mail: commits-h...@cassandra.apache.org

Reply via email to