From 6ff16631f6c035d705f783ec936a331e5e85d85b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Oct 2025 17:31:43 +0000 Subject: [PATCH 1/5] Initial plan From 6d3cdf86e5cbb8904f0421a023650bd90e3a9585 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Oct 2025 17:52:12 +0000 Subject: [PATCH 2/5] Fix role-to-role inheritance with domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed role inheritance storage from concatenated strings to tuples, enabling proper role hierarchy within domain contexts. Now supports multi-level role chains (e.g., admin → member → viewer) in domains. Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com> --- lib/acx/enforcer.ex | 4 +- lib/acx/internal/role_group.ex | 6 +- .../rbac_domain_role_inheritance_test.exs | 107 ++++++++++++++++++ 3 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 test/enforcer/rbac_domain_role_inheritance_test.exs diff --git a/lib/acx/enforcer.ex b/lib/acx/enforcer.ex index dc836d4..9385d69 100644 --- a/lib/acx/enforcer.ex +++ b/lib/acx/enforcer.ex @@ -475,7 +475,7 @@ defmodule Acx.Enforcer do when is_atom(mapping_name) and is_binary(role1) and is_binary(role2) and is_binary(dom) do with group when not is_nil(group) <- Map.get(groups, mapping_name), false <- Enum.member?(mappings, mapping), - group <- RoleGroup.add_inheritance(group, {role1, role2 <> dom}) do + group <- RoleGroup.add_inheritance(group, {{role1, dom}, {role2, dom}}) do new_enforcer = %{ enforcer | role_groups: %{groups | mapping_name => group}, @@ -720,7 +720,7 @@ defmodule Acx.Enforcer do ) when is_atom(mapping_name) and is_binary(role1) and is_binary(role2) and is_binary(dom) do with group when not is_nil(group) <- Map.get(groups, mapping_name), - group <- RoleGroup.remove_inheritance(group, {role1, role2 <> dom}), + group <- RoleGroup.remove_inheritance(group, {{role1, dom}, {role2, dom}}), mappings <- Enum.reject(mappings, fn m -> m == mapping end), {:ok, _adpater} <- PersistAdapter.remove_policy(adapter, {mapping_name, [role1, role2, dom]}) do diff --git a/lib/acx/internal/role_group.ex b/lib/acx/internal/role_group.ex index e7cc63c..02841e4 100644 --- a/lib/acx/internal/role_group.ex +++ b/lib/acx/internal/role_group.ex @@ -145,9 +145,9 @@ defmodule Acx.Internal.RoleGroup do ...> false = f.(1, 2) ...> f.("admin", "member") true - ...> g = g |> RoleGroup.add_inheritance({"admin", "memberdomain"}) + ...> g = g |> RoleGroup.add_inheritance({{"admin", "domain"}, {"member", "domain"}}) ...> f = g |> RoleGroup.stub_3 - ...> false = f.("member", "admin", "dom") + ...> false = f.("member", "admin", "domain") ...> f.("admin", "member", "domain") true """ @@ -161,7 +161,7 @@ defmodule Acx.Internal.RoleGroup do def stub_3(%__MODULE__{} = group) do fn arg1, arg2, arg3 -> - group |> inherit_from?(arg1, arg2 <> arg3) + group |> inherit_from?({arg1, arg3}, {arg2, arg3}) end end end diff --git a/test/enforcer/rbac_domain_role_inheritance_test.exs b/test/enforcer/rbac_domain_role_inheritance_test.exs new file mode 100644 index 0000000..eb9521c --- /dev/null +++ b/test/enforcer/rbac_domain_role_inheritance_test.exs @@ -0,0 +1,107 @@ +defmodule Acx.Enforcer.RbacDomainRoleInheritanceTest do + use ExUnit.Case, async: true + alias Acx.Enforcer + + @cfile "../data/rbac_domain.conf" |> Path.expand(__DIR__) + + setup do + {:ok, e} = Enforcer.init(@cfile) + {:ok, e: e} + end + + describe "role-to-role inheritance with domains" do + test "user inherits permissions through role chain within domain", %{e: e} do + domain = "org:test123" + + # Set up permissions for each role + e = e |> Enforcer.add_policy!({:p, ["reader", domain, "blog_post", "read"]}) + e = e |> Enforcer.add_policy!({:p, ["author", domain, "blog_post", "modify"]}) + e = e |> Enforcer.add_policy!({:p, ["admin", domain, "blog_post", "delete"]}) + + # Set up role inheritance chain: admin → author → reader + e = e |> Enforcer.add_mapping_policy!({:g, "author", "reader", domain}) + e = e |> Enforcer.add_mapping_policy!({:g, "admin", "author", domain}) + + # Assign user to admin role + e = e |> Enforcer.add_mapping_policy!({:g, "user:alice", "admin", domain}) + + # Test permissions - alice should inherit all permissions through the role chain + assert e |> Enforcer.allow?(["user:alice", domain, "blog_post", "delete"]) === true + assert e |> Enforcer.allow?(["user:alice", domain, "blog_post", "modify"]) === true + assert e |> Enforcer.allow?(["user:alice", domain, "blog_post", "read"]) === true + end + + test "role inheritance is isolated per domain", %{e: e} do + domain1 = "org:company1" + domain2 = "org:company2" + + # Set up permissions for reader role in both domains + e = e |> Enforcer.add_policy!({:p, ["reader", domain1, "data", "read"]}) + e = e |> Enforcer.add_policy!({:p, ["reader", domain2, "data", "read"]}) + e = e |> Enforcer.add_policy!({:p, ["admin", domain1, "data", "write"]}) + + # Set up role inheritance only in domain1 + e = e |> Enforcer.add_mapping_policy!({:g, "admin", "reader", domain1}) + + # Assign user to admin role in both domains + e = e |> Enforcer.add_mapping_policy!({:g, "bob", "admin", domain1}) + e = e |> Enforcer.add_mapping_policy!({:g, "bob", "admin", domain2}) + + # Bob should inherit reader permissions in domain1 but not in domain2 + assert e |> Enforcer.allow?(["bob", domain1, "data", "read"]) === true + assert e |> Enforcer.allow?(["bob", domain1, "data", "write"]) === true + assert e |> Enforcer.allow?(["bob", domain2, "data", "read"]) === false + end + + test "multi-level role inheritance with domains", %{e: e} do + domain = "org:deep" + + # Set up a 4-level hierarchy + e = e |> Enforcer.add_policy!({:p, ["viewer", domain, "doc", "view"]}) + e = e |> Enforcer.add_policy!({:p, ["editor", domain, "doc", "edit"]}) + e = e |> Enforcer.add_policy!({:p, ["moderator", domain, "doc", "approve"]}) + e = e |> Enforcer.add_policy!({:p, ["super_admin", domain, "doc", "delete"]}) + + # Create inheritance chain: super_admin → moderator → editor → viewer + e = e |> Enforcer.add_mapping_policy!({:g, "editor", "viewer", domain}) + e = e |> Enforcer.add_mapping_policy!({:g, "moderator", "editor", domain}) + e = e |> Enforcer.add_mapping_policy!({:g, "super_admin", "moderator", domain}) + + # Assign user to top role + e = e |> Enforcer.add_mapping_policy!({:g, "charlie", "super_admin", domain}) + + # Charlie should inherit all permissions through the chain + assert e |> Enforcer.allow?(["charlie", domain, "doc", "delete"]) === true + assert e |> Enforcer.allow?(["charlie", domain, "doc", "approve"]) === true + assert e |> Enforcer.allow?(["charlie", domain, "doc", "edit"]) === true + assert e |> Enforcer.allow?(["charlie", domain, "doc", "view"]) === true + end + + test "removing intermediate role in chain breaks inheritance", %{e: e} do + domain = "org:break" + + # Set up permissions + e = e |> Enforcer.add_policy!({:p, ["viewer", domain, "resource", "read"]}) + e = e |> Enforcer.add_policy!({:p, ["editor", domain, "resource", "write"]}) + e = e |> Enforcer.add_policy!({:p, ["admin", domain, "resource", "delete"]}) + + # Create inheritance chain + e = e |> Enforcer.add_mapping_policy!({:g, "editor", "viewer", domain}) + e = e |> Enforcer.add_mapping_policy!({:g, "admin", "editor", domain}) + e = e |> Enforcer.add_mapping_policy!({:g, "dave", "admin", domain}) + + # Dave should have all permissions + assert e |> Enforcer.allow?(["dave", domain, "resource", "read"]) === true + assert e |> Enforcer.allow?(["dave", domain, "resource", "write"]) === true + assert e |> Enforcer.allow?(["dave", domain, "resource", "delete"]) === true + + # Remove intermediate role inheritance + e = e |> Enforcer.remove_mapping_policy!({:g, "editor", "viewer", domain}) + + # Dave should no longer have viewer permissions + assert e |> Enforcer.allow?(["dave", domain, "resource", "read"]) === false + assert e |> Enforcer.allow?(["dave", domain, "resource", "write"]) === true + assert e |> Enforcer.allow?(["dave", domain, "resource", "delete"]) === true + end + end +end From ac2e74d140adb48f132dd0ef8c2c603d57e43791 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Oct 2025 17:54:14 +0000 Subject: [PATCH 3/5] Add test case reproducing exact issue scenario MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added test that reproduces the exact scenario from the issue: admin → member → viewer role chain with domain "org:abc" Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com> --- .../rbac_domain_role_inheritance_test.exs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/enforcer/rbac_domain_role_inheritance_test.exs b/test/enforcer/rbac_domain_role_inheritance_test.exs index eb9521c..baaa792 100644 --- a/test/enforcer/rbac_domain_role_inheritance_test.exs +++ b/test/enforcer/rbac_domain_role_inheritance_test.exs @@ -10,6 +10,28 @@ defmodule Acx.Enforcer.RbacDomainRoleInheritanceTest do end describe "role-to-role inheritance with domains" do + test "reproduces issue scenario: admin → author → reader chain", %{e: e} do + # This test reproduces the exact scenario from the GitHub issue + domain = "org:abc" + + # Set up permissions: viewer role has read, member has write, admin has delete + e = e |> Enforcer.add_policy!({:p, ["viewer", domain, "resource", "read"]}) + e = e |> Enforcer.add_policy!({:p, ["member", domain, "resource", "write"]}) + e = e |> Enforcer.add_policy!({:p, ["admin", domain, "resource", "delete"]}) + + # Set up role inheritance chain: admin → member → viewer + e = e |> Enforcer.add_mapping_policy!({:g, "member", "viewer", domain}) + e = e |> Enforcer.add_mapping_policy!({:g, "admin", "member", domain}) + + # Assign user alice to admin role + e = e |> Enforcer.add_mapping_policy!({:g, "alice", "admin", domain}) + + # Alice should have all permissions through the inheritance chain + assert e |> Enforcer.allow?(["alice", domain, "resource", "delete"]) === true + assert e |> Enforcer.allow?(["alice", domain, "resource", "write"]) === true + assert e |> Enforcer.allow?(["alice", domain, "resource", "read"]) === true + end + test "user inherits permissions through role chain within domain", %{e: e} do domain = "org:test123" From 47120260983c70b24c5244b0db317878a6e1acfd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:55:39 +0000 Subject: [PATCH 4/5] Add test matching Golang Casbin implementation Added test data file and test case matching rbac_api_with_domains_test.go from Golang Casbin, demonstrating role:global_admin inheriting from role:reader and role:writer in domain1. Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com> --- ...bac_with_hierarchy_with_domains_policy.csv | 10 +++++++ .../rbac_domain_role_inheritance_test.exs | 30 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 test/data/rbac_with_hierarchy_with_domains_policy.csv diff --git a/test/data/rbac_with_hierarchy_with_domains_policy.csv b/test/data/rbac_with_hierarchy_with_domains_policy.csv new file mode 100644 index 0000000..09ce790 --- /dev/null +++ b/test/data/rbac_with_hierarchy_with_domains_policy.csv @@ -0,0 +1,10 @@ +p, role:reader, domain1, data1, read +p, role:writer, domain1, data1, write + +p, alice, domain1, data2, read +p, alice, domain2, data2, read + +g, role:global_admin, role:reader, domain1 +g, role:global_admin, role:writer, domain1 + +g, alice, role:global_admin, domain1 diff --git a/test/enforcer/rbac_domain_role_inheritance_test.exs b/test/enforcer/rbac_domain_role_inheritance_test.exs index baaa792..59d3b58 100644 --- a/test/enforcer/rbac_domain_role_inheritance_test.exs +++ b/test/enforcer/rbac_domain_role_inheritance_test.exs @@ -3,12 +3,42 @@ defmodule Acx.Enforcer.RbacDomainRoleInheritanceTest do alias Acx.Enforcer @cfile "../data/rbac_domain.conf" |> Path.expand(__DIR__) + @pfile_hierarchy "../data/rbac_with_hierarchy_with_domains_policy.csv" |> Path.expand(__DIR__) setup do {:ok, e} = Enforcer.init(@cfile) {:ok, e: e} end + describe "role-to-role inheritance with domains (matching Golang Casbin tests)" do + test "global_admin inherits from reader and writer roles in domain1", %{e: e} do + # This test matches the Golang Casbin implementation + # from rbac_api_with_domains_test.go -> TestGetImplicitRolesForDomainUser + # and uses the same test data from rbac_with_hierarchy_with_domains_policy.csv + e = + e + |> Enforcer.load_policies!(@pfile_hierarchy) + |> Enforcer.load_mapping_policies!(@pfile_hierarchy) + + # Alice has role:global_admin role in domain1 + # role:global_admin inherits from role:reader and role:writer in domain1 + # Therefore, alice should have permissions from both reader and writer roles + + # Test that alice can read data1 (from role:reader) + assert e |> Enforcer.allow?(["alice", "domain1", "data1", "read"]) === true + + # Test that alice can write data1 (from role:writer) + assert e |> Enforcer.allow?(["alice", "domain1", "data1", "write"]) === true + + # Test that alice can read data2 (direct permission) + assert e |> Enforcer.allow?(["alice", "domain1", "data2", "read"]) === true + + # Test that alice in domain2 only has direct permissions, no role inheritance + assert e |> Enforcer.allow?(["alice", "domain2", "data2", "read"]) === true + assert e |> Enforcer.allow?(["alice", "domain2", "data1", "read"]) === false + end + end + describe "role-to-role inheritance with domains" do test "reproduces issue scenario: admin → author → reader chain", %{e: e} do # This test reproduces the exact scenario from the GitHub issue From cdbf033bfa86f34adf51e4bb7e101fbe516c484c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Oct 2025 12:08:56 +0000 Subject: [PATCH 5/5] Remove custom test file to match Golang Casbin test structure Removed rbac_domain_role_inheritance_test.exs and associated test data as they are not present in Golang Casbin. Core fix in role_group.ex and enforcer.ex remains, validated by existing test suite. Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com> --- ...bac_with_hierarchy_with_domains_policy.csv | 10 -- .../rbac_domain_role_inheritance_test.exs | 159 ------------------ 2 files changed, 169 deletions(-) delete mode 100644 test/data/rbac_with_hierarchy_with_domains_policy.csv delete mode 100644 test/enforcer/rbac_domain_role_inheritance_test.exs diff --git a/test/data/rbac_with_hierarchy_with_domains_policy.csv b/test/data/rbac_with_hierarchy_with_domains_policy.csv deleted file mode 100644 index 09ce790..0000000 --- a/test/data/rbac_with_hierarchy_with_domains_policy.csv +++ /dev/null @@ -1,10 +0,0 @@ -p, role:reader, domain1, data1, read -p, role:writer, domain1, data1, write - -p, alice, domain1, data2, read -p, alice, domain2, data2, read - -g, role:global_admin, role:reader, domain1 -g, role:global_admin, role:writer, domain1 - -g, alice, role:global_admin, domain1 diff --git a/test/enforcer/rbac_domain_role_inheritance_test.exs b/test/enforcer/rbac_domain_role_inheritance_test.exs deleted file mode 100644 index 59d3b58..0000000 --- a/test/enforcer/rbac_domain_role_inheritance_test.exs +++ /dev/null @@ -1,159 +0,0 @@ -defmodule Acx.Enforcer.RbacDomainRoleInheritanceTest do - use ExUnit.Case, async: true - alias Acx.Enforcer - - @cfile "../data/rbac_domain.conf" |> Path.expand(__DIR__) - @pfile_hierarchy "../data/rbac_with_hierarchy_with_domains_policy.csv" |> Path.expand(__DIR__) - - setup do - {:ok, e} = Enforcer.init(@cfile) - {:ok, e: e} - end - - describe "role-to-role inheritance with domains (matching Golang Casbin tests)" do - test "global_admin inherits from reader and writer roles in domain1", %{e: e} do - # This test matches the Golang Casbin implementation - # from rbac_api_with_domains_test.go -> TestGetImplicitRolesForDomainUser - # and uses the same test data from rbac_with_hierarchy_with_domains_policy.csv - e = - e - |> Enforcer.load_policies!(@pfile_hierarchy) - |> Enforcer.load_mapping_policies!(@pfile_hierarchy) - - # Alice has role:global_admin role in domain1 - # role:global_admin inherits from role:reader and role:writer in domain1 - # Therefore, alice should have permissions from both reader and writer roles - - # Test that alice can read data1 (from role:reader) - assert e |> Enforcer.allow?(["alice", "domain1", "data1", "read"]) === true - - # Test that alice can write data1 (from role:writer) - assert e |> Enforcer.allow?(["alice", "domain1", "data1", "write"]) === true - - # Test that alice can read data2 (direct permission) - assert e |> Enforcer.allow?(["alice", "domain1", "data2", "read"]) === true - - # Test that alice in domain2 only has direct permissions, no role inheritance - assert e |> Enforcer.allow?(["alice", "domain2", "data2", "read"]) === true - assert e |> Enforcer.allow?(["alice", "domain2", "data1", "read"]) === false - end - end - - describe "role-to-role inheritance with domains" do - test "reproduces issue scenario: admin → author → reader chain", %{e: e} do - # This test reproduces the exact scenario from the GitHub issue - domain = "org:abc" - - # Set up permissions: viewer role has read, member has write, admin has delete - e = e |> Enforcer.add_policy!({:p, ["viewer", domain, "resource", "read"]}) - e = e |> Enforcer.add_policy!({:p, ["member", domain, "resource", "write"]}) - e = e |> Enforcer.add_policy!({:p, ["admin", domain, "resource", "delete"]}) - - # Set up role inheritance chain: admin → member → viewer - e = e |> Enforcer.add_mapping_policy!({:g, "member", "viewer", domain}) - e = e |> Enforcer.add_mapping_policy!({:g, "admin", "member", domain}) - - # Assign user alice to admin role - e = e |> Enforcer.add_mapping_policy!({:g, "alice", "admin", domain}) - - # Alice should have all permissions through the inheritance chain - assert e |> Enforcer.allow?(["alice", domain, "resource", "delete"]) === true - assert e |> Enforcer.allow?(["alice", domain, "resource", "write"]) === true - assert e |> Enforcer.allow?(["alice", domain, "resource", "read"]) === true - end - - test "user inherits permissions through role chain within domain", %{e: e} do - domain = "org:test123" - - # Set up permissions for each role - e = e |> Enforcer.add_policy!({:p, ["reader", domain, "blog_post", "read"]}) - e = e |> Enforcer.add_policy!({:p, ["author", domain, "blog_post", "modify"]}) - e = e |> Enforcer.add_policy!({:p, ["admin", domain, "blog_post", "delete"]}) - - # Set up role inheritance chain: admin → author → reader - e = e |> Enforcer.add_mapping_policy!({:g, "author", "reader", domain}) - e = e |> Enforcer.add_mapping_policy!({:g, "admin", "author", domain}) - - # Assign user to admin role - e = e |> Enforcer.add_mapping_policy!({:g, "user:alice", "admin", domain}) - - # Test permissions - alice should inherit all permissions through the role chain - assert e |> Enforcer.allow?(["user:alice", domain, "blog_post", "delete"]) === true - assert e |> Enforcer.allow?(["user:alice", domain, "blog_post", "modify"]) === true - assert e |> Enforcer.allow?(["user:alice", domain, "blog_post", "read"]) === true - end - - test "role inheritance is isolated per domain", %{e: e} do - domain1 = "org:company1" - domain2 = "org:company2" - - # Set up permissions for reader role in both domains - e = e |> Enforcer.add_policy!({:p, ["reader", domain1, "data", "read"]}) - e = e |> Enforcer.add_policy!({:p, ["reader", domain2, "data", "read"]}) - e = e |> Enforcer.add_policy!({:p, ["admin", domain1, "data", "write"]}) - - # Set up role inheritance only in domain1 - e = e |> Enforcer.add_mapping_policy!({:g, "admin", "reader", domain1}) - - # Assign user to admin role in both domains - e = e |> Enforcer.add_mapping_policy!({:g, "bob", "admin", domain1}) - e = e |> Enforcer.add_mapping_policy!({:g, "bob", "admin", domain2}) - - # Bob should inherit reader permissions in domain1 but not in domain2 - assert e |> Enforcer.allow?(["bob", domain1, "data", "read"]) === true - assert e |> Enforcer.allow?(["bob", domain1, "data", "write"]) === true - assert e |> Enforcer.allow?(["bob", domain2, "data", "read"]) === false - end - - test "multi-level role inheritance with domains", %{e: e} do - domain = "org:deep" - - # Set up a 4-level hierarchy - e = e |> Enforcer.add_policy!({:p, ["viewer", domain, "doc", "view"]}) - e = e |> Enforcer.add_policy!({:p, ["editor", domain, "doc", "edit"]}) - e = e |> Enforcer.add_policy!({:p, ["moderator", domain, "doc", "approve"]}) - e = e |> Enforcer.add_policy!({:p, ["super_admin", domain, "doc", "delete"]}) - - # Create inheritance chain: super_admin → moderator → editor → viewer - e = e |> Enforcer.add_mapping_policy!({:g, "editor", "viewer", domain}) - e = e |> Enforcer.add_mapping_policy!({:g, "moderator", "editor", domain}) - e = e |> Enforcer.add_mapping_policy!({:g, "super_admin", "moderator", domain}) - - # Assign user to top role - e = e |> Enforcer.add_mapping_policy!({:g, "charlie", "super_admin", domain}) - - # Charlie should inherit all permissions through the chain - assert e |> Enforcer.allow?(["charlie", domain, "doc", "delete"]) === true - assert e |> Enforcer.allow?(["charlie", domain, "doc", "approve"]) === true - assert e |> Enforcer.allow?(["charlie", domain, "doc", "edit"]) === true - assert e |> Enforcer.allow?(["charlie", domain, "doc", "view"]) === true - end - - test "removing intermediate role in chain breaks inheritance", %{e: e} do - domain = "org:break" - - # Set up permissions - e = e |> Enforcer.add_policy!({:p, ["viewer", domain, "resource", "read"]}) - e = e |> Enforcer.add_policy!({:p, ["editor", domain, "resource", "write"]}) - e = e |> Enforcer.add_policy!({:p, ["admin", domain, "resource", "delete"]}) - - # Create inheritance chain - e = e |> Enforcer.add_mapping_policy!({:g, "editor", "viewer", domain}) - e = e |> Enforcer.add_mapping_policy!({:g, "admin", "editor", domain}) - e = e |> Enforcer.add_mapping_policy!({:g, "dave", "admin", domain}) - - # Dave should have all permissions - assert e |> Enforcer.allow?(["dave", domain, "resource", "read"]) === true - assert e |> Enforcer.allow?(["dave", domain, "resource", "write"]) === true - assert e |> Enforcer.allow?(["dave", domain, "resource", "delete"]) === true - - # Remove intermediate role inheritance - e = e |> Enforcer.remove_mapping_policy!({:g, "editor", "viewer", domain}) - - # Dave should no longer have viewer permissions - assert e |> Enforcer.allow?(["dave", domain, "resource", "read"]) === false - assert e |> Enforcer.allow?(["dave", domain, "resource", "write"]) === true - assert e |> Enforcer.allow?(["dave", domain, "resource", "delete"]) === true - end - end -end