2021-06-22 10:49:35 +00:00
|
|
|
|
2021-05-24 12:18:04 +00:00
|
|
|
import pytest
|
|
|
|
from helpers.cluster import ClickHouseCluster
|
|
|
|
from kazoo.client import KazooClient, KazooState
|
|
|
|
from kazoo.security import ACL, make_digest_acl, make_acl
|
|
|
|
from kazoo.exceptions import AuthFailedError, InvalidACLError, NoAuthError, KazooException
|
|
|
|
|
|
|
|
cluster = ClickHouseCluster(__file__)
|
2021-06-29 13:01:15 +00:00
|
|
|
node = cluster.add_instance('node', main_configs=['configs/keeper_config.xml'], with_zookeeper=True, use_keeper=False, stay_alive=True)
|
2021-05-24 12:18:04 +00:00
|
|
|
|
|
|
|
SUPERAUTH = "super:admin"
|
|
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
|
|
def started_cluster():
|
|
|
|
try:
|
|
|
|
cluster.start()
|
|
|
|
|
|
|
|
yield cluster
|
|
|
|
|
|
|
|
finally:
|
|
|
|
cluster.shutdown()
|
|
|
|
|
|
|
|
def get_fake_zk(timeout=30.0):
|
|
|
|
_fake_zk_instance = KazooClient(hosts=cluster.get_instance_ip('node') + ":9181", timeout=timeout)
|
|
|
|
_fake_zk_instance.start()
|
|
|
|
return _fake_zk_instance
|
|
|
|
|
|
|
|
def get_genuine_zk():
|
|
|
|
print("Zoo1", cluster.get_instance_ip("zoo1"))
|
|
|
|
return cluster.get_kazoo_client('zoo1')
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
('get_zk'),
|
|
|
|
[
|
|
|
|
get_genuine_zk,
|
|
|
|
get_fake_zk
|
|
|
|
]
|
|
|
|
)
|
2022-01-07 09:45:30 +00:00
|
|
|
|
|
|
|
def test_remove_acl(started_cluster, get_zk):
|
|
|
|
auth_connection = get_zk()
|
|
|
|
|
|
|
|
auth_connection.add_auth('digest', 'user1:password1')
|
|
|
|
|
|
|
|
# Consistent with zookeeper, accept generated digest
|
|
|
|
auth_connection.create("/test_remove_acl1", b"dataX", acl=[make_acl("digest", "user1:XDkd2dsEuhc9ImU3q8pa8UOdtpI=", read=True, write=False, create=False, delete=False, admin=False)])
|
|
|
|
auth_connection.create("/test_remove_acl2", b"dataX", acl=[make_acl("digest", "user1:XDkd2dsEuhc9ImU3q8pa8UOdtpI=", read=True, write=True, create=False, delete=False, admin=False)])
|
|
|
|
auth_connection.create("/test_remove_acl3", b"dataX", acl=[make_acl("digest", "user1:XDkd2dsEuhc9ImU3q8pa8UOdtpI=", all=True)])
|
|
|
|
|
|
|
|
auth_connection.delete("/test_remove_acl2")
|
|
|
|
|
|
|
|
auth_connection.create("/test_remove_acl4", b"dataX", acl=[make_acl("digest", "user1:XDkd2dsEuhc9ImU3q8pa8UOdtpI=", read=True, write=True, create=True, delete=False, admin=False)])
|
|
|
|
|
|
|
|
acls, stat = auth_connection.get_acls("/test_remove_acl3")
|
|
|
|
|
|
|
|
assert stat.aversion == 0
|
|
|
|
assert len(acls) == 1
|
|
|
|
for acl in acls:
|
|
|
|
assert acl.acl_list == ['ALL']
|
|
|
|
assert acl.perms == 31
|
|
|
|
|
|
|
|
|
2022-01-07 10:09:23 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
('get_zk'),
|
|
|
|
[
|
|
|
|
get_genuine_zk,
|
|
|
|
get_fake_zk
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2021-05-24 12:18:04 +00:00
|
|
|
def test_digest_auth_basic(started_cluster, get_zk):
|
|
|
|
auth_connection = get_zk()
|
|
|
|
|
|
|
|
auth_connection.add_auth('digest', 'user1:password1')
|
|
|
|
|
|
|
|
auth_connection.create("/test_no_acl", b"")
|
|
|
|
auth_connection.create("/test_all_acl", b"data", acl=[make_acl("auth", "", all=True)])
|
2021-12-28 09:17:01 +00:00
|
|
|
# Consistent with zookeeper, accept generated digest
|
|
|
|
auth_connection.create("/test_all_digest_acl", b"dataX", acl=[make_acl("digest", "user1:XDkd2dsEuhc9ImU3q8pa8UOdtpI=", all=True)])
|
2021-05-24 12:18:04 +00:00
|
|
|
|
|
|
|
assert auth_connection.get("/test_all_acl")[0] == b"data"
|
2021-12-28 09:17:01 +00:00
|
|
|
assert auth_connection.get("/test_all_digest_acl")[0] == b"dataX"
|
2021-05-24 12:18:04 +00:00
|
|
|
|
|
|
|
no_auth_connection = get_zk()
|
|
|
|
no_auth_connection.set("/test_no_acl", b"hello")
|
|
|
|
|
|
|
|
# no ACL, so cannot access these nodes
|
|
|
|
assert no_auth_connection.get("/test_no_acl")[0] == b"hello"
|
|
|
|
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
no_auth_connection.set("/test_all_acl", b"hello")
|
|
|
|
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
no_auth_connection.get("/test_all_acl")
|
|
|
|
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
no_auth_connection.get("/test_all_digest_acl")
|
|
|
|
|
|
|
|
# still doesn't help
|
|
|
|
with pytest.raises(AuthFailedError):
|
|
|
|
no_auth_connection.add_auth("world", "anyone")
|
|
|
|
|
|
|
|
# session became broken, reconnect
|
|
|
|
no_auth_connection = get_zk()
|
|
|
|
|
|
|
|
# wrong auth
|
|
|
|
no_auth_connection.add_auth('digest', 'user2:password2')
|
|
|
|
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
no_auth_connection.set("/test_all_acl", b"hello")
|
|
|
|
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
no_auth_connection.set("/test_all_acl", b"hello")
|
|
|
|
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
no_auth_connection.get("/test_all_acl")
|
|
|
|
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
no_auth_connection.get("/test_all_digest_acl")
|
|
|
|
|
|
|
|
# but can access some non restricted nodes
|
|
|
|
no_auth_connection.create("/some_allowed_node", b"data")
|
|
|
|
|
|
|
|
# auth added, go on
|
|
|
|
no_auth_connection.add_auth('digest', 'user1:password1')
|
|
|
|
for path in ["/test_no_acl", "/test_all_acl"]:
|
|
|
|
no_auth_connection.set(path, b"auth_added")
|
|
|
|
assert no_auth_connection.get(path)[0] == b"auth_added"
|
|
|
|
|
|
|
|
|
|
|
|
def test_super_auth(started_cluster):
|
|
|
|
auth_connection = get_fake_zk()
|
|
|
|
|
|
|
|
auth_connection.add_auth('digest', 'user1:password1')
|
|
|
|
|
|
|
|
auth_connection.create("/test_super_no_acl", b"")
|
|
|
|
auth_connection.create("/test_super_all_acl", b"data", acl=[make_acl("auth", "", all=True)])
|
|
|
|
|
|
|
|
super_connection = get_fake_zk()
|
|
|
|
super_connection.add_auth('digest', 'super:admin')
|
|
|
|
|
|
|
|
for path in ["/test_super_no_acl", "/test_super_all_acl"]:
|
|
|
|
super_connection.set(path, b"value")
|
|
|
|
assert super_connection.get(path)[0] == b"value"
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
('get_zk'),
|
|
|
|
[
|
|
|
|
get_genuine_zk,
|
|
|
|
get_fake_zk
|
|
|
|
]
|
|
|
|
)
|
|
|
|
def test_digest_auth_multiple(started_cluster, get_zk):
|
|
|
|
auth_connection = get_zk()
|
|
|
|
auth_connection.add_auth('digest', 'user1:password1')
|
|
|
|
auth_connection.add_auth('digest', 'user2:password2')
|
|
|
|
auth_connection.add_auth('digest', 'user3:password3')
|
|
|
|
|
|
|
|
auth_connection.create("/test_multi_all_acl", b"data", acl=[make_acl("auth", "", all=True)])
|
|
|
|
|
|
|
|
one_auth_connection = get_zk()
|
|
|
|
one_auth_connection.add_auth('digest', 'user1:password1')
|
|
|
|
|
|
|
|
one_auth_connection.set("/test_multi_all_acl", b"X")
|
|
|
|
assert one_auth_connection.get("/test_multi_all_acl")[0] == b"X"
|
|
|
|
|
|
|
|
other_auth_connection = get_zk()
|
|
|
|
other_auth_connection.add_auth('digest', 'user2:password2')
|
|
|
|
|
|
|
|
other_auth_connection.set("/test_multi_all_acl", b"Y")
|
|
|
|
|
|
|
|
assert other_auth_connection.get("/test_multi_all_acl")[0] == b"Y"
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
('get_zk'),
|
|
|
|
[
|
|
|
|
get_genuine_zk,
|
|
|
|
get_fake_zk
|
|
|
|
]
|
|
|
|
)
|
|
|
|
def test_partial_auth(started_cluster, get_zk):
|
|
|
|
auth_connection = get_zk()
|
|
|
|
auth_connection.add_auth('digest', 'user1:password1')
|
|
|
|
|
|
|
|
auth_connection.create("/test_partial_acl", b"data", acl=[make_acl("auth", "", read=False, write=True, create=True, delete=True, admin=True)])
|
|
|
|
|
|
|
|
auth_connection.set("/test_partial_acl", b"X")
|
|
|
|
auth_connection.create("/test_partial_acl/subnode", b"X", acl=[make_acl("auth", "", read=False, write=True, create=True, delete=True, admin=True)])
|
|
|
|
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
auth_connection.get("/test_partial_acl")
|
|
|
|
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
auth_connection.get_children("/test_partial_acl")
|
|
|
|
|
|
|
|
# exists works without read perm
|
|
|
|
assert auth_connection.exists("/test_partial_acl") is not None
|
|
|
|
|
|
|
|
auth_connection.create("/test_partial_acl_create", b"data", acl=[make_acl("auth", "", read=True, write=True, create=False, delete=True, admin=True)])
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
auth_connection.create("/test_partial_acl_create/subnode")
|
|
|
|
|
|
|
|
auth_connection.create("/test_partial_acl_set", b"data", acl=[make_acl("auth", "", read=True, write=False, create=True, delete=True, admin=True)])
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
auth_connection.set("/test_partial_acl_set", b"X")
|
|
|
|
|
|
|
|
# not allowed to delete child node
|
|
|
|
auth_connection.create("/test_partial_acl_delete", b"data", acl=[make_acl("auth", "", read=True, write=True, create=True, delete=False, admin=True)])
|
|
|
|
auth_connection.create("/test_partial_acl_delete/subnode")
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
auth_connection.delete("/test_partial_acl_delete/subnode")
|
|
|
|
|
|
|
|
|
|
|
|
def test_bad_auth(started_cluster):
|
|
|
|
auth_connection = get_fake_zk()
|
|
|
|
|
|
|
|
with pytest.raises(AuthFailedError):
|
|
|
|
auth_connection.add_auth('world', 'anyone')
|
|
|
|
|
|
|
|
auth_connection = get_fake_zk()
|
|
|
|
with pytest.raises(AuthFailedError):
|
|
|
|
print("Sending 1")
|
|
|
|
auth_connection.add_auth('adssagf', 'user1:password1')
|
|
|
|
|
|
|
|
auth_connection = get_fake_zk()
|
|
|
|
with pytest.raises(AuthFailedError):
|
|
|
|
print("Sending 2")
|
|
|
|
auth_connection.add_auth('digest', '')
|
|
|
|
|
|
|
|
auth_connection = get_fake_zk()
|
|
|
|
with pytest.raises(AuthFailedError):
|
|
|
|
print("Sending 3")
|
|
|
|
auth_connection.add_auth('', 'user1:password1')
|
|
|
|
|
|
|
|
auth_connection = get_fake_zk()
|
|
|
|
with pytest.raises(AuthFailedError):
|
|
|
|
print("Sending 4")
|
|
|
|
auth_connection.add_auth('digest', 'user1')
|
|
|
|
|
|
|
|
auth_connection = get_fake_zk()
|
|
|
|
with pytest.raises(AuthFailedError):
|
|
|
|
print("Sending 5")
|
|
|
|
auth_connection.add_auth('digest', 'user1:password:otherpassword')
|
|
|
|
|
|
|
|
auth_connection = get_fake_zk()
|
|
|
|
with pytest.raises(AuthFailedError):
|
|
|
|
print("Sending 6")
|
|
|
|
auth_connection.add_auth('auth', 'user1:password')
|
|
|
|
|
|
|
|
auth_connection = get_fake_zk()
|
|
|
|
with pytest.raises(AuthFailedError):
|
|
|
|
print("Sending 7")
|
|
|
|
auth_connection.add_auth('world', 'somebody')
|
|
|
|
|
|
|
|
auth_connection = get_fake_zk()
|
|
|
|
with pytest.raises(InvalidACLError):
|
|
|
|
print("Sending 8")
|
|
|
|
auth_connection.create("/test_bad_acl", b"data", acl=[make_acl("dasd", "", read=True, write=False, create=True, delete=True, admin=True)])
|
|
|
|
|
|
|
|
auth_connection = get_fake_zk()
|
|
|
|
with pytest.raises(InvalidACLError):
|
|
|
|
print("Sending 9")
|
|
|
|
auth_connection.create("/test_bad_acl", b"data", acl=[make_acl("digest", "", read=True, write=False, create=True, delete=True, admin=True)])
|
|
|
|
|
|
|
|
auth_connection = get_fake_zk()
|
|
|
|
with pytest.raises(InvalidACLError):
|
|
|
|
print("Sending 10")
|
|
|
|
auth_connection.create("/test_bad_acl", b"data", acl=[make_acl("", "", read=True, write=False, create=True, delete=True, admin=True)])
|
|
|
|
|
|
|
|
auth_connection = get_fake_zk()
|
|
|
|
with pytest.raises(InvalidACLError):
|
|
|
|
print("Sending 11")
|
|
|
|
auth_connection.create("/test_bad_acl", b"data", acl=[make_acl("digest", "dsdasda", read=True, write=False, create=True, delete=True, admin=True)])
|
|
|
|
|
|
|
|
auth_connection = get_fake_zk()
|
|
|
|
with pytest.raises(InvalidACLError):
|
|
|
|
print("Sending 12")
|
|
|
|
auth_connection.create("/test_bad_acl", b"data", acl=[make_acl("digest", "dsad:DSAa:d", read=True, write=False, create=True, delete=True, admin=True)])
|
2021-05-28 11:52:19 +00:00
|
|
|
|
|
|
|
def test_auth_snapshot(started_cluster):
|
|
|
|
connection = get_fake_zk()
|
|
|
|
connection.add_auth('digest', 'user1:password1')
|
|
|
|
|
|
|
|
connection.create("/test_snapshot_acl", b"data", acl=[make_acl("auth", "", all=True)])
|
|
|
|
|
|
|
|
connection1 = get_fake_zk()
|
|
|
|
connection1.add_auth('digest', 'user2:password2')
|
|
|
|
|
|
|
|
connection1.create("/test_snapshot_acl1", b"data", acl=[make_acl("auth", "", all=True)])
|
|
|
|
|
|
|
|
connection2 = get_fake_zk()
|
|
|
|
|
|
|
|
connection2.create("/test_snapshot_acl2", b"data")
|
|
|
|
|
|
|
|
for i in range(100):
|
|
|
|
connection.create(f"/test_snapshot_acl/path{i}", b"data", acl=[make_acl("auth", "", all=True)])
|
|
|
|
|
|
|
|
node.restart_clickhouse()
|
|
|
|
|
|
|
|
connection = get_fake_zk()
|
|
|
|
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
connection.get("/test_snapshot_acl")
|
|
|
|
|
|
|
|
connection.add_auth('digest', 'user1:password1')
|
|
|
|
|
|
|
|
assert connection.get("/test_snapshot_acl")[0] == b"data"
|
|
|
|
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
connection.get("/test_snapshot_acl1")
|
|
|
|
|
|
|
|
assert connection.get("/test_snapshot_acl2")[0] == b"data"
|
|
|
|
|
|
|
|
for i in range(100):
|
|
|
|
assert connection.get(f"/test_snapshot_acl/path{i}")[0] == b"data"
|
|
|
|
|
|
|
|
connection1 = get_fake_zk()
|
|
|
|
connection1.add_auth('digest', 'user2:password2')
|
|
|
|
|
|
|
|
assert connection1.get("/test_snapshot_acl1")[0] == b"data"
|
|
|
|
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
connection1.get("/test_snapshot_acl")
|
|
|
|
|
|
|
|
|
|
|
|
connection2 = get_fake_zk()
|
|
|
|
assert connection2.get("/test_snapshot_acl2")[0] == b"data"
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
connection2.get("/test_snapshot_acl")
|
|
|
|
|
|
|
|
with pytest.raises(NoAuthError):
|
|
|
|
connection2.get("/test_snapshot_acl1")
|
2021-06-22 10:49:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
('get_zk'),
|
|
|
|
[
|
|
|
|
get_genuine_zk,
|
|
|
|
get_fake_zk
|
|
|
|
]
|
|
|
|
)
|
|
|
|
def test_get_set_acl(started_cluster, get_zk):
|
|
|
|
auth_connection = get_zk()
|
|
|
|
auth_connection.add_auth('digest', 'username1:secret1')
|
|
|
|
auth_connection.add_auth('digest', 'username2:secret2')
|
|
|
|
|
|
|
|
auth_connection.create("/test_set_get_acl", b"data", acl=[make_acl("auth", "", all=True)])
|
|
|
|
|
|
|
|
acls, stat = auth_connection.get_acls("/test_set_get_acl")
|
|
|
|
|
|
|
|
assert stat.aversion == 0
|
|
|
|
assert len(acls) == 2
|
|
|
|
for acl in acls:
|
|
|
|
assert acl.acl_list == ['ALL']
|
|
|
|
assert acl.id.scheme == 'digest'
|
|
|
|
assert acl.perms == 31
|
|
|
|
assert acl.id.id in ('username1:eGncMdBgOfGS/TCojt51xWsWv/Y=', 'username2:qgSSumukVlhftkVycylbHNvxhFU=')
|
|
|
|
|
|
|
|
|
|
|
|
other_auth_connection = get_zk()
|
|
|
|
other_auth_connection.add_auth('digest', 'username1:secret1')
|
|
|
|
other_auth_connection.add_auth('digest', 'username3:secret3')
|
|
|
|
other_auth_connection.set_acls("/test_set_get_acl", acls=[make_acl("auth", "", read=True, write=False, create=True, delete=True, admin=True)])
|
|
|
|
|
|
|
|
acls, stat = other_auth_connection.get_acls("/test_set_get_acl")
|
|
|
|
|
|
|
|
assert stat.aversion == 1
|
|
|
|
assert len(acls) == 2
|
|
|
|
for acl in acls:
|
|
|
|
assert acl.acl_list == ['READ', 'CREATE', 'DELETE', 'ADMIN']
|
|
|
|
assert acl.id.scheme == 'digest'
|
|
|
|
assert acl.perms == 29
|
|
|
|
assert acl.id.id in ('username1:eGncMdBgOfGS/TCojt51xWsWv/Y=', 'username3:CvWITOxxTwk+u6S5PoGlQ4hNoWI=')
|
|
|
|
|
|
|
|
with pytest.raises(KazooException):
|
|
|
|
other_auth_connection.set_acls("/test_set_get_acl", acls=[make_acl("auth", "", all=True)], version=0)
|