From 1cdef195ecb40a05294dff616c01d75943e04471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=9D=E5=B7=9D?= Date: Tue, 1 Nov 2022 14:02:58 +0800 Subject: [PATCH] changes --- AccessPolicies/__init__.py | 0 AccessPolicies/crud.py | 13 +++++++ AccessPolicies/models.py | 16 +++++++++ AccessPolicies/router_policy.py | 36 +++++++++++++++++++ AccessPolicies/schemas.py | 30 ++++++++++++++++ User/crud.py | 31 +++++++++++----- User/default.py | 17 +++++---- User/router_user.py | 35 ++++++++++++++---- User/router_user_admin.py | 34 +++++++++--------- Utils/AccessControl/AccessUtil.py | 40 ++++++++++----------- Utils/AccessControl/model.conf | 10 +++--- Utils/AccessControl/policy.csv | 8 ----- Utils/AccessControl/policy_index_store.csv | 0 Utils/Authentication/Config.py | 3 +- Utils/Authentication/TokenUtil.py | 41 +++++++++++++--------- Utils/DataBase/__init__.py | 0 Utils/UniqueCoder/__init__.py | 0 main.py | 2 +- 18 files changed, 226 insertions(+), 90 deletions(-) create mode 100644 AccessPolicies/__init__.py create mode 100644 AccessPolicies/crud.py create mode 100644 AccessPolicies/models.py create mode 100644 AccessPolicies/router_policy.py create mode 100644 AccessPolicies/schemas.py delete mode 100644 Utils/AccessControl/policy.csv create mode 100644 Utils/AccessControl/policy_index_store.csv create mode 100644 Utils/DataBase/__init__.py create mode 100644 Utils/UniqueCoder/__init__.py diff --git a/AccessPolicies/__init__.py b/AccessPolicies/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/AccessPolicies/crud.py b/AccessPolicies/crud.py new file mode 100644 index 0000000..507d44e --- /dev/null +++ b/AccessPolicies/crud.py @@ -0,0 +1,13 @@ +from sqlalchemy.orm import Session + +from AccessPolicies import schemas, models +from Utils.UniqueCoder.TimeSerialNumUtils import create_time_serial_num + + +def create_policy(db: Session, body: schemas.PolicyBase): + item = models.Policy(**body.dict()) + item.id = create_time_serial_num(prefix="p", suffix="") + db.add(item) + db.commit() + db.refresh(item) + return item diff --git a/AccessPolicies/models.py b/AccessPolicies/models.py new file mode 100644 index 0000000..4e63b33 --- /dev/null +++ b/AccessPolicies/models.py @@ -0,0 +1,16 @@ +from sqlalchemy import Column, String + +from Utils.DataBase.SqlAlchemyUtils import Base + + +class Policy(Base): + __tablename__ = "policy" + + id = Column(String(64), primary_key=True, index=True) + ptype = Column(String(4), comment="策略类型") + v0 = Column(String(255), comment="主体 sub") + v1 = Column(String(255), comment="对象 obj") + v2 = Column(String(255), comment="动作 act") + v3 = Column(String(255), nullable=True) + v4 = Column(String(255), nullable=True) + v5 = Column(String(255), nullable=True) diff --git a/AccessPolicies/router_policy.py b/AccessPolicies/router_policy.py new file mode 100644 index 0000000..d718576 --- /dev/null +++ b/AccessPolicies/router_policy.py @@ -0,0 +1,36 @@ +from fastapi import APIRouter, Depends +from sqlalchemy.orm import Session + +from AccessPolicies import models, schemas, crud +from Utils.AccessControl.AccessUtil import ac_admin +from Utils.DataBase.SqlAlchemyUtils import engine, get_db + +models.Base.metadata.create_all(bind=engine) + +router = APIRouter( + tags=["访问策略"], + prefix="/api/user/access_policy", + dependencies=[ + Depends(ac_admin) + ] +) + + +@router.post("/create", response_model=schemas.Policy, summary="新建访问策略") +def create(body: schemas.PolicyBase, db: Session = Depends(get_db)): + return crud.create_policy(db=db, body=body) + + +@router.post("/delete", summary="删除访问策略") +def delete(): + pass + + +@router.post("/edit", summary="编辑访问策略") +def delete(): + pass + + +@router.post("/view", summary="查看访问策略") +def delete(): + pass diff --git a/AccessPolicies/schemas.py b/AccessPolicies/schemas.py new file mode 100644 index 0000000..47f9276 --- /dev/null +++ b/AccessPolicies/schemas.py @@ -0,0 +1,30 @@ +from enum import Enum +from typing import Optional + +from pydantic import BaseModel + + +class PtypeEnum(Enum): + enum1 = "p" + enum2 = "g" + enum3 = "g2" + + +class PolicyBase(BaseModel): + ptype: PtypeEnum + v0: str + v1: str + v2: str + v3: Optional[str] = None + v4: Optional[str] = None + v5: Optional[str] = None + + class Config: + use_enum_values = True + + +class Policy(PolicyBase): + id: str + + class Config: + orm_mode = True diff --git a/User/crud.py b/User/crud.py index 338a249..f76cd47 100644 --- a/User/crud.py +++ b/User/crud.py @@ -3,16 +3,31 @@ from werkzeug.security import generate_password_hash from sqlalchemy.orm import Session +from Utils.EncryptProcess import EncyptUtil from Utils.UniqueCoder.TimeSerialNumUtils import create_time_serial_num from . import models, schemas def get_user_by_name(db: Session, name: str): - return db.query(models.User).filter(models.User.name == name).first() + return db.query(models.User).with_entities( + models.User.id, + models.User.name, + models.User.email, + models.User.role, + models.User.depart, + models.User.is_active + ).filter(models.User.name == name).first() -def get_user_by_id(db: Session, uid: int): - return db.query(models.User).filter(models.User.id == uid).first() +def get_user_by_id(db: Session, uid: str): + return db.query(models.User).with_entities( + models.User.id, + models.User.name, + models.User.email, + models.User.role, + models.User.depart, + models.User.is_active + ).filter(models.User.id == uid).first() def get_user_by_email(db: Session, email: str): @@ -31,7 +46,7 @@ def get_users(db: Session, body: schemas.UserSearch, skip, limit): def create_user(db: Session, body: schemas.UserCreate): - body.passwd = generate_password_hash(body.passwd) + body.passwd = generate_password_hash(EncyptUtil.decrypt_data(encrypt_msg=body.passwd)) item = models.User(**body.dict()) item.id = create_time_serial_num(prefix="UID", suffix="") db.add(item) @@ -40,19 +55,19 @@ def create_user(db: Session, body: schemas.UserCreate): return item -def delete_user(db: Session, _id: int): - db.query(models.User).filter_by(id=_id).delete() +def delete_user(db: Session, uid: str): + db.query(models.User).filter_by(id=uid).delete() db.commit() return True -def edit_user(db: Session, uid: int, body: schemas.UserEdit): +def edit_user(db: Session, uid: str, body: schemas.UserEdit): db.query(models.User).filter_by(id=uid).update(body.dict()) db.commit() return True -def reset_user_pwd(db: Session, uid: int, passwd: schemas.PasswdRegex): +def reset_user_pwd(db: Session, uid: str, passwd: schemas.PasswdRegex): passwd = generate_password_hash(passwd) db.query(models.User).filter_by(id=uid).update({"passwd": passwd}) db.commit() diff --git a/User/default.py b/User/default.py index 4e930e8..7f6fa24 100644 --- a/User/default.py +++ b/User/default.py @@ -2,6 +2,15 @@ from User import crud, schemas from Utils.DataBase.SqlAlchemyUtils import Session +ROOT_USER = { + "email": "wangsichuan@fecr.com.cn", + "passwd": "Fecr1988", + "name": "root", + "role": "admin", + "depart": "数字化部" +} + + def default_root(): db = Session() @@ -9,13 +18,7 @@ def default_root(): if not data: - root = { - "email": "wangsichuan@fecr.com.cn", - "passwd": "Fecr1988.", - "name": "root", - "role": "admin", - "depart": "数字化部" - } + root = ROOT_USER crud.create_user(db=db, body=schemas.UserCreate(**root)) diff --git a/User/router_user.py b/User/router_user.py index 5af6003..bcc953f 100644 --- a/User/router_user.py +++ b/User/router_user.py @@ -1,6 +1,6 @@ from datetime import timedelta -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, HTTPException, Header from sqlalchemy.orm import Session from werkzeug.security import check_password_hash @@ -37,15 +37,38 @@ def login(body: schemas.UserLogin, db: Session = Depends(get_db)): } access_token_expires = timedelta(minutes=Config.ACCESS_TOKEN_EXPIRE_MINUTES) - access_token = TokenUtil.create_token( - data=user, expires_delta=access_token_expires - ) + access_token = TokenUtil.create_token(data=user, expires_delta=access_token_expires) + + refresh_token_expires = timedelta(minutes=Config.REFRESH_TOKEN_EXPIRE_MINUTES) + refresh_token = TokenUtil.create_refresh_token(uid=data.id, expires_delta=refresh_token_expires) user.update({"token": access_token}) + user.update({"refresh_token": refresh_token}) return user @router.get("/detail", summary="用户信息") -def detail(): - pass +def detail(token: str = Header(...), db: Session = Depends(get_db)): + uid = TokenUtil.get_uid_from_token(token) + data = crud.get_user_by_id(db=db, uid=uid) + if data is None: + raise HTTPException(status_code=404, detail="User Not found") + return data + + +@router.post("/refresh_token", summary="更新token") +def to_refresh_token(refresh_token: str = Header(...), db: Session = Depends(get_db)): + decoded_token = TokenUtil.decode_token(refresh_token) + uid = decoded_token.get("uid") + + data = crud.get_user_by_id(db=db, uid=uid) + if not data: + return {"info": "User Not Existed"} + + user = {**data} + + access_token_expires = timedelta(minutes=Config.ACCESS_TOKEN_EXPIRE_MINUTES) + access_token = TokenUtil.create_token(data=user, expires_delta=access_token_expires) + + return {"token": access_token} diff --git a/User/router_user_admin.py b/User/router_user_admin.py index 1ca5bd4..3955cb9 100644 --- a/User/router_user_admin.py +++ b/User/router_user_admin.py @@ -4,7 +4,7 @@ from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from User import crud, models, schemas -from User.default import default_root +from User.default import default_root, ROOT_USER from Utils.DataBase.SqlAlchemyUtils import engine, get_db from Utils.AccessControl.AccessUtil import ac_admin @@ -29,14 +29,14 @@ def create_user(body: schemas.UserCreate, db: Session = Depends(get_db)): return {"info": "Success"} -@router.post("/delete", summary="删除用户") -def delete_user(uid: int, db: Session = Depends(get_db)): - crud.delete_user(db=db, _id=uid) +@router.post("/delete/{uid}", summary="删除用户") +def delete_user(uid: str, db: Session = Depends(get_db)): + crud.delete_user(db=db, uid=uid) return {"info": "Success"} -@router.post("/edit", summary="编辑用户") -def user_edit(uid: int, body: schemas.UserEdit, db: Session = Depends(get_db)): +@router.post("/edit/{uid}", summary="编辑用户") +def user_edit(uid: str, body: schemas.UserEdit, db: Session = Depends(get_db)): data = crud.get_user_by_id(db, uid) if not data: raise HTTPException(status_code=400, detail="User Not found") @@ -44,25 +44,25 @@ def user_edit(uid: int, body: schemas.UserEdit, db: Session = Depends(get_db)): return {"info": "Success"} -@router.post("/reset_pwd", summary="重置用户密码") -def user_reset_pwd(uid: int, db: Session = Depends(get_db)): +@router.post("/reset_pwd/{uid}", summary="重置用户密码") +def user_reset_pwd(uid: str, db: Session = Depends(get_db)): data = crud.get_user_by_id(db, uid) if not data: raise HTTPException(status_code=400, detail="User Not found") - passwd = "Fecr1988" + passwd = ROOT_USER.get("passwd") crud.reset_user_pwd(db=db, passwd=passwd, uid=uid) return {"info": "Success"} +@router.get("/detail/{uid}", summary="用户信息") +def detail(uid: str, db: Session = Depends(get_db)): + data = crud.get_user_by_id(db=db, uid=uid) + if data is None: + raise HTTPException(status_code=404, detail="User Not found") + return data + + @router.post("/search", summary="查询用户", response_model=List[schemas.User]) def search_users(body: schemas.UserSearch, skip: int = 0, limit: int = 20, db: Session = Depends(get_db)): users = crud.get_users(db, body=body, skip=skip, limit=limit) return users - - -@router.post("/detail", summary="用户详情", response_model=schemas.User) -def user_detail(uid: int, db: Session = Depends(get_db)): - data = crud.get_user_by_id(db, uid=uid) - if data is None: - raise HTTPException(status_code=404, detail="User Not found") - return data diff --git a/Utils/AccessControl/AccessUtil.py b/Utils/AccessControl/AccessUtil.py index 58904c6..b0a629b 100644 --- a/Utils/AccessControl/AccessUtil.py +++ b/Utils/AccessControl/AccessUtil.py @@ -1,27 +1,27 @@ -import os from typing import Optional -import casbin -from fastapi import HTTPException, status, Request, Header +from fastapi import HTTPException, Header from Utils.Authentication.TokenUtil import decode_token -e = casbin.Enforcer( - os.getcwd() + r"\Utils\AccessControl\model.conf", - os.getcwd() + r"\Utils\AccessControl\policy.csv" -) + +def ac_admin(token: Optional[str] = Header(...)): + + role = decode_token(token).get("user_info").get("role") + + if role != "admin": + raise HTTPException(status_code=400, detail="No Access") -def access_interseptor(request: Request, token: Optional[str] = Header(...)): - - sub = decode_token(token).get("role") - obj = request.url.__str__().split(request.base_url.__str__()[:-1])[-1].split("?")[0] - act = request.method - - if not e.enforce(sub, obj, act): - credentials_exception = HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="No Access", - headers={"WWW-Authenticate": "Bearer"}, - ) - raise credentials_exception +# def ac_index_store(request: Request, token: Optional[str] = Header(...)): +# e = casbin.Enforcer( +# os.getcwd() + r"\Utils\AccessControl\model.conf", +# os.getcwd() + r"\Utils\AccessControl\policy_index_store.csv" +# ) +# +# sub = decode_token(token).get("role") +# obj = request.url.__str__().split(request.base_url.__str__()[:-1])[-1].split("?")[0] +# act = request.method +# +# if not e.enforce(sub, obj, act): +# raise HTTPException(status_code=400, detail="No Access") diff --git a/Utils/AccessControl/model.conf b/Utils/AccessControl/model.conf index 0fb08a6..9bad335 100644 --- a/Utils/AccessControl/model.conf +++ b/Utils/AccessControl/model.conf @@ -1,15 +1,15 @@ -# Request definition [request_definition] r = sub, obj, act -# Policy definition [policy_definition] p = sub, obj, act -# Policy effect +[role_definition] +g = _, _ +g2 = _, _ + [policy_effect] e = some(where (p.eft == allow)) -# Matchers [matchers] -m = r.sub == p.sub && r.obj == p.obj && r.act == p.act +m = g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.act || r.sub == "admin" \ No newline at end of file diff --git a/Utils/AccessControl/policy.csv b/Utils/AccessControl/policy.csv deleted file mode 100644 index d6214d8..0000000 --- a/Utils/AccessControl/policy.csv +++ /dev/null @@ -1,8 +0,0 @@ -p, admin, /api/user/admin/create, POST -p, admin, /api/user/admin/delete, POST -p, admin, /api/user/admin/edit, POST -p, admin, /api/user/admin/search, POST -p, admin, /api/user/admin/detail, POST - -p, user, /api/user/detail, POST -p, user, /api/user/change_pwd, POST \ No newline at end of file diff --git a/Utils/AccessControl/policy_index_store.csv b/Utils/AccessControl/policy_index_store.csv new file mode 100644 index 0000000..e69de29 diff --git a/Utils/Authentication/Config.py b/Utils/Authentication/Config.py index d5defcb..f0424ba 100644 --- a/Utils/Authentication/Config.py +++ b/Utils/Authentication/Config.py @@ -1,3 +1,4 @@ SECRET_KEY = "HpGXrdwbL73ZPgQC" ALGORITHM = "HS256" -ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 30 +ACCESS_TOKEN_EXPIRE_MINUTES = 10 +REFRESH_TOKEN_EXPIRE_MINUTES = 60 * 24 * 30 diff --git a/Utils/Authentication/TokenUtil.py b/Utils/Authentication/TokenUtil.py index 7f23345..3b27735 100644 --- a/Utils/Authentication/TokenUtil.py +++ b/Utils/Authentication/TokenUtil.py @@ -18,7 +18,7 @@ def create_token(*, data: dict, expires_delta: timedelta = None): if expires_delta: expire = datetime.utcnow() + expires_delta else: - expire = datetime.utcnow() + timedelta(minutes=15) + expire = datetime.utcnow() + timedelta(minutes=5) to_encode.update({"exp": expire}) # Token编码 @@ -26,26 +26,33 @@ def create_token(*, data: dict, expires_delta: timedelta = None): return encoded_jwt +def create_refresh_token(uid: str, expires_delta: timedelta = None): + to_encode = dict() + + to_encode.update({"uid": uid}) + + if expires_delta: + expire = datetime.utcnow() + expires_delta + else: + expire = datetime.utcnow() + timedelta(minutes=60) + + to_encode.update({"exp": expire}) + + encoded_jwt = jwt.encode(to_encode, Config.SECRET_KEY, algorithm=Config.ALGORITHM) + return encoded_jwt + + def decode_token(token: str): - credentials_exception = HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Invalid Token", - headers={"WWW-Authenticate": "Bearer"}, - ) + try: payload = jwt.decode(token, Config.SECRET_KEY, algorithms=[Config.ALGORITHM]) + except jwt.exceptions.ExpiredSignatureError: + raise HTTPException(status_code=201, detail="Token Has Expired") except PyJWTError: - raise credentials_exception + raise HTTPException(status_code=401, detail="Invalid Token") return payload -async def get_token_header(authorization: str = Header(...)): - """ - 获取Token并验证 - :param authorization: - :return: uid - """ - token = authorization.split(' ')[-1] # 获取token - openid = decode_token(token) # 验证token - if not openid: - raise HTTPException(status_code=400, detail="无效Token") +def get_uid_from_token(token: str): + uid = decode_token(token).get("user_info").get("uid") + return uid diff --git a/Utils/DataBase/__init__.py b/Utils/DataBase/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Utils/UniqueCoder/__init__.py b/Utils/UniqueCoder/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/main.py b/main.py index 98de45c..0af09b7 100644 --- a/main.py +++ b/main.py @@ -16,4 +16,4 @@ app.add_middleware( app.include_router(router_user.router) app.include_router(router_user_admin.router) -app.include_router(router_policy.router) +# app.include_router(router_policy.router)