diff --git a/.gitignore b/.gitignore index 8c3d35f..6a7b364 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -<<<<<<< HEAD .idea .vscode @@ -11,7 +10,6 @@ __pycache__ logs/ *.swap -======= # ---> Python # Byte-compiled / optimized / DLL files __pycache__/ @@ -151,5 +149,3 @@ dmypy.json # Cython debug symbols cython_debug/ - ->>>>>>> 47d098bb512e8791ebaa211917636f2e7f13a6f6 diff --git a/settings/dev.py b/settings/dev.py index 4689602..a5c2f66 100644 --- a/settings/dev.py +++ b/settings/dev.py @@ -3,11 +3,18 @@ import os.path # 项目目录 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +SECRET_KEY = 'cf036e50524a11ecb4a618c04d9eb245' + # mongodb 地址 MONGODB_SETTINGS = { 'host': 'mongodb://admin:111111@10.2.2.10:27017/ops_api?authSource=admin', } +# API static token 允许请求的静态token +X_TOKEN_SET = ( + "51ab21fa323a11eca1af000c2993d583", # +) + LOGGING = { 'version': 1, 'disable_existing_loggers': True, diff --git a/src/common/permission.py b/src/common/permission.py new file mode 100644 index 0000000..2d640a5 --- /dev/null +++ b/src/common/permission.py @@ -0,0 +1,17 @@ +from functools import wraps + +from flask import request, current_app as app +from flask_restful import abort + + +def token_header_required(func): + """获取请求头携带的 token,简单的过滤请求""" + @wraps(func) + def wrap_func(*args, **kwargs): + token = request.headers.get("X-Token") + token_set = app.config.get("X_TOKEN_SET", []) + + if token not in token_set: + abort(403, msg="permission denied", code=1403) + return func(*args, **kwargs) + return wrap_func diff --git a/src/common/views.py b/src/common/views.py index bf7992f..7974831 100644 --- a/src/common/views.py +++ b/src/common/views.py @@ -3,6 +3,7 @@ from flask_restful import abort, Resource, marshal, fields, reqparse class ModelViewBase(Resource): + """Mongodb 模型视图相关方法的扩展集合""" # 模型 model = None # marshal 字段 @@ -12,6 +13,12 @@ class ModelViewBase(Resource): # 是否分页 paging = True + # method_decorators 可以用作权限校验,参考 `flask_restful.Resource` + # 参数是装饰器列表(未指定方法将会对所有请求方法生效) + # 也可以为指定请求设置: {"get": [wrap_func1, ..], "post": []} + # method_decorators = [token_header_required, ] + method_decorators = [] + def get_object(self, pk): try: return self.model.objects(id=pk).first_or_404(message="resource not found") @@ -74,6 +81,9 @@ class CreateMixin(ModelViewBase): def post(self): """创建对象""" + + assert self.request_parse is not None, "缺少校验规则" + # 解析参数 args = self.request_parse.parse_args() @@ -88,6 +98,24 @@ class CreateMixin(ModelViewBase): return marshal(obj, self.fields) +# TODO 模型校验 +# class CreateFormMixin(ModelViewBase): +# +# def pre_create(self, args): +# """创建前钩子,接收参数,可以对参数进行处理,最后保存此方法返回的数据""" +# return args +# +# def post(self): +# """创建对象""" +# self.form = self.form if self.form else model_form(self.model) +# print("===> json", request.json) +# form = self.form(request.json, meta={'csrf': False}) +# if not form.validate(): +# print(form.errors) +# return {"msg": "error"} +# return {"msg": "ok"} + + class RetrieveMixin(ModelViewBase): def get(self, pk): diff --git a/src/project/views.py b/src/project/views.py index e053a18..a16a2ba 100644 --- a/src/project/views.py +++ b/src/project/views.py @@ -7,6 +7,7 @@ from asset.models import Host from project import fields from project.models import Project, Channel, Server, Version from common.views import ListMixin, CreateMixin, ListCreateViewSet, DetailViewSet +from common.permission import token_header_required class ProjectViews(ListMixin, CreateMixin): @@ -45,6 +46,7 @@ class ServerSyncView(CreateMixin): model = Server fields = fields.ServerFields project: Project = None + method_decorators = [token_header_required] def __init__(self): # 参数定义,json来源