Skip to content

add rbac authorization #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
May 17, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a52c306
WIP: add rbac authorization
wu-clan May 14, 2023
81a09cb
Perform pre-commit fixes
wu-clan May 14, 2023
5fcf974
add rbac route whitelist
wu-clan May 14, 2023
84b7665
add init test data user role associations
wu-clan May 14, 2023
2abe569
Restore database table id naming to fix generic crud base
wu-clan May 14, 2023
f0e4b8c
Add database section value uniqueness settings
wu-clan May 14, 2023
2431d1e
Update the test directory to tests
wu-clan May 15, 2023
258f2a8
Update route_name file name to health_check
wu-clan May 15, 2023
6eff3fc
Split user auth and user action interfaces
wu-clan May 15, 2023
ffa715a
Merge branch 'master' into add-RBAC
wu-clan May 16, 2023
a28a018
Fix conflict between merge and current branch
wu-clan May 16, 2023
e665444
Add pymysql dependencies
wu-clan May 16, 2023
b90713d
Fix RBAC authentication method
wu-clan May 16, 2023
ffdb763
Add the select serialisation tool
wu-clan May 17, 2023
b87cba0
Fix missing return messages due to global exception handler slicing
wu-clan May 17, 2023
183efea
Update the user interface with associated relationships
wu-clan May 17, 2023
84b2b18
Add items to be completed
wu-clan May 17, 2023
19f0c1b
Perform pre-commit fixes
wu-clan May 17, 2023
e18ed53
Merge branch 'master' into add-RBAC
wu-clan May 17, 2023
38398c8
Add pre-made routers
wu-clan May 17, 2023
fdba1a6
Paging data return structure optimisation
wu-clan May 17, 2023
73f771b
Split user auth and user interface tests
wu-clan May 17, 2023
14ca07c
Fix user register test data structure error
wu-clan May 17, 2023
244c006
Fix duplicate named test classes
wu-clan May 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# FastAPI Best Architecture

This is a base project of the FastAPI framework.
This is a base project of the FastAPI framework, in production

It‘s purpose is to allow you to develop your project directly with it
as your base project
Expand Down Expand Up @@ -29,19 +29,24 @@ git clone https://github.com/wu-clan/fastapi_best_architecture.git
### 1:Tradition

1. Install dependencies

```shell
pip install -r requirements.txt
```

2. Create a database `fba`, choose utf8mb4 encode
3. Install and start Redis
4. create a `.env` file in the `backend/app/` directory

```shell
cd backend/app/

touch .env
```
5. Copy .env.example to .env and view `backend/app/core/conf.py`, update database configuration information

