From c55c556f2cc5a37c170678400292eb571b4c3c86 Mon Sep 17 00:00:00 2001 From: Cedric Paillet Date: Mon, 13 May 2024 08:14:57 +0000 Subject: [PATCH 1/3] Refactor consul.acl Refactor consul.acl as consul.acl.token in preparation for upcoming review and eventual integration of consul.acl.policy. --- consul/api/acl.py | 6 +++ tests/api/test_acl.py | 93 ++++++++++++++++++++++++------------------- 2 files changed, 58 insertions(+), 41 deletions(-) diff --git a/consul/api/acl.py b/consul/api/acl.py index 017b04a..1f75172 100644 --- a/consul/api/acl.py +++ b/consul/api/acl.py @@ -4,6 +4,12 @@ class ACL: + def __init__(self, agent): + self.agent = agent + self.token = self.tokens = Token(agent) + + +class Token: def __init__(self, agent): self.agent = agent diff --git a/tests/api/test_acl.py b/tests/api/test_acl.py index 989615e..1325db3 100644 --- a/tests/api/test_acl.py +++ b/tests/api/test_acl.py @@ -9,37 +9,41 @@ def test_acl_permission_denied(self, acl_consul): c, _master_token, _consul_version = acl_consul # No token - pytest.raises(consul.ACLPermissionDenied, c.acl.list) - pytest.raises(consul.ACLPermissionDenied, c.acl.create) - pytest.raises(consul.ACLPermissionDenied, c.acl.update, accessor_id="00000000-0000-0000-0000-000000000002") - pytest.raises(consul.ACLPermissionDenied, c.acl.clone, accessor_id="00000000-0000-0000-0000-000000000002") - pytest.raises(consul.ACLPermissionDenied, c.acl.read, accessor_id="00000000-0000-0000-0000-000000000002") - pytest.raises(consul.ACLPermissionDenied, c.acl.delete, accessor_id="00000000-0000-0000-0000-000000000002") + pytest.raises(consul.ACLPermissionDenied, c.acl.token.list) + pytest.raises(consul.ACLPermissionDenied, c.acl.token.create) + pytest.raises( + consul.ACLPermissionDenied, c.acl.token.update, accessor_id="00000000-0000-0000-0000-000000000002" + ) + pytest.raises(consul.ACLPermissionDenied, c.acl.token.clone, accessor_id="00000000-0000-0000-0000-000000000002") + pytest.raises(consul.ACLPermissionDenied, c.acl.token.read, accessor_id="00000000-0000-0000-0000-000000000002") + pytest.raises( + consul.ACLPermissionDenied, c.acl.token.delete, accessor_id="00000000-0000-0000-0000-000000000002" + ) # Token without the right permission (acl:write or acl:read) - pytest.raises(consul.ACLPermissionDenied, c.acl.list, token="anonymous") - pytest.raises(consul.ACLPermissionDenied, c.acl.create, token="anonymous") + pytest.raises(consul.ACLPermissionDenied, c.acl.token.list, token="anonymous") + pytest.raises(consul.ACLPermissionDenied, c.acl.token.create, token="anonymous") pytest.raises( consul.ACLPermissionDenied, - c.acl.update, + c.acl.token.update, accessor_id="00000000-0000-0000-0000-000000000002", token="anonymous", ) pytest.raises( consul.ACLPermissionDenied, - c.acl.clone, + c.acl.token.clone, accessor_id="00000000-0000-0000-0000-000000000002", token="anonymous", ) pytest.raises( consul.ACLPermissionDenied, - c.acl.read, + c.acl.token.read, accessor_id="00000000-0000-0000-0000-000000000002", token="anonymous", ) pytest.raises( consul.ACLPermissionDenied, - c.acl.delete, + c.acl.token.delete, accessor_id="00000000-0000-0000-0000-000000000002", token="anonymous", ) @@ -48,7 +52,7 @@ def test_acl_list(self, acl_consul): c, master_token, _consul_version = acl_consul # Make sure both master and anonymous tokens are created - acls = c.acl.list(token=master_token) + acls = c.acl.token.list(token=master_token) master_token_repr = { "Description": "Initial Management Token", @@ -66,29 +70,29 @@ def test_acl_read(self, acl_consul): c, master_token, _consul_version = acl_consul # Unknown token - pytest.raises(consul.ConsulException, c.acl.read, accessor_id="unknown", token=master_token) + pytest.raises(consul.ConsulException, c.acl.token.read, accessor_id="unknown", token=master_token) anonymous_token_repr = { "AccessorID": "00000000-0000-0000-0000-000000000002", "SecretID": "anonymous", } - acl = c.acl.read(accessor_id="00000000-0000-0000-0000-000000000002", token=master_token) + acl = c.acl.token.read(accessor_id="00000000-0000-0000-0000-000000000002", token=master_token) assert find_recursive(acl, anonymous_token_repr) def test_acl_create(self, acl_consul): c, master_token, _consul_version = acl_consul - c.acl.create(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) - c.acl.create(secret_id="DEADBEEF-0000-0000-0000-000000000000", token=master_token) - c.acl.create( + c.acl.token.create(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) + c.acl.token.create(secret_id="DEADBEEF-0000-0000-0000-000000000000", token=master_token) + c.acl.token.create( secret_id="00000000-A5A5-0000-0000-000000000000", accessor_id="00000000-0000-A5A5-0000-000000000000", description="some token!", token=master_token, ) - assert c.acl.read(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) - assert c.acl.read(accessor_id="00000000-0000-A5A5-0000-000000000000", token=master_token) + assert c.acl.token.read(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) + assert c.acl.token.read(accessor_id="00000000-0000-A5A5-0000-000000000000", token=master_token) expected = [ { @@ -105,20 +109,20 @@ def test_acl_create(self, acl_consul): "Description": "some token!", }, ] - acl = c.acl.list(token=master_token) + acl = c.acl.token.list(token=master_token) assert find_recursive(acl, expected) def test_acl_clone(self, acl_consul): c, master_token, _consul_version = acl_consul - assert len(c.acl.list(token=master_token)) == 2 + assert len(c.acl.token.list(token=master_token)) == 2 # Unknown token - pytest.raises(consul.ConsulException, c.acl.clone, accessor_id="unknown", token=master_token) + pytest.raises(consul.ConsulException, c.acl.token.clone, accessor_id="unknown", token=master_token) - c.acl.create(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) - c.acl.clone(accessor_id="00000000-DEAD-BEEF-0000-000000000000", description="cloned", token=master_token) - assert len(c.acl.list(token=master_token)) == 4 + c.acl.token.create(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) + c.acl.token.clone(accessor_id="00000000-DEAD-BEEF-0000-000000000000", description="cloned", token=master_token) + assert len(c.acl.token.list(token=master_token)) == 4 expected = [ { @@ -128,41 +132,48 @@ def test_acl_clone(self, acl_consul): "Description": "cloned", }, ] - acl = c.acl.list(token=master_token) + acl = c.acl.token.list(token=master_token) assert find_recursive(acl, expected) def test_acl_update(self, acl_consul): c, master_token, _consul_version = acl_consul # Unknown token - pytest.raises(consul.ConsulException, c.acl.update, accessor_id="unknown", token=master_token) + pytest.raises(consul.ConsulException, c.acl.token.update, accessor_id="unknown", token=master_token) - assert len(c.acl.list(token=master_token)) == 2 - c.acl.create(accessor_id="00000000-DEAD-BEEF-0000-000000000000", description="original", token=master_token) - assert len(c.acl.list(token=master_token)) == 3 - c.acl.update(accessor_id="00000000-DEAD-BEEF-0000-000000000000", description="updated", token=master_token) - assert len(c.acl.list(token=master_token)) == 3 + assert len(c.acl.token.list(token=master_token)) == 2 + c.acl.token.create( + accessor_id="00000000-DEAD-BEEF-0000-000000000000", description="original", token=master_token + ) + assert len(c.acl.token.list(token=master_token)) == 3 + c.acl.token.update( + accessor_id="00000000-DEAD-BEEF-0000-000000000000", description="updated", token=master_token + ) + assert len(c.acl.token.list(token=master_token)) == 3 expected = { "AccessorID": "00000000-DEAD-BEEF-0000-000000000000", "Description": "updated", } - acl = c.acl.read(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) + acl = c.acl.token.read(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) assert find_recursive(acl, expected) def test_acl_delete(self, acl_consul): c, master_token, _consul_version = acl_consul - assert len(c.acl.list(token=master_token)) == 2 - c.acl.create(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) - assert len(c.acl.list(token=master_token)) == 3 - assert c.acl.read(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) + assert len(c.acl.token.list(token=master_token)) == 2 + c.acl.token.create(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) + assert len(c.acl.token.list(token=master_token)) == 3 + assert c.acl.token.read(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) # Delete and ensure it doesn't exist anymore - c.acl.delete(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) - assert len(c.acl.list(token=master_token)) == 2 + c.acl.token.delete(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) + assert len(c.acl.token.list(token=master_token)) == 2 pytest.raises( - consul.ConsulException, c.acl.read, accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token + consul.ConsulException, + c.acl.token.read, + accessor_id="00000000-DEAD-BEEF-0000-000000000000", + token=master_token, ) # From 883f6b45a750f114473b8c88f6c3224eff9d8f6d Mon Sep 17 00:00:00 2001 From: Cedric Paillet Date: Tue, 14 May 2024 06:57:01 +0000 Subject: [PATCH 2/3] create acl folder move token code to acl/token.py --- consul/api/acl/__init__.py | 7 +++++++ consul/api/{acl.py => acl/token.py} | 6 ------ tests/api/test_acl.py | 16 ++++++++-------- 3 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 consul/api/acl/__init__.py rename consul/api/{acl.py => acl/token.py} (97%) diff --git a/consul/api/acl/__init__.py b/consul/api/acl/__init__.py new file mode 100644 index 0000000..9248eb8 --- /dev/null +++ b/consul/api/acl/__init__.py @@ -0,0 +1,7 @@ +from consul.api.acl.token import Token + + +class ACL: + def __init__(self, agent): + self.agent = agent + self.token = self.tokens = Token(agent) diff --git a/consul/api/acl.py b/consul/api/acl/token.py similarity index 97% rename from consul/api/acl.py rename to consul/api/acl/token.py index 1f75172..a0ad534 100644 --- a/consul/api/acl.py +++ b/consul/api/acl/token.py @@ -3,12 +3,6 @@ from consul.callback import CB -class ACL: - def __init__(self, agent): - self.agent = agent - self.token = self.tokens = Token(agent) - - class Token: def __init__(self, agent): self.agent = agent diff --git a/tests/api/test_acl.py b/tests/api/test_acl.py index 1325db3..a23be93 100644 --- a/tests/api/test_acl.py +++ b/tests/api/test_acl.py @@ -5,7 +5,7 @@ class TestConsulAcl: - def test_acl_permission_denied(self, acl_consul): + def test_acl_token_permission_denied(self, acl_consul): c, _master_token, _consul_version = acl_consul # No token @@ -48,7 +48,7 @@ def test_acl_permission_denied(self, acl_consul): token="anonymous", ) - def test_acl_list(self, acl_consul): + def test_acl_token_list(self, acl_consul): c, master_token, _consul_version = acl_consul # Make sure both master and anonymous tokens are created @@ -66,7 +66,7 @@ def test_acl_list(self, acl_consul): assert find_recursive(acls, master_token_repr) assert find_recursive(acls, anonymous_token_repr) - def test_acl_read(self, acl_consul): + def test_acl_token_read(self, acl_consul): c, master_token, _consul_version = acl_consul # Unknown token @@ -79,7 +79,7 @@ def test_acl_read(self, acl_consul): acl = c.acl.token.read(accessor_id="00000000-0000-0000-0000-000000000002", token=master_token) assert find_recursive(acl, anonymous_token_repr) - def test_acl_create(self, acl_consul): + def test_acl_token_create(self, acl_consul): c, master_token, _consul_version = acl_consul c.acl.token.create(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) @@ -112,7 +112,7 @@ def test_acl_create(self, acl_consul): acl = c.acl.token.list(token=master_token) assert find_recursive(acl, expected) - def test_acl_clone(self, acl_consul): + def test_acl_token_clone(self, acl_consul): c, master_token, _consul_version = acl_consul assert len(c.acl.token.list(token=master_token)) == 2 @@ -135,7 +135,7 @@ def test_acl_clone(self, acl_consul): acl = c.acl.token.list(token=master_token) assert find_recursive(acl, expected) - def test_acl_update(self, acl_consul): + def test_acl_token_update(self, acl_consul): c, master_token, _consul_version = acl_consul # Unknown token @@ -158,7 +158,7 @@ def test_acl_update(self, acl_consul): acl = c.acl.token.read(accessor_id="00000000-DEAD-BEEF-0000-000000000000", token=master_token) assert find_recursive(acl, expected) - def test_acl_delete(self, acl_consul): + def test_acl_token_delete(self, acl_consul): c, master_token, _consul_version = acl_consul assert len(c.acl.token.list(token=master_token)) == 2 @@ -177,7 +177,7 @@ def test_acl_delete(self, acl_consul): ) # - # def test_acl_implicit_token_use(self, acl_consul): + # def test_acl_token_implicit_token_use(self, acl_consul): # # configure client to use the master token by default # port, _token, _consul_version = acl_consul # c = consul.Consul(port=port) From 3c6a3562579bb769eb3cbc5a1ada9fe71e05eee2 Mon Sep 17 00:00:00 2001 From: Cedric Paillet Date: Tue, 14 May 2024 11:38:01 +0000 Subject: [PATCH 3/3] add acl.policy add acl.policy.list and acl.policy.read --- consul/api/acl/__init__.py | 3 +++ consul/api/acl/policy.py | 32 ++++++++++++++++++++++++++++++++ tests/api/test_acl.py | 16 ++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 consul/api/acl/policy.py diff --git a/consul/api/acl/__init__.py b/consul/api/acl/__init__.py index 9248eb8..d19ceda 100644 --- a/consul/api/acl/__init__.py +++ b/consul/api/acl/__init__.py @@ -1,7 +1,10 @@ +from consul.api.acl.policy import Policy from consul.api.acl.token import Token class ACL: def __init__(self, agent): self.agent = agent + self.token = self.tokens = Token(agent) + self.policy = self.policies = Policy(agent) diff --git a/consul/api/acl/policy.py b/consul/api/acl/policy.py new file mode 100644 index 0000000..6787ac1 --- /dev/null +++ b/consul/api/acl/policy.py @@ -0,0 +1,32 @@ +from consul.callback import CB + + +class Policy: + def __init__(self, agent): + self.agent = agent + + def list(self, token=None): + """ + Lists all the active ACL policies. This is a privileged endpoint, and + requires a management token. *token* will override this client's + default token. + Requires a token with acl:read capability. ACLPermissionDenied raised otherwise + """ + params = [] + token = token or self.agent.token + if token: + params.append(("token", token)) + return self.agent.http.get(CB.json(), "/v1/acl/policies", params=params) + + def read(self, uuid, token=None): + """ + Returns the policy information for *id*. Requires a token with acl:read capability. + :param accessor_id: Specifies the UUID of the policy you lookup. + :param token: token with acl:read capability + :return: selected Polic information + """ + params = [] + token = token or self.agent.token + if token: + params.append(("token", token)) + return self.agent.http.get(CB.json(), f"/v1/acl/policy/{uuid}", params=params) diff --git a/tests/api/test_acl.py b/tests/api/test_acl.py index a23be93..367d5e4 100644 --- a/tests/api/test_acl.py +++ b/tests/api/test_acl.py @@ -176,6 +176,22 @@ def test_acl_token_delete(self, acl_consul): token=master_token, ) + def test_acl_policy_list(self, acl_consul): + c, master_token, _consul_version = acl_consul + + # Make sure both master and anonymous tokens are created + policies = c.acl.policy.list(token=master_token) + assert find_recursive(policies, {"ID": "00000000-0000-0000-0000-000000000001", "Name": "global-management"}) + + def test_acl_policy_read(self, acl_consul): + c, master_token, _consul_version = acl_consul + + # Unknown token + pytest.raises(consul.ConsulException, c.acl.policy.read, uuid="unknown", token=master_token) + + policy = c.acl.policy.read(uuid="00000000-0000-0000-0000-000000000001", token=master_token) + assert find_recursive(policy, {"ID": "00000000-0000-0000-0000-000000000001", "Name": "global-management"}) + # # def test_acl_token_implicit_token_use(self, acl_consul): # # configure client to use the master token by default