Skip to content

Commit 191c6f1

Browse files
committed
feat: add readme and demo
1 parent 56f7906 commit 191c6f1

File tree

2 files changed

+196
-1
lines changed

2 files changed

+196
-1
lines changed

README.md

Lines changed: 147 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,147 @@
1-
# fastapi-authz
1+
# fastapi-authz
2+
3+
fastapi-authz is an authorization middleware for [FastAPI](https://fastapi.tiangolo.com/), it's based
4+
on [PyCasbin](https://github.com/casbin/pycasbin).
5+
6+
## Installation
7+
8+
Clone this repo
9+
10+
```bash
11+
git clone https://github.com/pycasbin/fastapi-authz.git
12+
python setup.py install
13+
```
14+
15+
## Quickstart
16+
This middleware is designed to work with another middleware which implement `AuthenticationMiddleware` interface.
17+
```python
18+
import base64
19+
import binascii
20+
21+
import casbin
22+
23+
from fastapi import FastAPI
24+
from starlette.authentication import AuthenticationBackend, AuthenticationError, SimpleUser, AuthCredentials
25+
from starlette.middleware.authentication import AuthenticationMiddleware
26+
27+
from fastapi_authz import CasbinMiddleware
28+
29+
app = FastAPI()
30+
31+
32+
class BasicAuth(AuthenticationBackend):
33+
async def authenticate(self, request):
34+
if "Authorization" not in request.headers:
35+
return None
36+
37+
auth = request.headers["Authorization"]
38+
try:
39+
scheme, credentials = auth.split()
40+
decoded = base64.b64decode(credentials).decode("ascii")
41+
except (ValueError, UnicodeDecodeError, binascii.Error):
42+
raise AuthenticationError("Invalid basic auth credentials")
43+
44+
username, _, password = decoded.partition(":")
45+
return AuthCredentials(["authenticated"]), SimpleUser(username)
46+
47+
48+
enforcer = casbin.Enforcer('../examples/rbac_model.conf', '../examples/rbac_policy.csv')
49+
50+
app.add_middleware(CasbinMiddleware, enforcer=enforcer)
51+
app.add_middleware(AuthenticationMiddleware, backend=BasicAuth())
52+
53+
54+
@app.get('/')
55+
async def index():
56+
return "If you see this, you have been authenticated."
57+
58+
59+
@app.get('/dataset1/protected')
60+
async def auth_test():
61+
return "You must be alice to see this."
62+
```
63+
- anonymous request
64+
```bash
65+
curl -i http://127.0.0.1:8000/dataset1/protected
66+
```
67+
```bash
68+
HTTP/1.1 403 Forbidden
69+
date: Mon, 01 Mar 2021 09:00:08 GMT
70+
server: uvicorn
71+
content-length: 11
72+
content-type: application/json
73+
74+
"Forbidden"
75+
```
76+
77+
- authenticated request
78+
```bash
79+
curl -i -u alice:password http://127.0.0.1:8000/dataset1/protected
80+
```
81+
82+
```bash
83+
HTTP/1.1 200 OK
84+
date: Mon, 01 Mar 2021 09:04:54 GMT
85+
server: uvicorn
86+
content-length: 32
87+
content-type: application/json
88+
89+
"You must be alice to see this."
90+
```
91+
92+
It used the casbin config from `examples` folder, and you can find this demo in `demo` folder.
93+
94+
You can also view the unit tests to understand this middleware.
95+
96+
## Development
97+
98+
### Run unit tests
99+
100+
1. Fork/Clone repository
101+
2. Install flask-authz dependencies, and run `pytest`
102+
103+
```bash
104+
pip install -r dev_requirements.txt
105+
pip install -r requirements.txt
106+
pytest
107+
```
108+
109+
### Update requirements with pip-tools
110+
111+
```bash
112+
# update requirements.txt
113+
pip-compile --no-annotate --no-header --rebuild requirements.in
114+
# sync venv
115+
pip-sync
116+
```
117+
118+
### Manually Bump Version
119+
120+
```
121+
bumpversion major # major release
122+
or
123+
bumpversion minor # minor release
124+
or
125+
bumpversion patch # hotfix release
126+
```
127+
128+
## Documentation
129+
130+
The authorization determines a request based on ``{subject, object, action}``, which means what ``subject`` can perform
131+
what ``action`` on what ``object``. In this plugin, the meanings are:
132+
133+
1. ``subject``: the logged-in user name
134+
2. ``object``: the URL path for the web resource like `dataset1/item1`
135+
3. ``action``: HTTP method like GET, POST, PUT, DELETE, or the high-level actions you defined like "read-file", "
136+
write-blog" (currently no official support in this middleware)
137+
138+
For how to write authorization policy and other details, please refer
139+
to [the Casbin's documentation](https://casbin.org).
140+
141+
## Getting Help
142+
143+
- [Casbin](https://casbin.org)
144+
145+
## License
146+
147+
This project is under Apache 2.0 License. See the [LICENSE](LICENSE) file for the full license text.

demo/test.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import base64
2+
import binascii
3+
4+
import casbin
5+
import uvicorn
6+
7+
from fastapi import FastAPI
8+
from starlette.authentication import AuthenticationBackend, AuthenticationError, SimpleUser, AuthCredentials
9+
from starlette.middleware.authentication import AuthenticationMiddleware
10+
11+
from fastapi_authz import CasbinMiddleware
12+
13+
app = FastAPI()
14+
15+
16+
class BasicAuth(AuthenticationBackend):
17+
async def authenticate(self, request):
18+
if "Authorization" not in request.headers:
19+
return None
20+
21+
auth = request.headers["Authorization"]
22+
try:
23+
scheme, credentials = auth.split()
24+
decoded = base64.b64decode(credentials).decode("ascii")
25+
except (ValueError, UnicodeDecodeError, binascii.Error):
26+
raise AuthenticationError("Invalid basic auth credentials")
27+
28+
username, _, password = decoded.partition(":")
29+
return AuthCredentials(["authenticated"]), SimpleUser(username)
30+
31+
32+
enforcer = casbin.Enforcer('../examples/rbac_model.conf', '../examples/rbac_policy.csv')
33+
34+
app.add_middleware(CasbinMiddleware, enforcer=enforcer)
35+
app.add_middleware(AuthenticationMiddleware, backend=BasicAuth())
36+
37+
38+
@app.get('/')
39+
async def index():
40+
return "If you see this, you have been authenticated."
41+
42+
43+
@app.get('/dataset1/protected')
44+
async def auth_test():
45+
return "You must be alice to see this."
46+
47+
48+
if __name__ == '__main__':
49+
uvicorn.run('test:app', debug=True)

0 commit comments

Comments
 (0)