diff --git a/api/api_v1/api.py b/api/api_v1/api.py index 04674b9..335edcc 100644 --- a/api/api_v1/api.py +++ b/api/api_v1/api.py @@ -12,6 +12,7 @@ from .endpoints import xquery from .endpoints import data_auth from .endpoints import event_mana from .endpoints import test +from .authz import authz api_router = APIRouter() api_router.include_router(test.router, tags=["test"], prefix='/test') @@ -31,3 +32,5 @@ api_router.include_router(event_mana.router, tags=["数据管理"], prefix='/dat api_router.include_router(query.router, tags=["ck"], prefix='/ck') api_router.include_router(xquery.router, tags=["xck"], prefix='/ck') + +api_router.include_router(authz.router, tags=["api接口管理"], prefix='/authz') diff --git a/api/api_v1/authz/__init__.py b/api/api_v1/authz/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api_v1/authz/authz.py b/api/api_v1/authz/authz.py new file mode 100644 index 0000000..cf228f7 --- /dev/null +++ b/api/api_v1/authz/authz.py @@ -0,0 +1,181 @@ +from typing import Any + +from fastapi import APIRouter, Depends, Request +from motor.motor_asyncio import AsyncIOMotorDatabase + +import crud +import schemas +from api import deps +from db import get_database +from db.ckdb import CKDrive, get_ck_db +from db.redisdb import RedisDrive, get_redis_pool +from models.behavior_analysis import BehaviorAnalysis +from utils import casbin_enforcer + +router = APIRouter() + + +@router.post("/add_role_domain") +async def add_role_domain( + request: Request, + data_in: schemas.AddRoleForUserInDomain, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user)): + """ + 在域内为用户添加角色 + """ + + # username role dom + is_exists_role = await crud.role.check(db, _id=data_in.role_id, game=data_in.game) + if not is_exists_role: + return schemas.Msg(code='-1', msg='角色不存在') + casbin_enforcer.add_role_for_user_in_domain(user=data_in.username, + role=data_in.role_id, + domain=data_in.game) + return schemas.Msg(code='-1', msg='添加成功') + + +@router.post("/del_role_user_domain") +async def del_role_domain( + request: Request, + data_in: schemas.DeleteRolesForUserInDomain, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user)): + """ + 删除用户角色域 + """ + + # username role dom + + res = casbin_enforcer.delete_roles_for_user_in_domain(user=data_in.username, + role=data_in.role_id, + domain=data_in.game) + return schemas.Msg(code=0, msg='ok', data=res) + + +@router.post("/add_policy") +async def add_policy( + request: Request, + data_id: schemas.AddPolicy, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user)): + """ + 向当前策略添加授权规则 + """ + + res = casbin_enforcer.add_policy(data_id.role_id, data_id.game, data_id.path, data_id.act) + return schemas.Msg(code=0, msg='ok', data=res) + + +@router.post("/del_policy") +async def remove_policy( + request: Request, + data_id: schemas.DelPolicy, + current_user: schemas.UserDB = Depends(deps.get_current_user)): + """ + 向当前策略添加授权规则 + """ + + res = casbin_enforcer.remove_policy(data_id.role_id, data_id.game, data_id.path, data_id.act) + return schemas.Msg(code=0, msg='ok', data=res) + + +@router.get("/api_list") +async def api_list( + request: Request, + game: str, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user)): + """ + + GetPermissionsForUserInDomain + 已经添加的api + 标记 已添加的权限 + """ + res = await crud.api_list.all_api(db, game) + return schemas.Msg(code=0, msg='ok', data=res) + + +@router.post("/add_api") +async def add_api( + request: Request, + data_in: schemas.AddApi, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user) +) -> schemas.Msg: + """ + 添加api + """ + res = await crud.api_list.add_api(db, data_in) + return schemas.Msg(code=0, msg='ok', data=res.matched_count) + + +@router.post("/del_api") +async def del_api( + request: Request, + data_in: schemas.DelApi, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user)) -> schemas.Msg: + """ + 删除api + """ + res = await crud.api_list.del_api(db, data_in) + return schemas.Msg(code=0, msg='ok', data=res.deleted_count) + + +@router.post("/edit_api") +async def edit_api( + request: Request, + data_in: schemas.EditApi, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user)) -> schemas.Msg: + """ + 编辑api + """ + res = await crud.api_list.edit_api(db, data_in) + return schemas.Msg(code=0, msg='ok', data=res.deleted_count) + + +@router.get("/domain") +async def domain_list( + request: Request, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user) +) -> schemas.Msg: + """ + 可选择域 游戏代号 + """ + # roel dom path * + res = await crud.project.all_game(db) + return schemas.Msg(code=0, msg='ok', data=res) + + +@router.post("/add_roles") +async def add_roles( + request: Request, + data_in: schemas.AddRole, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user) +) -> schemas.Msg: + """ + 新建角色 + """ + try: + res = await crud.role.add_role(db, data_in) + return schemas.Msg(code=0, msg='ok', data=res.matched_count) + except Exception as e: + return schemas.Msg(code=-1, msg='添加失败', data=str(e)) + + +@router.get("/roles") +async def roles( + request: Request, + game: str, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user) +) -> schemas.Msg: + """ + 域内所有角色 + """ + res = await crud.role.dom_roles(db, game) + return schemas.Msg(code=0, msg='ok', data=res) diff --git a/api/api_v1/endpoints/dashboard.py b/api/api_v1/endpoints/dashboard.py index b479057..aa00597 100644 --- a/api/api_v1/endpoints/dashboard.py +++ b/api/api_v1/endpoints/dashboard.py @@ -143,6 +143,18 @@ async def del_report( return schemas.Msg(code=0, msg='ok', data='ok') +@router.post("/edit") +async def edit( + game: str, + data_in: schemas.EditDashboard, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user) +): + """编辑看板名""" + await crud.dashboard.update_one(db, {'_id': data_in.dashboard_id}, {'$set': {'name': data_in.new_name}}) + return schemas.Msg(code=0, msg='ok', data='ok') + + @router.post("/") async def dashboards(request: Request, game: str, diff --git a/crud/__init__.py b/crud/__init__.py index 98a83d7..4d59f41 100644 --- a/crud/__init__.py +++ b/crud/__init__.py @@ -9,3 +9,5 @@ from .crud_data_auth import data_auth from .crud_data_attr import data_attr from .crud_api_log import api_log from .crud_event_mana import event_mana +from .crud_api_list import api_list +from .crud_role import role diff --git a/crud/base.py b/crud/base.py index 310db8a..a82d647 100644 --- a/crud/base.py +++ b/crud/base.py @@ -1,6 +1,7 @@ from typing import Union from bson import ObjectId +from motor.motor_asyncio import AsyncIOMotorDatabase class CRUDBase: @@ -38,6 +39,9 @@ class CRUDBase: async def delete(self, db, filter, collation=None, hint=None, session=None): return await db[self.coll_name].delete_many(filter, collation, hint, session) + async def delete_id(self, db, *args): + return await db[self.coll_name].delete_one({'_id': {'$in': list(args)}}) + async def update_one(self, db, filter, update, upsert=False): res = await db[self.coll_name].update_one(filter, update, upsert) return res @@ -47,3 +51,6 @@ class CRUDBase: async def distinct(self, db, key, filter=None): return await db[self.coll_name].distinct(key, filter) + + # async def _create_index(self, db: AsyncIOMotorDatabase, *args, **kwargs): + # return await db[self.coll_name].create_index(*args, **kwargs) diff --git a/crud/crud_api_list.py b/crud/crud_api_list.py new file mode 100644 index 0000000..760ac8c --- /dev/null +++ b/crud/crud_api_list.py @@ -0,0 +1,38 @@ +from motor.motor_asyncio import AsyncIOMotorDatabase + +import schemas +from crud.base import CRUDBase + +__all__ = 'api_list', + + +class CRUDApiList(CRUDBase): + async def add_api(self, db: AsyncIOMotorDatabase, data_in: schemas.AddApi): + where = {'path': data_in.path} + data = {'$set': schemas.AddApiDB(**data_in.dict()).dict(by_alias=True)} + + return await self.update_one(db, where, data, upsert=True) + + async def edit_api(self, db: AsyncIOMotorDatabase, data_in: schemas.EditApi): + where = {'_id': data_in.id} + data = {'$set': data_in.dict()} + + return await self.update_one(db, where, data) + + async def all_api(self, db: AsyncIOMotorDatabase, game): + where = {'game': game} + + return await self.find_many(db, where) + + + + + + async def del_api(self, db: AsyncIOMotorDatabase, data_in: schemas.DelApi): + return await self.delete_id(db, *data_in.ids) + + async def create_index(self, db: AsyncIOMotorDatabase): + await db[self.coll_name].create_index('path', unique=True) + + +api_list = CRUDApiList('api_list') diff --git a/crud/crud_project.py b/crud/crud_project.py index c119ef8..0a35f9f 100644 --- a/crud/crud_project.py +++ b/crud/crud_project.py @@ -5,16 +5,21 @@ from schemas import * __all__ = 'project', +from utils import get_uid + class CRUDProject(CRUDBase): async def create(self, db: AsyncIOMotorDatabase, obj_in: ProjectCreate, current_user): db_obj = ProjectDB( **obj_in.dict(), user_id=current_user.id, members=[current_user.username], - _id=uuid.uuid1().hex + _id=get_uid() ) return await db[self.coll_name].insert_one(db_obj.dict(by_alias=True)) + async def all_game(self, db: AsyncIOMotorDatabase): + return await self.find_many(db, {}, {'game': True, 'name': True, '_id': False}) + async def read_project(self, db: AsyncIOMotorDatabase, username: str, **kwargs): return await self.read_have(db, username, **kwargs) diff --git a/crud/crud_role.py b/crud/crud_role.py new file mode 100644 index 0000000..15e924e --- /dev/null +++ b/crud/crud_role.py @@ -0,0 +1,37 @@ +from motor.motor_asyncio import AsyncIOMotorDatabase + +import schemas +from crud.base import CRUDBase + +__all__ = 'role', + + +class CRUDApiList(CRUDBase): + async def add_role(self, db: AsyncIOMotorDatabase, data_in: schemas.AddRole): + where = {'name': data_in.name, 'game': data_in.game} + data = {'$set': schemas.AddRoleDB(**data_in.dict()).dict(by_alias=True)} + + return await self.update_one(db, where, data, upsert=True) + + async def edit_role(self, db: AsyncIOMotorDatabase, data_in: schemas.EditRole): + where = {'_id': data_in.id} + data = {'$set': data_in.dict()} + + return await self.update_one(db, where, data) + + async def check(self, db, **kwargs): + res = await self.find_one(db, kwargs) + return True if res else False + + async def dom_roles(self, db: AsyncIOMotorDatabase, game: str): + where = {'game': game} + return await self.find_many(db, where) + + async def del_role(self, db: AsyncIOMotorDatabase, data_in: schemas.DelRole): + return await self.delete_id(db, *data_in.ids) + + async def create_index(self, db: AsyncIOMotorDatabase): + await db[self.coll_name].create_index([('game', 1), ('name', 1)], unique=True) + + +role = CRUDApiList('role') diff --git a/init_db.py b/init_db.py index 7660f67..2ba612b 100644 --- a/init_db.py +++ b/init_db.py @@ -52,6 +52,13 @@ async def event_mana(): await crud.event_mana.create_index(db) +async def api_list_index(): + await crud.api_list.create_index(db) + +async def role_index(): + await crud.role.create_index(db) + + async def authority_init(): await crud.authority.create_index(db) await crud.authority.create(db, 'p', '*', '*', '/docs', '*') @@ -62,15 +69,17 @@ async def authority_init(): async def main(): - await create_superuser() - await project_index() - await folder_index() - await space_index() - await dashboard_index() - await report_index() - await authority_init() - await data_attr_index() - await event_mana() + # await create_superuser() + # await project_index() + # await folder_index() + # await space_index() + # await dashboard_index() + # await report_index() + # await authority_init() + # await data_attr_index() + # await event_mana() + await api_list_index() + await role_index() loop = asyncio.get_event_loop() diff --git a/initial_data.py b/initial_data.py deleted file mode 100644 index 996f856..0000000 --- a/initial_data.py +++ /dev/null @@ -1,22 +0,0 @@ -import logging - -from db.init_db import init_db -from db.mongodb_utils import SessionLocal - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - - -def init() -> None: - db = SessionLocal() - init_db(db) - - -def main() -> None: - logger.info("Creating initial data") - init() - logger.info("Initial data created") - - -if __name__ == "__main__": - main() diff --git a/schemas/__init__.py b/schemas/__init__.py index 918d2c7..fbbf477 100644 --- a/schemas/__init__.py +++ b/schemas/__init__.py @@ -12,4 +12,6 @@ from .data_attr import * from .sql import * from .api_log import * from .event_mana import * -from .xquery import * \ No newline at end of file +from .xquery import * +from .api_list import * +from .role import * \ No newline at end of file diff --git a/schemas/api_list.py b/schemas/api_list.py new file mode 100644 index 0000000..8811a11 --- /dev/null +++ b/schemas/api_list.py @@ -0,0 +1,33 @@ +from typing import Any, List, Union + +from pydantic import BaseModel, Field + +from schemas import DBBase +from typing import Optional + + +class ApiBase(BaseModel): + path: str = None + name: str = None + desc: str = None + + + + + +class AddApi(ApiBase): + path: str + name: str + desc: str + + +class AddApiDB(DBBase, AddApi): + pass + + +class DelApi(BaseModel): + ids: List[str] = Field(..., description='要删除的id') + + +class EditApi(ApiBase): + id: str = Field(..., description='要编辑的id') diff --git a/schemas/authotity.py b/schemas/authotity.py index fce4b2f..b4614e9 100644 --- a/schemas/authotity.py +++ b/schemas/authotity.py @@ -4,6 +4,31 @@ from typing import List from pydantic import BaseModel +class AddRoleForUserInDomain(BaseModel): + username: str + role_id: str + game: str + + +class DeleteRolesForUserInDomain(BaseModel): + username: str + role_id: str + game: str + + +class Policy(BaseModel): + role_id: str + game: str + path: str + act: str = '*' + +class AddPolicy(Policy): + pass + + +class DelPolicy(Policy): + pass + class Ptype(str, Enum): p = 'p' g = 'g' diff --git a/schemas/base.py b/schemas/base.py index 477f335..40c5718 100644 --- a/schemas/base.py +++ b/schemas/base.py @@ -2,7 +2,8 @@ import uuid from typing import Optional, Union from bson import ObjectId -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, validator +from utils import * # # mongodb _id 类型 @@ -21,3 +22,7 @@ from pydantic import BaseModel, Field class DBBase(BaseModel): id: str = Field(None, alias='_id') + + @validator('id', pre=True, always=True) + def default_id(cls, v): + return v or get_uid() diff --git a/schemas/dashboard.py b/schemas/dashboard.py index 3467af0..9d06714 100644 --- a/schemas/dashboard.py +++ b/schemas/dashboard.py @@ -34,6 +34,7 @@ class Report(BaseModel): graph_type: str model: str graph_size: str + sort: int class EditShowReport(BaseModel): @@ -46,6 +47,11 @@ class Category(str, Enum): space = 'space' +class EditDashboard(BaseModel): + dashboard_id: str + new_name: str + + class DashboardMove(BaseModel): source_ids: List[str] dest_pid: str diff --git a/schemas/role.py b/schemas/role.py new file mode 100644 index 0000000..5bf168c --- /dev/null +++ b/schemas/role.py @@ -0,0 +1,29 @@ +from typing import List + +from pydantic import Field +from pydantic.main import BaseModel + +from schemas import DBBase + + +class RoleBase(BaseModel): + game: str = None + name: str = None + desc: str = None + + +class AddRole(BaseModel): + game: str + name: str + desc: str + + +class AddRoleDB(DBBase, AddRole): + pass + +class DelRole(BaseModel): + ids: List[str] = Field(..., description='要删除的id') + + +class EditRole(RoleBase): + id: str = Field(..., description='要编辑的id') \ No newline at end of file diff --git a/utils/__init__.py b/utils/__init__.py index d61d95d..392b3c8 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1,3 +1,4 @@ from .adapter import * from . import casbin +from .func import * diff --git a/utils/adapter.py b/utils/adapter.py index cca7324..6143202 100644 --- a/utils/adapter.py +++ b/utils/adapter.py @@ -7,7 +7,6 @@ from core.config import settings __all__ = 'casbin_adapter', 'casbin_enforcer', 'casbin_model' - class CasbinRule: ''' CasbinRule model @@ -47,6 +46,24 @@ class Adapter(persist.Adapter): db = client[dbname] self._collection = db[collection] + @staticmethod + def format_policy(ptype, args): + line = CasbinRule(ptype=ptype) + if len(args) > 0: + line.v0 = args[0] + if len(args) > 1: + line.v1 = args[1] + if len(args) > 2: + line.v2 = args[2] + if len(args) > 3: + line.v3 = args[3] + if len(args) > 4: + line.v4 = args[4] + if len(args) > 5: + line.v5 = args[5] + + return line + def load_policy(self, model): ''' implementing add Interface for casbin \n @@ -108,13 +125,15 @@ class Adapter(persist.Adapter): def remove_policy(self, sec, ptype, rule): """delete policy rules from mongodb""" - pass + line = self.format_policy(ptype, rule) + self._collection.delete_one(line.dict()) def remove_filtered_policy(self, sec, ptype, field_index, *field_values): """ delete policy rules for matching filters from mongodb """ - pass + line = self.format_policy(ptype, field_values) + self._collection.delete_one(line.dict()) casbin_adapter = Adapter(settings.DATABASE_URI, settings.MDB_DB) diff --git a/utils/async_adapter.py b/utils/async_adapter.py deleted file mode 100644 index 241d2bf..0000000 --- a/utils/async_adapter.py +++ /dev/null @@ -1,110 +0,0 @@ - -from .casbin import persist - - -class CasbinRule: - ''' - CasbinRule model - ''' - - def __init__(self, ptype = None, v0 = None, v1 = None, v2 = None, v3 = None, v4 = None, v5 = None): - self.ptype = ptype - self.v0 = v0 - self.v1 = v1 - self.v2 = v2 - self.v3 = v3 - self.v4 = v4 - self.v5 = v5 - - def dict(self): - d = {'ptype': self.ptype} - - for i, v in enumerate([self.v0, self.v1, self.v2, self.v3, self.v4, self.v5]): - if v is None: - break - d['v' + str(i)] = v - - return d - - def __str__(self): - return ', '.join(self.dict().values()) - - def __repr__(self): - return ''.format(str(self)) - -class Adapter(persist.Adapter): - """the interface for Casbin adapters.""" - - def __init__(self,db, collection="casbin_rule"): - self._collection = db[collection] - - async def load_policy(self, model): - ''' - implementing add Interface for casbin \n - load all policy rules from mongodb \n - ''' - - async for line in self._collection.find(): - if 'ptype' not in line: - continue - - rule = CasbinRule(line['ptype']) - if 'v0' in line: - rule.v0 = line['v0'] - if 'v1' in line: - rule.v1 = line['v1'] - if 'v2' in line: - rule.v2 = line['v2'] - if 'v3' in line: - rule.v3 = line['v3'] - if 'v4' in line: - rule.v4 = line['v4'] - if 'v5' in line: - rule.v5 = line['v5'] - - persist.load_policy_line(str(rule), model) - - async def _save_policy_line(self, ptype, rule): - line = CasbinRule(ptype=ptype) - if len(rule) > 0: - line.v0 = rule[0] - if len(rule) > 1: - line.v1 = rule[1] - if len(rule) > 2: - line.v2 = rule[2] - if len(rule) > 3: - line.v3 = rule[3] - if len(rule) > 4: - line.v4 = rule[4] - if len(rule) > 5: - line.v5 = rule[5] - await self._collection.insert_one(line.dict()) - - async def save_policy(self, model): - ''' - implementing add Interface for casbin \n - save the policy in mongodb \n - ''' - for sec in ["p", "g"]: - if sec not in model.model.keys(): - continue - for ptype, ast in model.model[sec].items(): - for rule in ast.policy: - await self._save_policy_line(ptype, rule) - return True - - async def add_policy(self, sec, ptype, rule): - """add policy rules to mongodb""" - await self._save_policy_line(ptype, rule) - - def remove_policy(self, sec, ptype, rule): - """delete policy rules from mongodb""" - pass - - def remove_filtered_policy(self, sec, ptype, field_index, *field_values): - """ - delete policy rules for matching filters from mongodb - """ - pass - - diff --git a/utils/func.py b/utils/func.py index 8214c58..2f9f747 100644 --- a/utils/func.py +++ b/utils/func.py @@ -1,5 +1,6 @@ +import random import time def get_uid(): - return hex(int(time.time() * 10 ** 7))[2:] + return hex(int(time.time() * 10 ** 7) + random.randint(0, 10000))[2:]