5. Copy `.env.example` to `.env` and view `backend/app/core/conf.py`, update database configuration information
6. Perform a database migration [alembic](https://alembic.sqlalchemy.org/en/latest/tutorial.html)

```shell
cd backend/app/

Expand All @@ -51,7 +56,8 @@ git clone https://github.com/wu-clan/fastapi_best_architecture.git
# Perform the migration
alembic upgrade head
```
7. Execute the backend/app/main.py file startup service

7. Execute the `backend/app/main.py` file startup service
8. Browser access: http://127.0.0.1:8000/v1/docs

---
Expand All @@ -63,6 +69,7 @@ git clone https://github.com/wu-clan/fastapi_best_architecture.git
```shell
docker-compose up -d --build
```

2. Wait for the command to finish automatically

3. Browser access: http://127.0.0.1:8000/v1/docs
Expand Down
90 changes: 90 additions & 0 deletions README_zh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# FastAPI 最佳架构

这是 FastAPI 框架的一个基础项目,在制作中

它的目的是让你直接用它作为你的基础项目来开发你的项目

支持 python3.10 及以上版本

## 技术栈

- [x] FastAPI
- [x] Pydantic
- [x] SQLAlchemy
- [x] Alembic
- [x] MySQL
- [x] Redis
- [x] APScheduler
- [x] Docker

## 克隆

```shell
git clone https://github.com/wu-clan/fastapi_best_architecture.git
```

## 使用:

### 1:传统

1. 安装依赖项
```shell
pip install -r requirements.txt
```

2. 创建一个数据库`fba`,选择 utf8mb4 编码
3. 安装并启动 Redis
4. 在`backend/app/`目录下创建一个`.env`文件
```shell
cd backend/app/
touch .env
```
5. 复制 `.env.example` 到 `.env` 并查看`backend/app/core/conf.py`,更新数据库配置信息
6. 进行数据库迁移[alembic](https://alembic.sqlalchemy.org/en/latest/tutorial.html)
```shell
cd backend/app/

# 生成迁移文件
alembic revision --autogenerate

# 执行迁移
alembic upgrade head
```
7. 执行 `backend/app/main.py` 文件启动服务
8. 浏览器访问:http://127.0.0.1:8000/v1/docs

---

### 2:Docker

1. 在 `docker-compose.yml` 文件所在的目录中运行一键启动命令

```shell
docker-compose up -d -build
```

2. 等待命令自动完成

3. 浏览器访问:http://127.0.0.1:8000/v1/docs

## 初始化测试数据

执行 `backend/app/init_test_data.py` 文件

## 测试

通过 pytest 进行测试

**提示**: 在测试开始前,请先执行初始化测试数据,同时,需要启动 fastapi 服务。

1. 首先,进入app目录

```shell
cd backend/app/
```

2. 执行测试命令

```shell
pytest -vs --disable-warnings
```
18 changes: 14 additions & 4 deletions backend/app/api/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,23 @@
from fastapi import APIRouter

from backend.app.api.v1.auth import router as auth_router
from backend.app.api.v1.user import router as user_router
from backend.app.api.v1.casbin import router as casbin_router
from backend.app.api.v1.dept import router as dept_router
from backend.app.api.v1.role import router as role_router
from backend.app.api.v1.menu import router as menu_router
from backend.app.api.v1.api import router as api_router
from backend.app.api.v1.task_demo import router as task_demo_router
from backend.app.api.v1.sys_config import router as sys_config_router
from backend.app.api.v1.config import router as config_router

v1 = APIRouter(prefix='/v1')

v1.include_router(auth_router)

v1.include_router(user_router, prefix='/users', tags=['用户管理'])
v1.include_router(casbin_router, prefix='/casbin', tags=['权限管理'])
v1.include_router(dept_router, prefix='/depts', tags=['部门管理'])
v1.include_router(role_router, prefix='/roles', tags=['角色管理'])
v1.include_router(menu_router, prefix='/menus', tags=['菜单管理'])
v1.include_router(api_router, prefix='/apis', tags=['API管理'])
v1.include_router(config_router, prefix='/configs', tags=['系统配置'])
v1.include_router(task_demo_router, prefix='/tasks', tags=['任务管理'])

v1.include_router(sys_config_router, prefix='/configs', tags=['系统配置'])
7 changes: 7 additions & 0 deletions backend/app/api/v1/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from fastapi import APIRouter

router = APIRouter()

# TODO: 添加 api 相关接口
6 changes: 3 additions & 3 deletions backend/app/api/v1/auth/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from fastapi import APIRouter
from backend.app.api.v1.auth.user import router as user_router
from backend.app.api.v1.auth.auth import router as auth_router

router = APIRouter(prefix='/auth', tags=['用户管理'])
router = APIRouter(prefix='/auth', tags=['认证'])

router.include_router(user_router, prefix='/users')
router.include_router(auth_router, prefix='/users')
32 changes: 32 additions & 0 deletions backend/app/api/v1/auth/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from fastapi import APIRouter, Depends
from fastapi.security import OAuth2PasswordRequestForm

from backend.app.common.jwt import DependsUser
from backend.app.common.response.response_schema import response_base
from backend.app.schemas.token import Token
from backend.app.schemas.user import Auth
from backend.app.services.user_service import UserService

router = APIRouter()


@router.post('/swagger_login', summary='swagger 表单登录', description='form 格式登录,仅用于 swagger 文档调试接口')
async def swagger_user_login(form_data: OAuth2PasswordRequestForm = Depends()) -> Token:
token, user = await UserService.swagger_login(form_data)
return Token(access_token=token, user=user)


@router.post('/login', summary='用户登录', description='json 格式登录, 仅支持在第三方api工具调试接口, 例如: postman')
async def user_login(obj: Auth):
token, user = await UserService.login(obj)
# TODO: token 存储
data = Token(access_token=token, user=user)
return response_base.response_200(data=data)


@router.post('/logout', summary='用户登出', dependencies=[DependsUser])
async def user_logout():
# TODO: 加入 token 黑名单
return response_base.response_200()
7 changes: 7 additions & 0 deletions backend/app/api/v1/casbin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from fastapi import APIRouter

router = APIRouter()

# TODO: 添加 casbin 相关接口
22 changes: 16 additions & 6 deletions backend/app/api/v1/sys_config.py → backend/app/api/v1/config.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from fastapi import APIRouter
from fastapi import APIRouter, Request
from fastapi.routing import APIRoute

from backend.app.api.jwt import DependsSuperUser
from backend.app.common.response.response_schema import ResponseModel
from backend.app.common.casbin_rbac import DependsRBAC
from backend.app.common.response.response_schema import response_base
from backend.app.core.conf import settings

router = APIRouter()


@router.get('', summary='获取系统配置', dependencies=[DependsSuperUser])
async def get_sys_config() -> ResponseModel:
return ResponseModel(
@router.get('', summary='获取系统配置', dependencies=[DependsRBAC])
async def get_sys_config():
return response_base.success(
data={
'title': settings.TITLE,
'version': settings.VERSION,
Expand Down Expand Up @@ -49,3 +50,12 @@ async def get_sys_config() -> ResponseModel:
'middleware_access': settings.MIDDLEWARE_ACCESS,
}
)


@router.get('/routers', summary='获取所有路由', dependencies=[DependsRBAC])
async def get_all_route(request: Request):
data = []
for route in request.app.routes:
if isinstance(route, APIRoute):
data.append({'path': route.path, 'name': route.name, 'summary': route.summary, 'methods': route.methods})
return response_base.success(data={'route_list': data})
7 changes: 7 additions & 0 deletions backend/app/api/v1/dept.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from fastapi import APIRouter

router = APIRouter()

# TODO: 添加 dept 相关接口
7 changes: 7 additions & 0 deletions backend/app/api/v1/menu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from fastapi import APIRouter

router = APIRouter()

# TODO: 添加 menu 相关接口
7 changes: 7 additions & 0 deletions backend/app/api/v1/role.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from fastapi import APIRouter

router = APIRouter()

# TODO: 添加 role 相关接口
38 changes: 14 additions & 24 deletions backend/app/api/v1/auth/user.py → backend/app/api/v1/user.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,18 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from fastapi import APIRouter, Depends
from fastapi.security import OAuth2PasswordRequestForm
from fastapi import APIRouter

from backend.app.api.jwt import CurrentUser, DependsUser, DependsSuperUser
from backend.app.api.service.user_service import UserService
from backend.app.common.pagination import Page
from backend.app.common.jwt import DependsUser, CurrentUser, DependsSuperUser
from backend.app.common.pagination import paging_data, PageDepends
from backend.app.common.response.response_schema import response_base
from backend.app.schemas.token import Token
from backend.app.schemas.user import CreateUser, GetUserInfo, ResetPassword, UpdateUser, Avatar, Auth
from backend.app.database.db_mysql import CurrentSession
from backend.app.schemas.user import CreateUser, GetUserInfo, ResetPassword, UpdateUser, Avatar
from backend.app.services.user_service import UserService
from backend.app.utils.serializers import select_to_json

router = APIRouter()


@router.post('/swagger_login', summary='swagger 表单登录', description='form 格式登录,仅用于 swagger 文档调试接口')
async def swagger_user_login(form_data: OAuth2PasswordRequestForm = Depends()) -> Token:
token, user = await UserService.swagger_login(form_data)
return Token(access_token=token, user=user)


@router.post('/login', summary='用户登录', description='json 格式登录, 仅支持在第三方api工具调试接口, 例如: postman')
async def user_login(obj: Auth):
token, user = await UserService.login(obj)
data = Token(access_token=token, user=user)
return response_base.response_200(data=data)


@router.post('/register', summary='用户注册')
async def user_register(obj: CreateUser):
await UserService.register(obj)
Expand All @@ -41,7 +28,8 @@ async def password_reset(obj: ResetPassword):
@router.get('/{username}', summary='查看用户信息', dependencies=[DependsUser])
async def userinfo(username: str):
current_user = await UserService.get_userinfo(username)
return response_base.response_200(data=current_user, exclude={'password'})
data = GetUserInfo(**select_to_json(current_user))
return response_base.response_200(data=data, exclude={'password'})


@router.put('/{username}', summary='更新用户信息')
Expand All @@ -60,9 +48,11 @@ async def update_avatar(username: str, avatar: Avatar, current_user: CurrentUser
return response_base.fail()


@router.get('', summary='获取所有用户', dependencies=[DependsUser])
async def get_all_users() -> Page[GetUserInfo]:
return await UserService.get_user_list()
@router.get('', summary='获取所有用户', dependencies=[DependsUser, PageDepends])
async def get_all_users(db: CurrentSession):
user_list = await UserService.get_user_list()
page_data = await paging_data(db, user_list, GetUserInfo)
return response_base.response_200(data=page_data)


@router.post('/{pk}/super', summary='修改用户超级权限', dependencies=[DependsSuperUser])
Expand Down
Loading