{"id":19298235,"url":"https://github.com/casbin/casbin-ex","last_synced_at":"2025-04-22T09:32:16.136Z","repository":{"id":42186988,"uuid":"261421603","full_name":"casbin/casbin-ex","owner":"casbin","description":"An authorization library that supports access control models like ACL, RBAC, ABAC in Elixir","archived":false,"fork":false,"pushed_at":"2024-05-03T23:48:43.000Z","size":140,"stargazers_count":77,"open_issues_count":1,"forks_count":18,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-10T18:47:52.363Z","etag":null,"topics":["abac","access-control","acl","auth","authorization","authz","casbin","elixir","permission","rbac"],"latest_commit_sha":null,"homepage":"https://casbin.org","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/casbin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"casbin"}},"created_at":"2020-05-05T10:09:19.000Z","updated_at":"2025-01-31T02:26:16.000Z","dependencies_parsed_at":"2024-03-25T09:21:23.391Z","dependency_job_id":"55f78d4e-5d79-4733-af6b-28a4969e7ab6","html_url":"https://github.com/casbin/casbin-ex","commit_stats":{"total_commits":77,"total_committers":9,"mean_commits":8.555555555555555,"dds":0.1558441558441559,"last_synced_commit":"85efaa43d38ce89065b150debbe51f9dbc7427b9"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/casbin%2Fcasbin-ex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/casbin%2Fcasbin-ex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/casbin%2Fcasbin-ex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/casbin%2Fcasbin-ex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/casbin","download_url":"https://codeload.github.com/casbin/casbin-ex/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248325219,"owners_count":21084891,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["abac","access-control","acl","auth","authorization","authz","casbin","elixir","permission","rbac"],"created_at":"2024-11-09T23:07:21.866Z","updated_at":"2025-04-22T09:32:16.119Z","avatar_url":"https://github.com/casbin.png","language":"Elixir","funding_links":["https://github.com/sponsors/casbin"],"categories":[],"sub_categories":[],"readme":"# Acx\nAcx is an access control library that can do whatever shit\n[Casbin](https://casbin.org/) can and much more...\n\n## Installation\n\n```elixir\ndef deps do\n  [\n    {:acx, git: \"https://github.com/casbin/casbin-ex\"}\n  ]\nend\n```\n\n## [Access Control List (ACL)](https://en.wikipedia.org/wiki/Access-control_list)\n\nLet's say we have just built a wonderful blogging system, and now we want\nto add the access control feature to it to control **who can do what** with\nthe resource named `blog_post`. Our system requirements would look something\nlike this:\n\n|       | blog_post.create | blog_post.read | blog_post.modify | blog_post.delete |\n| ----- |:----------------:|:--------------:|:----------------:|:----------------:|\n| alice |     yes          |       yes      |        yes       |          yes     |\n| bob   |     no           |       yes      |        no        |          yes     |\n| peter |     yes          |       yes      |        yes       |          no      |\n\nBased on this requirements, our first step is to choose an appropriate\naccess control model. Let's say we choose to go with the ACL model.\nSimilar to Casbin, in Acx, an access control model is abstracted into a\nconfig file based on the **[PERM Meta-Model](https://casbin.org/docs/how-it-works)**. The content of the config file for our system would look\nlike so:\n\n```ini\n# blog_ac.conf\n\n# We want each request to be a tuple of three items, in which first item\n# associated with the attribute named `sub`, second `obj` and third `act`.\n# An example of a valid request based on this definition is\n# `[\"alice, \"blog_post\", \"read\"]` (can `alice` `read` `blog_post`?).\n[request_definition]\nr = sub, obj, act\n\n# Each policy definition should have a key and a list of attributes separated by\n# an equal `=` sign. In Acx all policy rules have in common the `eft` attribute\n# and it can only take value of either `\"allow\"` or `\"deny\"`, so you can ommit\n# it in your policy definition.\n[policy_definition]\np = sub, obj, act\n\n# Policy effect defines whether the access should be approved or denied\n# if multiple policy rules match the request.\n# We use the following policy effect for our blog system to mean that:\n# if there's any matched policy rule of type `allow` (i.e `eft` == \"allow\"),\n# the final effect is `allow`. Which also means if there's no match or all\n# matches are of type `deny`, the final effect is `deny`.\n[policy_effect]\ne = some(where (p.eft == allow))\n\n# matchers is just a boolean expression used to determine whether a request\n# matches the given policy rule.\n[matchers]\nm = r.sub == p.sub \u0026\u0026 r.obj == p.obj \u0026\u0026 r.act == p.act\n\n```\n\nDone with the model. Our next step is to define policy rules based on the\nsystem requirements and the policy definition. We can choose to put these\nrules in a database or in our case a `*.csv` file named `blog_ac.csv`:\n\n```\np, alice, blog_post, create\np, alice, blog_post, read\np, alice, blog_post, modify\np, alice, blog_post, delete\n\np, bob, blog_post, read\n\np, peter, blog_post, create\np, peter, blog_post, read\np, peter, blog_post, modify\n\n```\n\nNote that, first of all , since we don't specify the value for the `eft`\nattribute for any of the above rules, all of our rules are of type `allow`\n(a.k.a `yes`) by default. Second, we don't have to define any `deny`\n(a.k.a `no`) rules for our system.\n\nThe final step is to combine the model, the policy rules and Acx to\nconstruct our access control system.\n\n```elixir\nalias Acx.{EnforcerSupervisor, EnforcerServer}\n\n# Give our system a name so that we can reference it by its name\n# rather than the process ID (a.k.a `pid`).\nename = \"blog_ac\"\n\n# Starts a new enforcer process and supervises it.\nEnforcerSupervisor.start_enforcer(ename, blog_ac.conf)\n\n# Load policy rules.\nEnforcerServer.load_policies(ename, blog_ac.csv)\n\nnew_req = [\"alice\", \"blog_post\", \"read\"]\n\ncase EnforcerServer.allow?(ename, new_req) do\n  true -\u003e\n    # Yes, this `new_req` is allowed\n\n  false -\u003e\n    # Nope, `new_req` is denied (not allowed)\nend\n```\n\nIf you are not a fan of supervision tree or stateful server, read on to\nfigure our how to use Acx without any of those.\n\n## [Role Base Access Control (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control)\n\nOur ACL access control system is working just fine for initial purpose, but\nnow our bussiness is expanding like nuts, so we need a more flexible access\ncontrol model to meet new bussiness requirements. We went back to the\ndrawing-board and came up with this design for our new system:\n\n![rbac diagram](rbac.png)\n\nWe assign different roles to different users, `bob` has the role `reader`,\n`peter` has the role `author` and `alice` has the role `admin`, and so on...\nWe then define mappings from `role` to `permission` (instead of asking\n*who can do what* like in the ACL model, now it's time to ask **which role can\ndo what?**). We also define mappings from role to role  to represent\ninheritance. In the above diagram, we have `admin` inherits from `author`,\nwhich in turn inherits from `reader`.\n\nNote that *has role* or *inherits from* relation is [transitive](https://en.wikipedia.org/wiki/Transitive_relation).\n\nBased on this design, the config file for our new model would look like\nso:\n\n```ini\n# blog_ac.conf\n\n[request_definition]\nr = sub, obj, act\n\n[policy_definition]\np = sub, obj, act\n\n# This is the name of the mapping we mentioned above, I call it `g`\n# to make it compatible with Casbin (which for some reason only allows name\n# like `g, g2, ...`) but you can name it whatever shit you like so long as\n# you're consistent.\n[role_definition]\ng = _, _\n\n[policy_effect]\ne = some(where (p.eft == allow))\n\n# We change this part `r.sub == p.sub` of our initial matcher expression to\n# `g(r.sub, p.sub)` to mean that: if `r.sub` has role (or inherits from)\n# `p.sub` and ... and ...\n[matchers]\nm = g(r.sub, p.sub) \u0026\u0026 r.obj == p.obj \u0026\u0026 r.act == p.act\n\n```\n\nAnd the content of the file `blog_ac.csv` now become:\n\n```\np, reader, blog_post, read\np, author, blog_post, modify\np, author, blog_post, create\np, admin, blog_post, delete\n\ng, bob, reader\ng, peter, author\ng, alice, admin\n\ng, author, reader\ng, admin, author\n```\n\nFinally:\n\n```elixir\nalias Acx.{EnforcerSupervisor, EnforcerServer}\n\nename = \"blog_ac\"\nEnforcerSupervisor.start_enforcer(ename, blog_ac.conf)\nEnforcerServer.load_policies(ename, blog_ac.csv)\n\n# You only have to add this new line to load mapping rules. Unlike Casbin\n# Acx distinguishes from `normal` policy rules and `mapping` rules.\n# We've just happended to put the two types of rules in the same `*.csv` file.\nEnforcerServer.load_mapping_policies(ename, blog_ac.csv)\n\nnew_req = [\"alice\", \"blog_post\", \"read\"]\n\ncase EnforcerServer.allow?(ename, new_req) do\n  true -\u003e\n    # Yes, this `new_req` is allowed\n\n  false -\u003e\n    # Nope, `new_req` is denied (not allowed)\nend\n```\n\nAs you can see, the cost of swithching or upgrading to another access control\nmechanism is just as cheap as modifying the configuration.\n\n## RESTful example\nTODO\n\nThe config file:\n\n```ini\n# restful_ac.conf\n\n[request_definition]\nr = sub, obj, act\n\n[policy_definition]\np = sub, obj, act\n\n[policy_effect]\ne = some(where (p.eft == allow))\n\n# The function named `match?` will be defined later in code.\n[matchers]\nm = r.sub == p.sub \u0026\u0026 match?(r.obj, p.obj) \u0026\u0026 match?(r.act, p.act)\n```\n\nPolicy rules `restful_ac.csv`:\n\n```\np, alice, /alice_data/.*, GET\np, alice, /alice_data/resource1, POST\n\np, bob, /alice_data/resource2, GET\np, bob, /bob_data/.*, POST\n\np, peter, /peter_data, (GET)|(POST)\n```\n\nCode:\n\n```elixir\nalias Acx.{EnforcerSupervisor, EnforcerServer}\n\nename = \"restful_ac\"\nEnforcerSupervisor.start_enforcer(ename, restful_ac.conf)\nEnforcerServer.load_policies(ename, restful_ac.csv)\n\n# We have to define the `match?/2` and add it to our enforcer system.\n\n# This anonymous function is idential to the built-in function\n# `regex_match?/2`, but I redifine it here to illustrate the idea of\n# how you can customize the system to meet your need.\nfun = fn str, pattern -\u003e\n  case Regex.compile(\"^#{pattern}$\") do\n    {:error, _} -\u003e\n      false\n\n    {:ok, r} -\u003e\n      Regex.match?(r, str)\n  end\nend\n\n# add `fun` to our system. Note that the name `:match?` is an atom not\n# a string.\nEnforcerServer.add_fun(ename, {:match?, fun})\n\nnew_req = [\"alice\", \"/alice_data/foo\", \"GET\"]\n\ncase EnforcerServer.allow?(ename, new_req) do\n  true -\u003e\n    # Yes, this `new_req` is allowed\n\n  false -\u003e\n    # Nope, `new_req` is denied (not allowed)\nend\n```\n\n## TODO\n\n### Global\n\nImplement all [matchers' functions](https://casbin.org/docs/function):\n- [x] regexMatch\n- [ ] keyMatch\n- [ ] keyGet\n- [x] KeyMatch2\n- [ ] keyGet2\n- [ ] keyMatch3\n- [ ] keyMatch4\n- [ ] ipMatch\n- [ ] globMatch\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcasbin%2Fcasbin-ex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcasbin%2Fcasbin-ex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcasbin%2Fcasbin-ex/lists"}