This is an automated email from the ASF dual-hosted git repository.
hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-website.git
The following commit(s) were added to refs/heads/master by this push:
new 1ab6e624c docs: add RBAC tutorials, comparison pages and llms.txt
1ab6e624c is described below
commit 1ab6e624c6638547c650645a0d3af1520069c625
Author: Yang Luo <[email protected]>
AuthorDate: Sat Oct 3 22:07:22 2026 +0800
docs: add RBAC tutorials, comparison pages and llms.txt
Add step-by-step RBAC tutorials for Go (Gin), Node.js (Express), Python
(FastAPI) and Java (Spring Boot), comparisons with OpenFGA, OPA, CASL and
Cedar, and generate /llms.txt from the docs sidebar at build time.
---
docs/CompareCASL.mdx | 82 +++++++++++++++++
docs/CompareCedar.mdx | 81 +++++++++++++++++
docs/CompareOPA.mdx | 90 +++++++++++++++++++
docs/CompareOpenFGA.mdx | 73 ++++++++++++++++
docs/Comparison.mdx | 49 +++++++++++
docs/Overview.mdx | 5 ++
docs/TutorialGoRBAC.mdx | 153 ++++++++++++++++++++++++++++++++
docs/TutorialJavaRBAC.mdx | 209 ++++++++++++++++++++++++++++++++++++++++++++
docs/TutorialNodejsRBAC.mdx | 147 +++++++++++++++++++++++++++++++
docs/TutorialPythonRBAC.mdx | 144 ++++++++++++++++++++++++++++++
docusaurus.config.js | 11 +++
sidebars.js | 22 +++++
src/plugins/llms-txt.js | 78 +++++++++++++++++
13 files changed, 1144 insertions(+)
diff --git a/docs/CompareCASL.mdx b/docs/CompareCASL.mdx
new file mode 100644
index 000000000..dce76eec0
--- /dev/null
+++ b/docs/CompareCASL.mdx
@@ -0,0 +1,82 @@
+---
+id: casbin-vs-casl
+title: Casbin vs. CASL
+description: Apache Casbin and CASL compared for JavaScript and TypeScript
applications - rules as data versus abilities in code, frontend and backend
use, multi-language services, and when to choose each.
+keywords: [casbin vs casl, casl alternatives, casl vs casbin, nodejs
authorization library, javascript rbac, typescript permissions]
+authors: [hsluoyz]
+---
+
+CASL and Casbin are the two authorization libraries JavaScript developers
compare most often. CASL is written for JavaScript and TypeScript and defines
permissions in code. Casbin keeps permissions outside the code as a model and
policy rules, and the same rules run in Node.js, the browser, and other
languages.
+
+## Summary
+
+| | Casbin | CASL |
+|---|---|---|
+| Languages | Node.js and browser, plus Go, Java, Python, .NET, PHP, Rust,
C++, and more | JavaScript and TypeScript |
+| How rules are written | A model file plus policy rules as rows | `can` and
`cannot` calls in code, or rule objects in JSON |
+| Typical rule | `p, editor, article, write` | `can('update', 'Article', {
authorId: user.id })` |
+| Roles | Built in: role assignment, hierarchies, per-tenant roles | You map
roles to abilities in your own code |
+| Rule storage | Database adapters (Sequelize, TypeORM, Prisma, Mongoose, and
others) | You store and load rules yourself |
+| Frontend | [casbin.js](/docs/frontend) receives the user's permissions from
the backend | Packages for React, Vue, and Angular |
+| Database query filtering | Through the policy and your own queries | Helpers
that turn conditions into Prisma or Mongoose queries |
+| Governance and license | Apache Software Foundation (Incubating), Apache-2.0
| Community project, MIT |
+
+## Rules in code versus rules as data
+
+A CASL ability is built in code for the current user:
+
+```js
+import { AbilityBuilder, createMongoAbility } from "@casl/ability";
+
+function defineAbilityFor(user) {
+ const { can, build } = new AbilityBuilder(createMongoAbility);
+ can("read", "Article");
+ if (user.role === "editor") {
+ can("update", "Article", { authorId: user.id });
+ }
+ return build();
+}
+```
+
+This is easy to read, type-safe in TypeScript, and convenient when the rules
are fixed and known to developers. Changing who may do what means changing and
redeploying code, unless you build your own storage for rule objects.
+
+Casbin separates the two parts. The model describes the shape of a rule and is
written once:
+
+```ini
+[matchers]
+m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
+```
+
+The rules are data:
+
+```csv
+p, reader, article, read
+p, editor, article, update
+g, alice, editor
+```
+
+An administrator can add a role or a permission at runtime through the
[Management API](/docs/management-api), and the change is saved to your
database by an [adapter](/docs/adapters). Ownership rules such as "editors
update only their own articles" are written in the matcher with
[ABAC](/docs/abac): `r.sub == r.obj.authorId`.
+
+## Frontend
+
+CASL is strong in the browser. Its React, Vue, and Angular packages make it
simple to show or hide buttons, and the same ability object can be used on both
sides.
+
+With Casbin, the server remains the source of truth. It sends the current
user's permissions to the browser, where [casbin.js](/docs/frontend) answers
`can` questions for UI components. The frontend never holds other users' rules.
+
+## More than one language
+
+CASL covers JavaScript. If part of your backend is written in Go, Java, or
Python, those services need a different authorization library and a second copy
of the rules.
+
+A Casbin model and policy are language-neutral. A Node.js API, a Go worker,
and a Python service can load the same policy from the same database and reach
the same decisions.
+
+## When to choose which
+
+Choose **CASL** if your stack is entirely JavaScript or TypeScript,
permissions are defined by developers and rarely change at runtime, and you
want tight integration with UI frameworks and with Prisma or Mongoose queries.
+
+Choose **Casbin** if administrators manage roles and permissions at runtime,
you need role hierarchies or multi-tenant roles out of the box, your rules
should live in a database, or several services in different languages must
enforce the same policy.
+
+## Try Casbin
+
+Follow the [Node.js and Express tutorial](/docs/nodejs-rbac-tutorial), or test
a model and policy in the [online editor](/docs/online-editor).
+
+See also the [comparison overview](/docs/comparison).
diff --git a/docs/CompareCedar.mdx b/docs/CompareCedar.mdx
new file mode 100644
index 000000000..3721c7a7a
--- /dev/null
+++ b/docs/CompareCedar.mdx
@@ -0,0 +1,81 @@
+---
+id: casbin-vs-cedar
+title: Casbin vs. Cedar
+description: Apache Casbin and Cedar compared - configurable PERM model versus
the Cedar policy language, validation and analysis, language support, policy
storage, and when to choose each.
+keywords: [casbin vs cedar, cedar alternatives, cedar policy language, amazon
verified permissions alternative, authorization library comparison]
+authors: [hsluoyz]
+---
+
+Cedar is an open-source policy language and evaluation engine created at AWS;
it is the language behind Amazon Verified Permissions. Casbin is an
authorization library under the Apache Software Foundation with a configurable
model and implementations in many languages. Both evaluate requests inside your
application; they differ in how rules are expressed and in what surrounds the
engine.
+
+## Summary
+
+| | Casbin | Cedar |
+|---|---|---|
+| How rules are written | A model file plus policy rules as rows | `permit`
and `forbid` statements in the Cedar language |
+| Access control styles | ACL, RBAC, RBAC with domains, ABAC, ReBAC, and more,
chosen in the model | RBAC through entity groups, ABAC through conditions |
+| Semantics | Defined by the model's policy effect; allow-override,
deny-override, and priority are available | Fixed: deny by default, `forbid`
overrides `permit` |
+| Validation | Model syntax is checked on load; test with the online editor |
Policies are validated against a schema; automated analysis tools exist |
+| Native implementations | Go, Java, Node.js, Python, .NET, PHP, Rust, C++,
and more | Rust, with Java, Go, and WebAssembly options |
+| Storage and distribution | Database adapters and watchers | You provide
storage, or use the managed AWS service |
+| Managed service | None required; runs in your process | Amazon Verified
Permissions |
+| Governance | Apache Software Foundation (Incubating) | Open source from AWS,
Apache-2.0 |
+
+## Policy language versus configurable model
+
+A Cedar policy reads like a sentence:
+
+```cedar
+permit (
+ principal in Role::"editor",
+ action == Action::"update",
+ resource in Folder::"articles"
+)
+when { resource.owner == principal };
+```
+
+The language has a fixed meaning: everything is denied unless a `permit`
matches, and any matching `forbid` wins. Policies can be checked against a
schema before they are deployed, and the design allows automated reasoning
about what a policy set permits.
+
+Casbin splits the same rule into a model and data. Here the request object is
an article with `Folder` and `Owner` fields ([ABAC](/docs/abac)):
+
+```ini
+[matchers]
+m = g(r.sub, p.sub) && r.obj.Folder == p.obj && r.act == p.act && r.obj.Owner
== r.sub
+```
+
+```csv
+p, editor, articles, update
+g, alice, editor
+```
+
+The model decides how requests and policy rules are compared and how results
combine. That flexibility is the point of Casbin's PERM design: the same engine
can behave as a simple ACL, as RBAC with tenants, as deny-override, or as an
ordered firewall-style list. See [How it works](/docs/how-it-works) and
[Supported models](/docs/supported-models).
+
+## What surrounds the engine
+
+Cedar gives you an evaluator. Loading entities, storing policies, and
distributing changes are left to your application, or to Amazon Verified
Permissions if you use AWS.
+
+Casbin includes those pieces:
+
+- [Adapters](/docs/adapters) persist policy in SQL and NoSQL databases, files,
and cloud stores.
+- [Watchers](/docs/watchers) keep multiple instances synchronized.
+- [Role managers](/docs/role-managers) resolve role hierarchies, including
from external sources such as LDAP or an identity provider.
+- The [Management API](/docs/management-api) and [RBAC API](/docs/rbac-api)
let administrators change permissions at runtime.
+- [Middleware](/docs/middlewares) exists for most web frameworks.
+
+## Languages
+
+Cedar's reference implementation is in Rust, with bindings and ports for a few
other languages.
+
+Casbin has maintained native implementations for Go, Java, Node.js, Python,
.NET, PHP, Rust, C++, and more, and a model and policy written once work in all
of them.
+
+## When to choose which
+
+Choose **Cedar** if you want a readable policy language with schema validation
and formal analysis, or you plan to use Amazon Verified Permissions.
+
+Choose **Casbin** if you need to choose or customize the access control model,
want storage, synchronization, and management APIs included, need a native
library in your language, or prefer a project under Apache Software Foundation
governance that does not depend on one cloud provider.
+
+## Try Casbin
+
+Test a model and policy in the [online editor](/docs/online-editor), or follow
the tutorial for [Go](/docs/go-rbac-tutorial),
[Node.js](/docs/nodejs-rbac-tutorial), [Python](/docs/python-rbac-tutorial), or
[Java](/docs/java-rbac-tutorial).
+
+See also the [comparison overview](/docs/comparison).
diff --git a/docs/CompareOPA.mdx b/docs/CompareOPA.mdx
new file mode 100644
index 000000000..2c64dd9f9
--- /dev/null
+++ b/docs/CompareOPA.mdx
@@ -0,0 +1,90 @@
+---
+id: casbin-vs-opa
+title: Casbin vs. Open Policy Agent (OPA)
+description: Apache Casbin and Open Policy Agent compared - model and policy
data versus Rego, embedded library versus policy agent, runtime policy
management, language support, and when to choose each.
+keywords: [casbin vs opa, opa vs casbin, open policy agent alternative, rego
alternative, policy engine comparison, authorization library]
+authors: [hsluoyz]
+---
+
+Open Policy Agent (OPA) and Casbin are both open-source policy engines, and
they are often shortlisted together. OPA is a general-purpose engine: one
language, Rego, for decisions across Kubernetes, infrastructure, and services.
Casbin concentrates on access control inside applications, with rules stored as
data that can be changed at runtime.
+
+## Summary
+
+| | Casbin | OPA |
+|---|---|---|
+| Scope | Application access control | General-purpose policy for any JSON
input |
+| How rules are written | A short model file plus policy rules as rows |
Programs in the Rego language |
+| Typical deployment | Library in your process | Sidecar or daemon queried
over HTTP; also a Go library and WebAssembly |
+| Native implementations | Go, Java, Node.js, Python, .NET, PHP, Rust, C++,
and more | Go |
+| Where rules and data live | Your database, through adapters | Bundles and
data documents loaded into OPA's memory |
+| Changing permissions | Add or remove a policy row through the Management API
| Change the data or publish a new policy bundle |
+| Who usually edits rules | Application administrators, through a UI or API |
Engineers, through code review |
+| Governance | Apache Software Foundation (Incubating) | Cloud Native
Computing Foundation |
+
+## Rego versus model and policy
+
+In OPA you write the decision logic itself in Rego:
+
+```rego
+package app.authz
+
+default allow := false
+
+allow if {
+ some role in data.user_roles[input.user]
+ some grant in data.role_grants[role]
+ grant.action == input.action
+ grant.resource == input.resource
+}
+```
+
+Rego is expressive. It can inspect arbitrary nested JSON, which is why OPA is
used for Kubernetes admission control and configuration checks as well as for
API authorization. It is also a language your team has to learn, test, and
review.
+
+In Casbin the logic is one line in a model file and the grants are rows:
+
+```ini
+[matchers]
+m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
+```
+
+```csv
+p, editor, article, write
+g, alice, editor
+```
+
+Most access control requirements (roles, role hierarchies, tenants, resource
ownership, path patterns) fit this shape; see [Supported
models](/docs/supported-models). For logic the built-in operators do not cover,
a matcher can call a [custom function](/docs/function) written in your
application's language.
+
+## Managing permissions at runtime
+
+Applications usually need an administration screen where someone assigns roles
and grants permissions. With Casbin this is a direct API call:
+
+```go
+e.AddRoleForUser("bob", "editor")
+e.AddPolicy("editor", "comment", "write")
+```
+
+The change is enforced immediately, saved through the
[adapter](/docs/adapters) to your database, and propagated to other instances
by a [watcher](/docs/watchers). The [Management API](/docs/management-api) and
[RBAC API](/docs/rbac-api) cover listing and querying as well.
+
+With OPA, the equivalent is a data document that your own service maintains
and ships to every OPA instance, through bundles or the data API. That works
well, and the storage, distribution, and admin API are yours to build.
+
+## Deployment and languages
+
+OPA is written in Go. Go programs can embed it; other languages normally query
an OPA process over HTTP or run policies compiled to WebAssembly.
+
+Casbin has native implementations for Go, Java, Node.js, Python, .NET, PHP,
Rust, C++, and others. A check is an in-process function call with no
serialization or network hop. The same model and policy files work unchanged
across languages.
+
+## Infrastructure policy
+
+For Kubernetes admission control and cloud configuration, OPA with Gatekeeper
is the established choice and has a large library of ready-made policies.
Casbin can also be used there, with [K8s-Gatekeeper](/docs/k8s-gatekeeper) and
[Envoy](/docs/envoy) integrations, but its strength is application
authorization.
+
+## When to choose which
+
+Choose **OPA** if you want one policy language across infrastructure and
services, your decisions depend on complex structured input, or policies should
be authored and versioned as code by engineers.
+
+Choose **Casbin** if you are adding roles and permissions to an application,
permissions must be editable at runtime by administrators, you want rules
stored in your existing database, or you want a native library in your language
with no extra process.
+
+## Try Casbin
+
+Test a model and policy in the [online editor](/docs/online-editor), or follow
the tutorial for [Go](/docs/go-rbac-tutorial),
[Node.js](/docs/nodejs-rbac-tutorial), [Python](/docs/python-rbac-tutorial), or
[Java](/docs/java-rbac-tutorial).
+
+See also the [comparison overview](/docs/comparison).
diff --git a/docs/CompareOpenFGA.mdx b/docs/CompareOpenFGA.mdx
new file mode 100644
index 000000000..cb7186e43
--- /dev/null
+++ b/docs/CompareOpenFGA.mdx
@@ -0,0 +1,73 @@
+---
+id: casbin-vs-openfga
+title: Casbin vs. OpenFGA
+description: Apache Casbin and OpenFGA compared - embedded library versus
Zanzibar-style service, RBAC and ReBAC modeling, storage, performance
characteristics, and when to choose each.
+keywords: [casbin vs openfga, openfga alternatives, openfga vs casbin,
zanzibar alternative, rebac library, fine-grained authorization]
+authors: [hsluoyz]
+---
+
+OpenFGA and Casbin both answer the question "may this user do this to that
object?". They are built very differently. OpenFGA is a server modeled on
Google's Zanzibar paper. Casbin is a library that runs inside your application.
+
+## Summary
+
+| | Casbin | OpenFGA |
+|---|---|---|
+| Form | Library in your process | Separate server, called over HTTP or gRPC |
+| Core idea | PERM model: request, policy, effect, matchers | Relationship
tuples and an authorization model |
+| Access control styles | ACL, RBAC, RBAC with domains, ABAC, ReBAC, and more
| ReBAC; roles and attributes are expressed through relations and conditions |
+| Rule storage | Your own database through an adapter | OpenFGA's datastore
(PostgreSQL, MySQL, or SQLite) |
+| A check costs | A function call | A network round trip, plus graph
resolution on the server |
+| Languages | Native implementations in Go, Java, Node.js, Python, .NET, PHP,
Rust, C++, and more | SDKs for several languages that call the server |
+| Listing what a user can access | `GetImplicitPermissionsForUser`,
`BatchEnforce`, and related APIs | `ListObjects` and `ListUsers` APIs |
+| Governance | Apache Software Foundation (Incubating) | Cloud Native
Computing Foundation |
+
+## Library versus service
+
+With OpenFGA you run and operate another service: a server, its database,
upgrades, and monitoring. In return, every application in your company can ask
the same service the same question, whatever language it is written in, and the
relationship data lives in one place.
+
+With Casbin there is nothing extra to deploy. You add a dependency, load a
model and a policy, and call `Enforce()`. Each check runs in memory, so there
is no network latency and no new failure mode. If you later want a central
service, you can put Casbin behind an API yourself or use [Casbin
Server](/docs/service).
+
+## Modeling
+
+OpenFGA starts from relationships. You declare types and relations such as "a
document has an owner, an editor, and a viewer, and editors of the parent
folder are editors of the document", then write tuples such as `user:alice` is
`editor` of `document:budget`. This is a natural fit for sharing features,
nested folders, and organization hierarchies.
+
+Casbin starts from a model that you configure. The usual starting point is
RBAC:
+
+```ini
+[request_definition]
+r = sub, obj, act
+
+[policy_definition]
+p = sub, obj, act
+
+[role_definition]
+g = _, _
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
+```
+
+From there the same engine covers tenants ([RBAC with
domains](/docs/rbac-with-domains)), resource hierarchies and per-resource roles
([ReBAC](/docs/rebac)), and attribute rules such as `r.sub == r.obj.Owner`
([ABAC](/docs/abac)). Matchers can also call [built-in or custom
functions](/docs/function) for path and IP matching.
+
+If most of your rules are "role X may do Y on resource type Z", Casbin's model
is shorter and easier to read. If most of your rules are "users related to this
object through a chain of relationships may access it", OpenFGA's model
expresses that more directly.
+
+## Scale
+
+Casbin loads policy into memory. That is what makes checks fast, and it means
the policy set of one application should fit in memory. For large or
multi-tenant data sets, load only what a process needs with [policy subset
loading](/docs/policy-subset-loading), and keep instances in step with
[watchers](/docs/watchers).
+
+OpenFGA keeps tuples in its database and resolves each check on the server. It
is designed for very large numbers of tuples shared by many applications, at
the cost of a network call and server-side evaluation for each check.
+
+## When to choose which
+
+Choose **OpenFGA** if authorization is a shared platform service in your
organization, your rules are mostly relationship chains, or your tuple count is
too large to hold in application memory.
+
+Choose **Casbin** if you want authorization inside your application without
new infrastructure, your rules are role, tenant, or attribute based, you want
policies in your existing database, or you need native support in a language
OpenFGA only reaches through a remote call.
+
+## Try Casbin
+
+Paste the model above into the [online editor](/docs/online-editor), or follow
the tutorial for [Go](/docs/go-rbac-tutorial),
[Node.js](/docs/nodejs-rbac-tutorial), [Python](/docs/python-rbac-tutorial), or
[Java](/docs/java-rbac-tutorial).
+
+See also the [comparison overview](/docs/comparison).
diff --git a/docs/Comparison.mdx b/docs/Comparison.mdx
new file mode 100644
index 000000000..5b913db2d
--- /dev/null
+++ b/docs/Comparison.mdx
@@ -0,0 +1,49 @@
+---
+id: comparison
+title: Casbin vs. Alternatives
+description: How Apache Casbin compares with OpenFGA, Open Policy Agent (OPA),
CASL, and Cedar - library or service, policy language, access control models,
language support, and when to choose each.
+keywords: [casbin alternatives, authorization library comparison, casbin vs
openfga, casbin vs opa, casbin vs casl, casbin vs cedar, open source
authorization]
+authors: [hsluoyz]
+---
+
+Open-source authorization tools take noticeably different approaches. This
page places Casbin among the most common alternatives so you can tell quickly
which family of tool fits your problem. Each linked page goes deeper and says
when the other tool is the better choice.
+
+| Compared with | In one sentence |
+|---|---|
+| [OpenFGA](/docs/casbin-vs-openfga) | A Zanzibar-style relationship service
you deploy; Casbin is a library inside your application. |
+| [Open Policy Agent](/docs/casbin-vs-opa) | A general-purpose policy engine
with the Rego language; Casbin focuses on application access control with
policies stored as data. |
+| [CASL](/docs/casbin-vs-casl) | A JavaScript library where abilities are
defined in code; Casbin keeps rules outside the code and runs in many
languages. |
+| [Cedar](/docs/casbin-vs-cedar) | A policy language with schema validation
and analysis; Casbin offers a configurable model and a large adapter ecosystem.
|
+
+## At a glance
+
+The table describes each project as of 2026. Check each project's own
documentation before making a decision.
+
+| | Casbin | OpenFGA | OPA | CASL | Cedar |
+|---|---|---|---|---|---|
+| Form | Library embedded in your application | Server with HTTP and gRPC APIs
| Daemon or sidecar, also a Go library and WebAssembly | Library | Library
(Rust core, with bindings) |
+| How rules are written | A model file (`.conf`) plus policy rules as rows |
An authorization model in a DSL plus relationship tuples | Rego programs plus
JSON data | JavaScript or TypeScript code | Cedar policy language plus a schema
|
+| Main access control style | ACL, RBAC, ABAC, ReBAC, and others, chosen in
the model | ReBAC | Anything expressible in Rego | Attribute and ownership
checks on subjects | RBAC and ABAC |
+| Native implementations | Go, Java, Node.js, Python, .NET, PHP, Rust, C++,
and more | SDKs call the server | Go; other languages call the server or use
WebAssembly | JavaScript and TypeScript | Rust, with Java, Go, and WebAssembly
options |
+| Where rules are stored | Your database, through adapters | The OpenFGA
server's database | Bundles loaded into memory | In application code | Wherever
you store policy text |
+| Changing rules at runtime | Management API, effective immediately | Write
API | Publish a new bundle or push data | Redeploy, or build abilities from
your own data | Replace policies |
+| Governance | Apache Software Foundation (Incubating) | Cloud Native
Computing Foundation | Cloud Native Computing Foundation | Community, MIT
license | Open source from AWS |
+
+## What is specific to Casbin
+
+- **It runs inside your process.** An `Enforce()` call is a function call, not
a network request. There is nothing extra to deploy, and typical checks take
microseconds; see [Benchmarks](/docs/benchmark).
+- **The model is configuration.** The same engine does ACL,
[RBAC](/docs/rbac), [RBAC with domains](/docs/rbac-with-domains),
[ABAC](/docs/abac), [ReBAC](/docs/rebac), and more. Moving from one to another
means editing the model file, not rewriting code. See [Supported
models](/docs/supported-models).
+- **Policies are rows you can manage.** Rules live in your existing database
through an [adapter](/docs/adapters), and an administrator can add or remove
them at runtime through the [Management API](/docs/management-api) or an [admin
portal](/docs/admin-portal).
+- **The same model works in every language.** A model and policy written for a
Go service can be enforced unchanged in Java, Node.js, Python, .NET, PHP, Rust,
or C++.
+
+## When another tool fits better
+
+- You need Google-Zanzibar-style relationship checks across billions of
objects, served centrally to many applications: look at
[OpenFGA](/docs/casbin-vs-openfga).
+- You need one policy language for Kubernetes admission, infrastructure
configuration, and services: look at [OPA](/docs/casbin-vs-opa).
+- Your application is JavaScript end to end and you mainly want type-safe
permission checks in UI components: look at [CASL](/docs/casbin-vs-casl).
+- You need static validation and formal analysis of policies: look at
[Cedar](/docs/casbin-vs-cedar).
+
+## Try Casbin
+
+- Test a model and policy in the browser with the [online
editor](/docs/online-editor).
+- Follow a tutorial for [Go](/docs/go-rbac-tutorial),
[Node.js](/docs/nodejs-rbac-tutorial), [Python](/docs/python-rbac-tutorial), or
[Java](/docs/java-rbac-tutorial).
diff --git a/docs/Overview.mdx b/docs/Overview.mdx
index d0ab0fc70..774211a23 100644
--- a/docs/Overview.mdx
+++ b/docs/Overview.mdx
@@ -68,3 +68,8 @@ Casbin also supports more complex scenarios through [roles
(RBAC)](/docs/rbac),
2. **User or role list management** — maintaining the list of users or roles.
Most applications already manage users, roles, and credentials. Casbin focuses
only on authorization and does not store or verify passwords. In RBAC mode, it
does maintain user–role associations.
+
+## Next steps
+
+- Build a working example in [Go](/docs/go-rbac-tutorial),
[Node.js](/docs/nodejs-rbac-tutorial), [Python](/docs/python-rbac-tutorial), or
[Java](/docs/java-rbac-tutorial).
+- See how Casbin compares with [OpenFGA, OPA, CASL, and
Cedar](/docs/comparison).
diff --git a/docs/TutorialGoRBAC.mdx b/docs/TutorialGoRBAC.mdx
new file mode 100644
index 000000000..2c65c13ae
--- /dev/null
+++ b/docs/TutorialGoRBAC.mdx
@@ -0,0 +1,153 @@
+---
+id: go-rbac-tutorial
+title: RBAC in Go with Gin
+description: Step-by-step Go RBAC tutorial - add role-based access control to
a Gin REST API with Casbin, including the model, policy, middleware, and
runtime role management.
+keywords: [golang rbac, go rbac, gin rbac, gin casbin, golang authorization
library, role-based access control go]
+authors: [hsluoyz]
+---
+
+This tutorial adds role-based access control (RBAC) to a REST API written in
Go with [Gin](https://github.com/gin-gonic/gin). At the end, three roles
(`admin`, `editor`, `viewer`) are allowed different HTTP methods on different
routes, and the rules live in a policy file that you can change without
touching the handlers.
+
+The same model and policy work in every Casbin implementation, so you can
reuse them in the [Node.js](/docs/nodejs-rbac-tutorial),
[Python](/docs/python-rbac-tutorial), and [Java](/docs/java-rbac-tutorial)
versions of this tutorial.
+
+## 1. Install
+
+```bash
+go mod init example.com/rbac
+go get github.com/casbin/casbin/v3 github.com/gin-gonic/gin
+```
+
+## 2. Write the model
+
+The model says what a request looks like and how it is matched against the
policy. Save this as `model.conf`:
+
+```ini
+[request_definition]
+r = sub, obj, act
+
+[policy_definition]
+p = sub, obj, act
+
+[role_definition]
+g = _, _
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)
+```
+
+- `r = sub, obj, act`: each request is a user, a URL path, and an HTTP method.
+- `g = _, _`: users can be assigned to roles.
+- `g(r.sub, p.sub)`: the request's user must have the role named in the policy
rule.
+- `keyMatch2` matches paths such as `/api/articles/:id`, and `regexMatch` lets
one rule list several methods. See [Functions](/docs/function) for the other
built-in matchers.
+
+## 3. Write the policy
+
+Save this as `policy.csv`:
+
+```csv
+p, admin, /api/*, (GET)|(POST)|(PUT)|(DELETE)
+p, editor, /api/articles, (GET)|(POST)
+p, editor, /api/articles/:id, (GET)|(PUT)
+p, viewer, /api/articles, GET
+p, viewer, /api/articles/:id, GET
+
+g, alice, admin
+g, bob, editor
+g, carol, viewer
+```
+
+Lines starting with `p` grant a role access to a path and a set of methods.
Lines starting with `g` assign users to roles.
+
+## 4. Add the middleware
+
+The middleware calls `Enforce` with the user, the path, and the method, and
rejects the request with `403 Forbidden` when Casbin says no.
+
+```go title="main.go"
+package main
+
+import (
+ "log"
+ "net/http"
+
+ "github.com/casbin/casbin/v3"
+ "github.com/gin-gonic/gin"
+)
+
+// Authorize asks Casbin whether the current user may call this route.
+func Authorize(e *casbin.Enforcer) gin.HandlerFunc {
+ return func(c *gin.Context) {
+ // Replace this with the user from your session or JWT.
+ user := c.GetHeader("X-User")
+
+ allowed, err := e.Enforce(user, c.Request.URL.Path, c.Request.Method)
+ if err != nil {
+ c.AbortWithStatusJSON(http.StatusInternalServerError,
gin.H{"error": err.Error()})
+ return
+ }
+ if !allowed {
+ c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error":
"forbidden"})
+ return
+ }
+ c.Next()
+ }
+}
+
+func main() {
+ e, err := casbin.NewEnforcer("model.conf", "policy.csv")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ r := gin.Default()
+ api := r.Group("/api", Authorize(e))
+ api.GET("/articles", func(c *gin.Context) { c.JSON(http.StatusOK,
gin.H{"articles": []string{}}) })
+ api.POST("/articles", func(c *gin.Context) { c.JSON(http.StatusCreated,
gin.H{"created": true}) })
+ api.PUT("/articles/:id", func(c *gin.Context) { c.JSON(http.StatusOK,
gin.H{"updated": c.Param("id")}) })
+ api.DELETE("/articles/:id", func(c *gin.Context) { c.JSON(http.StatusOK,
gin.H{"deleted": c.Param("id")}) })
+
+ log.Fatal(r.Run(":8080"))
+}
+```
+
+:::note
+The `X-User` header keeps the example short. Casbin handles authorization
only; in a real application the user name comes from your authentication layer,
such as a verified JWT or a session.
+:::
+
+## 5. Try it
+
+```bash
+go run .
+```
+
+```bash
+curl -i -X POST -H "X-User: bob" http://localhost:8080/api/articles #
201, editors can create
+curl -i -X DELETE -H "X-User: bob" http://localhost:8080/api/articles/1 #
403, editors cannot delete
+curl -i -X DELETE -H "X-User: alice" http://localhost:8080/api/articles/1 #
200, admins can
+curl -i -X POST -H "X-User: carol" http://localhost:8080/api/articles #
403, viewers are read-only
+```
+
+## 6. Change roles at runtime
+
+Roles and permissions are data, so you can change them while the server is
running:
+
+```go
+e.AddRoleForUser("dave", "editor") // dave becomes an editor
+e.DeleteRoleForUser("bob", "editor") // bob is no longer an editor
+e.AddPolicy("editor", "/api/comments", "POST") // editors may now post comments
+
+roles, _ := e.GetRolesForUser("alice") // [admin]
+```
+
+The full list is in the [RBAC API](/docs/rbac-api) and the [Management
API](/docs/management-api).
+
+## Next steps
+
+- **Store the policy in a database.** Replace `policy.csv` with an
[adapter](/docs/adapters) for MySQL, PostgreSQL, MongoDB, Redis, and others.
Policy changes made through the API are then saved automatically.
+- **Run more than one instance.** Use a [watcher](/docs/watchers) so every
instance reloads the policy when one of them changes it.
+- **Multi-tenant applications.** [RBAC with domains](/docs/rbac-with-domains)
gives a user different roles in different tenants.
+- **Rules based on attributes.** Combine roles with [ABAC](/docs/abac), for
example "editors may update only their own articles".
+- **Ready-made middleware.** The [middleware list](/docs/middlewares) has
plugins for Gin, Echo, Fiber, Chi, and other frameworks.
+- **Experiment in the browser.** Paste the model and policy into the [online
editor](/docs/online-editor) to test requests without running any code.
diff --git a/docs/TutorialJavaRBAC.mdx b/docs/TutorialJavaRBAC.mdx
new file mode 100644
index 000000000..cb00fa35e
--- /dev/null
+++ b/docs/TutorialJavaRBAC.mdx
@@ -0,0 +1,209 @@
+---
+id: java-rbac-tutorial
+title: RBAC in Java with Spring Boot
+description: Step-by-step Java RBAC tutorial - add role-based access control
to a Spring Boot REST API with jCasbin, including the model, policy,
interceptor, and runtime role management.
+keywords: [spring boot rbac, java rbac, jcasbin, spring boot authorization,
role-based access control java]
+authors: [hsluoyz]
+---
+
+This tutorial adds role-based access control (RBAC) to a REST API built with
[Spring Boot](https://spring.io/projects/spring-boot) and jCasbin. At the end,
three roles (`admin`, `editor`, `viewer`) are allowed different HTTP methods on
different routes, and the rules live in a policy file that you can change
without touching the controllers.
+
+The same model and policy work in every Casbin implementation, so you can
reuse them in the [Go](/docs/go-rbac-tutorial),
[Node.js](/docs/nodejs-rbac-tutorial), and [Python](/docs/python-rbac-tutorial)
versions of this tutorial.
+
+## 1. Add the dependency
+
+In a Spring Boot 3 project that already has `spring-boot-starter-web`, add
jCasbin to `pom.xml`. Use the latest version from [Maven
Central](https://mvnrepository.com/artifact/org.casbin/jcasbin).
+
+```xml
+<dependency>
+ <groupId>org.casbin</groupId>
+ <artifactId>jcasbin</artifactId>
+ <version>1.81.0</version>
+</dependency>
+```
+
+## 2. Write the model
+
+The model says what a request looks like and how it is matched against the
policy. Save this as `model.conf` in the directory you start the application
from:
+
+```ini
+[request_definition]
+r = sub, obj, act
+
+[policy_definition]
+p = sub, obj, act
+
+[role_definition]
+g = _, _
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)
+```
+
+- `r = sub, obj, act`: each request is a user, a URL path, and an HTTP method.
+- `g = _, _`: users can be assigned to roles.
+- `g(r.sub, p.sub)`: the request's user must have the role named in the policy
rule.
+- `keyMatch2` matches paths such as `/api/articles/:id`, and `regexMatch` lets
one rule list several methods. See [Functions](/docs/function) for the other
built-in matchers.
+
+## 3. Write the policy
+
+Save this as `policy.csv` next to `model.conf`:
+
+```csv
+p, admin, /api/*, (GET)|(POST)|(PUT)|(DELETE)
+p, editor, /api/articles, (GET)|(POST)
+p, editor, /api/articles/:id, (GET)|(PUT)
+p, viewer, /api/articles, GET
+p, viewer, /api/articles/:id, GET
+
+g, alice, admin
+g, bob, editor
+g, carol, viewer
+```
+
+Lines starting with `p` grant a role access to a path and a set of methods.
Lines starting with `g` assign users to roles.
+
+## 4. Add the interceptor
+
+The interceptor calls `enforce` with the user, the path, and the method, and
responds with `403 Forbidden` when Casbin says no.
+
+```java title="AuthorizationInterceptor.java"
+package com.example.rbac;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.casbin.jcasbin.main.Enforcer;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+public class AuthorizationInterceptor implements HandlerInterceptor {
+
+ private final Enforcer enforcer;
+
+ public AuthorizationInterceptor(Enforcer enforcer) {
+ this.enforcer = enforcer;
+ }
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse
response, Object handler) throws Exception {
+ // Replace this with the user from your session or JWT.
+ String user = request.getHeader("X-User");
+ if (user == null) {
+ user = "";
+ }
+
+ if (enforcer.enforce(user, request.getRequestURI(),
request.getMethod())) {
+ return true;
+ }
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, "forbidden");
+ return false;
+ }
+}
+```
+
+Create the `Enforcer` as a bean and register the interceptor for the API
routes:
+
+```java title="CasbinConfig.java"
+package com.example.rbac;
+
+import org.casbin.jcasbin.main.Enforcer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class CasbinConfig implements WebMvcConfigurer {
+
+ @Bean
+ public Enforcer enforcer() {
+ return new Enforcer("model.conf", "policy.csv");
+ }
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(new
AuthorizationInterceptor(enforcer())).addPathPatterns("/api/**");
+ }
+}
+```
+
+A controller to protect:
+
+```java title="ArticleController.java"
+package com.example.rbac;
+
+import java.util.List;
+import java.util.Map;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/api/articles")
+public class ArticleController {
+
+ @GetMapping
+ public Map<String, Object> list() {
+ return Map.of("articles", List.of());
+ }
+
+ @PostMapping
+ @ResponseStatus(HttpStatus.CREATED)
+ public Map<String, Object> create() {
+ return Map.of("created", true);
+ }
+
+ @PutMapping("/{id}")
+ public Map<String, Object> update(@PathVariable long id) {
+ return Map.of("updated", id);
+ }
+
+ @DeleteMapping("/{id}")
+ public Map<String, Object> delete(@PathVariable long id) {
+ return Map.of("deleted", id);
+ }
+}
+```
+
+:::note
+`new Enforcer("model.conf", "policy.csv")` reads the two files from the
working directory. Files packaged inside the application JAR under
`src/main/resources` cannot be opened as file paths; keep the files outside the
JAR, or load the policy from a database with an [adapter](/docs/adapters).
+
+The `X-User` header keeps the example short. Casbin handles authorization
only; in a real application the user name comes from your authentication layer,
such as Spring Security, a verified JWT, or a session.
+:::
+
+## 5. Try it
+
+```bash
+mvn spring-boot:run
+```
+
+```bash
+curl -i -X POST -H "X-User: bob" http://localhost:8080/api/articles #
201, editors can create
+curl -i -X DELETE -H "X-User: bob" http://localhost:8080/api/articles/1 #
403, editors cannot delete
+curl -i -X DELETE -H "X-User: alice" http://localhost:8080/api/articles/1 #
200, admins can
+curl -i -X POST -H "X-User: carol" http://localhost:8080/api/articles #
403, viewers are read-only
+```
+
+## 6. Change roles at runtime
+
+Roles and permissions are data, so you can change them while the server is
running. Inject the `Enforcer` bean wherever you manage users:
+
+```java
+enforcer.addRoleForUser("dave", "editor"); // dave becomes an editor
+enforcer.deleteRoleForUser("bob", "editor"); // bob is no longer an
editor
+enforcer.addPolicy("editor", "/api/comments", "POST"); // editors may now post
comments
+
+List<String> roles = enforcer.getRolesForUser("alice"); // [admin]
+```
+
+The full list is in the [RBAC API](/docs/rbac-api) and the [Management
API](/docs/management-api).
+
+## Next steps
+
+- **Store the policy in a database.** Replace `policy.csv` with an
[adapter](/docs/adapters) for JDBC, MyBatis, Hibernate, MongoDB, and others.
Policy changes made through the API are then saved automatically.
+- **Use the Spring Boot starter.**
[casbin-spring-boot-starter](https://github.com/jcasbin/casbin-spring-boot-starter)
creates the `Enforcer` and a JDBC adapter from `application.yml`.
+- **Run more than one instance.** Use a [watcher](/docs/watchers) so every
instance reloads the policy when one of them changes it.
+- **Multi-tenant applications.** [RBAC with domains](/docs/rbac-with-domains)
gives a user different roles in different tenants.
+- **Rules based on attributes.** Combine roles with [ABAC](/docs/abac), for
example "editors may update only their own articles".
+- **Other frameworks.** The [middleware list](/docs/middlewares) has plugins
for Spring Boot, Apache Shiro, JFinal, Nutz, and other frameworks.
diff --git a/docs/TutorialNodejsRBAC.mdx b/docs/TutorialNodejsRBAC.mdx
new file mode 100644
index 000000000..1cd3f2577
--- /dev/null
+++ b/docs/TutorialNodejsRBAC.mdx
@@ -0,0 +1,147 @@
+---
+id: nodejs-rbac-tutorial
+title: RBAC in Node.js with Express
+description: Step-by-step Node.js RBAC tutorial - add role-based access
control to an Express REST API with Casbin, including the model, policy,
middleware, and runtime role management.
+keywords: [nodejs rbac, express rbac, node casbin, express authorization
middleware, role-based access control nodejs]
+authors: [hsluoyz]
+---
+
+This tutorial adds role-based access control (RBAC) to a REST API built with
[Express](https://expressjs.com). At the end, three roles (`admin`, `editor`,
`viewer`) are allowed different HTTP methods on different routes, and the rules
live in a policy file that you can change without touching the route handlers.
+
+The same model and policy work in every Casbin implementation, so you can
reuse them in the [Go](/docs/go-rbac-tutorial),
[Python](/docs/python-rbac-tutorial), and [Java](/docs/java-rbac-tutorial)
versions of this tutorial.
+
+## 1. Install
+
+```bash
+npm init -y
+npm install express casbin
+```
+
+## 2. Write the model
+
+The model says what a request looks like and how it is matched against the
policy. Save this as `model.conf`:
+
+```ini
+[request_definition]
+r = sub, obj, act
+
+[policy_definition]
+p = sub, obj, act
+
+[role_definition]
+g = _, _
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)
+```
+
+- `r = sub, obj, act`: each request is a user, a URL path, and an HTTP method.
+- `g = _, _`: users can be assigned to roles.
+- `g(r.sub, p.sub)`: the request's user must have the role named in the policy
rule.
+- `keyMatch2` matches paths such as `/api/articles/:id`, and `regexMatch` lets
one rule list several methods. See [Functions](/docs/function) for the other
built-in matchers.
+
+## 3. Write the policy
+
+Save this as `policy.csv`:
+
+```csv
+p, admin, /api/*, (GET)|(POST)|(PUT)|(DELETE)
+p, editor, /api/articles, (GET)|(POST)
+p, editor, /api/articles/:id, (GET)|(PUT)
+p, viewer, /api/articles, GET
+p, viewer, /api/articles/:id, GET
+
+g, alice, admin
+g, bob, editor
+g, carol, viewer
+```
+
+Lines starting with `p` grant a role access to a path and a set of methods.
Lines starting with `g` assign users to roles.
+
+## 4. Add the middleware
+
+The middleware calls `enforce` with the user, the path, and the method, and
responds with `403 Forbidden` when Casbin says no.
+
+```js title="server.js"
+const express = require("express");
+const { newEnforcer } = require("casbin");
+
+// authorize asks Casbin whether the current user may call this route.
+function authorize(enforcer) {
+ return async (req, res, next) => {
+ // Replace this with the user from your session or JWT.
+ const user = req.get("X-User") || "";
+
+ try {
+ const allowed = await enforcer.enforce(user, req.baseUrl + req.path,
req.method);
+ if (!allowed) {
+ return res.status(403).json({ error: "forbidden" });
+ }
+ next();
+ } catch (err) {
+ next(err);
+ }
+ };
+}
+
+async function main() {
+ const enforcer = await newEnforcer("model.conf", "policy.csv");
+
+ const app = express();
+ const api = express.Router();
+ api.get("/articles", (req, res) => res.json({ articles: [] }));
+ api.post("/articles", (req, res) => res.status(201).json({ created: true }));
+ api.put("/articles/:id", (req, res) => res.json({ updated: req.params.id }));
+ api.delete("/articles/:id", (req, res) => res.json({ deleted: req.params.id
}));
+
+ app.use("/api", authorize(enforcer), api);
+ app.listen(8080, () => console.log("listening on :8080"));
+}
+
+main();
+```
+
+:::note
+Inside a mounted router, `req.path` does not include the mount point.
`req.baseUrl + req.path` gives the full path (`/api/articles`) that the policy
refers to.
+
+The `X-User` header keeps the example short. Casbin handles authorization
only; in a real application the user name comes from your authentication layer,
such as a verified JWT or a session.
+:::
+
+## 5. Try it
+
+```bash
+node server.js
+```
+
+```bash
+curl -i -X POST -H "X-User: bob" http://localhost:8080/api/articles #
201, editors can create
+curl -i -X DELETE -H "X-User: bob" http://localhost:8080/api/articles/1 #
403, editors cannot delete
+curl -i -X DELETE -H "X-User: alice" http://localhost:8080/api/articles/1 #
200, admins can
+curl -i -X POST -H "X-User: carol" http://localhost:8080/api/articles #
403, viewers are read-only
+```
+
+## 6. Change roles at runtime
+
+Roles and permissions are data, so you can change them while the server is
running:
+
+```js
+await enforcer.addRoleForUser("dave", "editor"); // dave becomes an
editor
+await enforcer.deleteRoleForUser("bob", "editor"); // bob is no
longer an editor
+await enforcer.addPolicy("editor", "/api/comments", "POST"); // editors may
now post comments
+
+const roles = await enforcer.getRolesForUser("alice"); // ["admin"]
+```
+
+The full list is in the [RBAC API](/docs/rbac-api) and the [Management
API](/docs/management-api).
+
+## Next steps
+
+- **Store the policy in a database.** Replace `policy.csv` with an
[adapter](/docs/adapters) for Sequelize, TypeORM, Prisma, Mongoose, and others.
Policy changes made through the API are then saved automatically.
+- **Run more than one instance.** Use a [watcher](/docs/watchers) so every
instance reloads the policy when one of them changes it.
+- **Show or hide UI elements.** [Frontend usage](/docs/frontend) explains how
to send a user's permissions to the browser with `casbin.js`.
+- **Multi-tenant applications.** [RBAC with domains](/docs/rbac-with-domains)
gives a user different roles in different tenants.
+- **Rules based on attributes.** Combine roles with [ABAC](/docs/abac), for
example "editors may update only their own articles".
+- **Ready-made middleware.** The [middleware list](/docs/middlewares) has
plugins for Express, Koa, NestJS, Fastify, and other frameworks.
diff --git a/docs/TutorialPythonRBAC.mdx b/docs/TutorialPythonRBAC.mdx
new file mode 100644
index 000000000..1c82f94bf
--- /dev/null
+++ b/docs/TutorialPythonRBAC.mdx
@@ -0,0 +1,144 @@
+---
+id: python-rbac-tutorial
+title: RBAC in Python with FastAPI
+description: Step-by-step Python RBAC tutorial - add role-based access control
to a FastAPI REST API with PyCasbin, including the model, policy, dependency,
and runtime role management.
+keywords: [python rbac, fastapi rbac, pycasbin, python rbac library, fastapi
authorization, role-based access control python]
+authors: [hsluoyz]
+---
+
+This tutorial adds role-based access control (RBAC) to a REST API built with
[FastAPI](https://fastapi.tiangolo.com) and PyCasbin. At the end, three roles
(`admin`, `editor`, `viewer`) are allowed different HTTP methods on different
routes, and the rules live in a policy file that you can change without
touching the route functions.
+
+The same model and policy work in every Casbin implementation, so you can
reuse them in the [Go](/docs/go-rbac-tutorial),
[Node.js](/docs/nodejs-rbac-tutorial), and [Java](/docs/java-rbac-tutorial)
versions of this tutorial.
+
+## 1. Install
+
+```bash
+pip install casbin fastapi uvicorn
+```
+
+## 2. Write the model
+
+The model says what a request looks like and how it is matched against the
policy. Save this as `model.conf`:
+
+```ini
+[request_definition]
+r = sub, obj, act
+
+[policy_definition]
+p = sub, obj, act
+
+[role_definition]
+g = _, _
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)
+```
+
+- `r = sub, obj, act`: each request is a user, a URL path, and an HTTP method.
+- `g = _, _`: users can be assigned to roles.
+- `g(r.sub, p.sub)`: the request's user must have the role named in the policy
rule.
+- `keyMatch2` matches paths such as `/api/articles/:id`, and `regexMatch` lets
one rule list several methods. See [Functions](/docs/function) for the other
built-in matchers.
+
+## 3. Write the policy
+
+Save this as `policy.csv`:
+
+```csv
+p, admin, /api/*, (GET)|(POST)|(PUT)|(DELETE)
+p, editor, /api/articles, (GET)|(POST)
+p, editor, /api/articles/:id, (GET)|(PUT)
+p, viewer, /api/articles, GET
+p, viewer, /api/articles/:id, GET
+
+g, alice, admin
+g, bob, editor
+g, carol, viewer
+```
+
+Lines starting with `p` grant a role access to a path and a set of methods.
Lines starting with `g` assign users to roles.
+
+## 4. Add the dependency
+
+A FastAPI dependency calls `enforce` with the user, the path, and the method,
and raises `403 Forbidden` when Casbin says no. Registering it on the
application applies it to every route.
+
+```python title="main.py"
+import casbin
+from fastapi import Depends, FastAPI, HTTPException, Request
+
+enforcer = casbin.Enforcer("model.conf", "policy.csv")
+
+
+def authorize(request: Request) -> None:
+ """Ask Casbin whether the current user may call this route."""
+ # Replace this with the user from your session or JWT.
+ user = request.headers.get("X-User", "")
+
+ if not enforcer.enforce(user, request.url.path, request.method):
+ raise HTTPException(status_code=403, detail="forbidden")
+
+
+app = FastAPI(dependencies=[Depends(authorize)])
+
+
[email protected]("/api/articles")
+def list_articles():
+ return {"articles": []}
+
+
[email protected]("/api/articles", status_code=201)
+def create_article():
+ return {"created": True}
+
+
[email protected]("/api/articles/{article_id}")
+def update_article(article_id: int):
+ return {"updated": article_id}
+
+
[email protected]("/api/articles/{article_id}")
+def delete_article(article_id: int):
+ return {"deleted": article_id}
+```
+
+:::note
+The `X-User` header keeps the example short. Casbin handles authorization
only; in a real application the user name comes from your authentication layer,
such as a verified JWT or a session.
+:::
+
+## 5. Try it
+
+```bash
+uvicorn main:app --port 8080
+```
+
+```bash
+curl -i -X POST -H "X-User: bob" http://localhost:8080/api/articles #
201, editors can create
+curl -i -X DELETE -H "X-User: bob" http://localhost:8080/api/articles/1 #
403, editors cannot delete
+curl -i -X DELETE -H "X-User: alice" http://localhost:8080/api/articles/1 #
200, admins can
+curl -i -X POST -H "X-User: carol" http://localhost:8080/api/articles #
403, viewers are read-only
+```
+
+## 6. Change roles at runtime
+
+Roles and permissions are data, so you can change them while the server is
running:
+
+```python
+enforcer.add_role_for_user("dave", "editor") # dave becomes an
editor
+enforcer.delete_role_for_user("bob", "editor") # bob is no longer an
editor
+enforcer.add_policy("editor", "/api/comments", "POST") # editors may now post
comments
+
+enforcer.get_roles_for_user("alice") # ['admin']
+```
+
+The full list is in the [RBAC API](/docs/rbac-api) and the [Management
API](/docs/management-api).
+
+## Next steps
+
+- **Store the policy in a database.** Replace `policy.csv` with an
[adapter](/docs/adapters) for SQLAlchemy, Django ORM, MongoDB, Redis, and
others. Policy changes made through the API are then saved automatically.
+- **Run more than one worker.** Use a [watcher](/docs/watchers) so every
process reloads the policy when one of them changes it.
+- **Multi-tenant applications.** [RBAC with domains](/docs/rbac-with-domains)
gives a user different roles in different tenants.
+- **Rules based on attributes.** Combine roles with [ABAC](/docs/abac), for
example "editors may update only their own articles".
+- **Ready-made middleware.** The [middleware list](/docs/middlewares) has
plugins for FastAPI, Django, Flask, and other frameworks.
+- **Experiment in the browser.** Paste the model and policy into the [online
editor](/docs/online-editor) to test requests without running any code.
diff --git a/docusaurus.config.js b/docusaurus.config.js
index 4ae86ff36..325782803 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -307,6 +307,17 @@ module.exports = {
],
],
plugins: [
+ [
+ require.resolve("./src/plugins/llms-txt"),
+ {
+ title: "Apache Casbin",
+ summary: "Apache Casbin (Incubating) is an open-source authorization
library that supports access control models like ACL, RBAC, ABAC, ReBAC, PBAC,
BLP, Biba and more. A model file (PERM: policy, effect, request, matchers) and
policy rules decide every request, and the same model works in Go, Java,
Node.js, Python, .NET, PHP, Rust, C++ and other languages.",
+ links: [
+ {title: "Source code", url: "https://github.com/apache/casbin",
description: "Go implementation and examples (Apache-2.0)"},
+ {title: "Online editor", url: "https://editor.casbin.org",
description: "Write and test a model and policy in the browser"},
+ ],
+ },
+ ],
],
i18n: {
defaultLocale: "en",
diff --git a/sidebars.js b/sidebars.js
index 7278a1668..a6d0a066e 100644
--- a/sidebars.js
+++ b/sidebars.js
@@ -28,6 +28,17 @@ module.exports = {
"tutorials",
],
},
+ {
+ type: "category",
+ label: "Guides",
+ link: {type: "generated-index"},
+ items: [
+ "go-rbac-tutorial",
+ "nodejs-rbac-tutorial",
+ "python-rbac-tutorial",
+ "java-rbac-tutorial",
+ ],
+ },
{
type: "category",
label: "Access Control Models",
@@ -155,6 +166,17 @@ module.exports = {
"ide-plugins",
],
},
+ {
+ type: "category",
+ label: "Comparisons",
+ link: {type: "doc", id: "comparison"},
+ items: [
+ "casbin-vs-openfga",
+ "casbin-vs-opa",
+ "casbin-vs-casl",
+ "casbin-vs-cedar",
+ ],
+ },
{
type: "category",
label: "About & More",
diff --git a/src/plugins/llms-txt.js b/src/plugins/llms-txt.js
new file mode 100644
index 000000000..5a11c2d03
--- /dev/null
+++ b/src/plugins/llms-txt.js
@@ -0,0 +1,78 @@
+const fs = require("fs");
+const path = require("path");
+
+// Writes /llms.txt (https://llmstxt.org) from the docs sidebar at build time,
+// so AI assistants get an up-to-date index of the documentation.
+module.exports = function llmsTxtPlugin(context, options) {
+ let version = null;
+
+ function collectDocIds(item, ids) {
+ if (typeof item === "string") {
+ ids.push(item);
+ } else if (item.type === "doc" || item.type === "ref") {
+ ids.push(item.id);
+ } else if (item.type === "category") {
+ if (item.link && item.link.type === "doc") {
+ ids.push(item.link.id);
+ }
+ (item.items || []).forEach((child) => collectDocIds(child, ids));
+ }
+ return ids;
+ }
+
+ return {
+ name: "llms-txt",
+
+ async allContentLoaded({allContent}) {
+ const docsContent = allContent["docusaurus-plugin-content-docs"];
+ version = docsContent && docsContent.default ?
docsContent.default.loadedVersions[0] : null;
+ },
+
+ async postBuild({outDir, siteConfig}) {
+ if (!version) {
+ return;
+ }
+
+ const docsById = new Map(version.docs.map((doc) => [doc.id, doc]));
+ const siteUrl = siteConfig.url.replace(/\/$/, "");
+ const formatDoc = (id) => {
+ const doc = docsById.get(id);
+ if (!doc) {
+ return null;
+ }
+ const description = (doc.description || "").replace(/\s+/g, "
").trim();
+ const permalink = siteConfig.trailingSlash &&
!doc.permalink.endsWith("/") ? `${doc.permalink}/` : doc.permalink;
+ return `- [${doc.title}](${siteUrl}${permalink})${description ? `:
${description}` : ""}`;
+ };
+
+ const sections = [];
+ const ungrouped = [];
+ const seen = new Set();
+ Object.values(version.sidebars).forEach((sidebar) => {
+ sidebar.forEach((item) => {
+ const ids = collectDocIds(item, []).filter((id) => !seen.has(id));
+ ids.forEach((id) => seen.add(id));
+ const lines = ids.map(formatDoc).filter(Boolean);
+ if (item.type === "category") {
+ if (lines.length > 0) {
+ sections.push(`## ${item.label}\n\n${lines.join("\n")}`);
+ }
+ } else {
+ ungrouped.push(...lines);
+ }
+ });
+ });
+ if (ungrouped.length > 0) {
+ sections.push(`## More\n\n${ungrouped.join("\n")}`);
+ }
+
+ const links = (options.links || []).map((link) => `-
[${link.title}](${link.url})${link.description ? `: ${link.description}` :
""}`);
+ if (links.length > 0) {
+ sections.push(`## Optional\n\n${links.join("\n")}`);
+ }
+
+ const content = `# ${options.title}\n\n>
${options.summary}\n\n${sections.join("\n\n")}\n`;
+ fs.writeFileSync(path.join(outDir, "llms.txt"), content);
+ },
+ };
+};
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]