Merge pull request #444 from EstrellaXD/auth-method-change

change: Auth method changed from Auth token to cookies.
This commit is contained in:
Estrella Pan
2023-09-06 21:44:40 +08:00
committed by GitHub
12 changed files with 125 additions and 274 deletions

View File

@@ -3,7 +3,6 @@ from fastapi import APIRouter
from .auth import router as auth_router
from .bangumi import router as bangumi_router
from .config import router as config_router
from .download import router as download_router
from .log import router as log_router
from .program import router as program_router
from .rss import router as rss_router
@@ -16,7 +15,6 @@ v1 = APIRouter(prefix="/v1")
v1.include_router(auth_router)
v1.include_router(log_router)
v1.include_router(program_router)
v1.include_router(download_router)
v1.include_router(bangumi_router)
v1.include_router(config_router)
v1.include_router(rss_router)

View File

@@ -2,7 +2,7 @@ from datetime import timedelta
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from fastapi.responses import JSONResponse
from fastapi.responses import JSONResponse, Response
from module.models.user import User, UserUpdate
from module.models import APIResponse
@@ -10,6 +10,7 @@ from module.security.api import (
auth_user,
get_current_user,
update_user_info,
active_user
)
from module.security.jwt import create_access_token
@@ -17,70 +18,46 @@ router = APIRouter(prefix="/auth", tags=["auth"])
@router.post("/login", response_model=dict)
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
async def login(response: Response, form_data=Depends(OAuth2PasswordRequestForm)):
user = User(username=form_data.username, password=form_data.password)
auth_user(user)
token = create_access_token(
data={"sub": user.username}, expires_delta=timedelta(days=1)
)
response.set_cookie(key="token", value=token, httponly=True, max_age=86400)
return {"access_token": token, "token_type": "bearer"}
@router.get("/refresh_token", response_model=dict, dependencies=[Depends(get_current_user)])
async def refresh(response: Response):
token = create_access_token(
data={"sub": get_current_user()}, expires_delta=timedelta(days=1)
)
response.set_cookie(key="token", value=token, httponly=True, max_age=86400)
return {"access_token": token, "token_type": "bearer"}
@router.get("/logout", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def logout(response: Response):
active_user.clear()
response.delete_cookie(key="token")
return JSONResponse(
status_code=status.HTTP_200_OK,
content={
"access_token": token,
"token_type": "bearer",
"expire": 86400,
},
status_code=200,
content={"msg_en": "Logout successfully.", "msg_zh": "登出成功。"},
)
@router.get("/refresh_token", response_model=dict)
async def refresh(current_user: User = Depends(get_current_user)):
if not current_user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
)
token = create_access_token(data={"sub": current_user.username})
return JSONResponse(
status_code=status.HTTP_200_OK,
content={
"access_token": token,
"token_type": "bearer",
"expire": 86400,
},
)
@router.get("/logout", response_model=APIResponse)
async def logout(current_user: User = Depends(get_current_user)):
if not current_user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
)
return JSONResponse(
status_code=status.HTTP_200_OK,
content={
"msg_en": "Logout success",
"msg_zh": "登出成功",
},
)
@router.post("/update", response_model=dict)
@router.post("/update", response_model=dict, dependencies=[Depends(get_current_user)])
async def update_user(
user_data: UserUpdate, current_user: User = Depends(get_current_user)
user_data: UserUpdate, response: Response
):
if not current_user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
)
if update_user_info(user_data, current_user):
return JSONResponse(
status_code=status.HTTP_200_OK,
content={
"message": "update success",
"access_token": create_access_token({"sub": user_data.username}),
"token_type": "bearer",
"expire": 86400,
},
old_user = active_user[0]
if update_user_info(user_data, old_user):
token = create_access_token(data={"sub": old_user}, expires_delta=timedelta(days=1))
response.set_cookie(
key="token",
value=token,
httponly=True,
max_age=86400,
)
return {"access_token": token, "token_type": "bearer"}

View File

@@ -16,93 +16,67 @@ def str_to_list(data: Bangumi):
return data
@router.get("/get/all", response_model=list[Bangumi])
async def get_all_data(current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.get("/get/all", response_model=list[Bangumi], dependencies=[Depends(get_current_user)])
async def get_all_data():
with TorrentManager() as manager:
return manager.bangumi.search_all()
@router.get("/get/{bangumi_id}", response_model=Bangumi)
async def get_data(bangumi_id: str, current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.get("/get/{bangumi_id}", response_model=Bangumi, dependencies=[Depends(get_current_user)])
async def get_data(bangumi_id: str):
with TorrentManager() as manager:
resp = manager.search_one(bangumi_id)
return resp
@router.patch("/update/{bangumi_id}", response_model=APIResponse)
@router.patch("/update/{bangumi_id}", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def update_rule(
bangumi_id: int, data: BangumiUpdate, current_user=Depends(get_current_user)
bangumi_id: int, data: BangumiUpdate,
):
if not current_user:
raise UNAUTHORIZED
with TorrentManager() as manager:
resp = manager.update_rule(bangumi_id, data)
return u_response(resp)
@router.delete(path="/delete/{bangumi_id}", response_model=APIResponse)
async def delete_rule(
bangumi_id: str, file: bool = False, current_user=Depends(get_current_user)
):
if not current_user:
raise UNAUTHORIZED
@router.delete(path="/delete/{bangumi_id}", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def delete_rule(bangumi_id: str, file: bool = False):
with TorrentManager() as manager:
resp = manager.delete_rule(bangumi_id, file)
return u_response(resp)
@router.delete(path="/delete/many/", response_model=APIResponse)
async def delete_many_rule(
bangumi_id: list, file: bool = False, current_user=Depends(get_current_user)
):
if not current_user:
raise UNAUTHORIZED
@router.delete(path="/delete/many/", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def delete_many_rule(bangumi_id: list, file: bool = False):
with TorrentManager() as manager:
for i in bangumi_id:
resp = manager.delete_rule(i, file)
return u_response(resp)
@router.delete(path="/disable/{bangumi_id}", response_model=APIResponse)
async def disable_rule(
bangumi_id: str, file: bool = False, current_user=Depends(get_current_user)
):
if not current_user:
raise UNAUTHORIZED
@router.delete(path="/disable/{bangumi_id}", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def disable_rule(bangumi_id: str, file: bool = False):
with TorrentManager() as manager:
resp = manager.disable_rule(bangumi_id, file)
return u_response(resp)
@router.delete(path="/disable/many/", response_model=APIResponse)
async def disable_many_rule(
bangumi_id: list, file: bool = False, current_user=Depends(get_current_user)
):
if not current_user:
raise UNAUTHORIZED
@router.delete(path="/disable/many/", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def disable_many_rule(bangumi_id: list, file: bool = False):
with TorrentManager() as manager:
for i in bangumi_id:
resp = manager.disable_rule(i, file)
return u_response(resp)
@router.get(path="/enable/{bangumi_id}", response_model=APIResponse)
async def enable_rule(bangumi_id: str, current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.get(path="/enable/{bangumi_id}", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def enable_rule(bangumi_id: str):
with TorrentManager() as manager:
resp = manager.enable_rule(bangumi_id)
return u_response(resp)
@router.get("/reset/all", response_model=APIResponse)
async def reset_all(current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.get("/reset/all", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def reset_all():
with TorrentManager() as manager:
manager.bangumi.delete_all()
return JSONResponse(

View File

@@ -11,17 +11,13 @@ router = APIRouter(prefix="/config", tags=["config"])
logger = logging.getLogger(__name__)
@router.get("/get", response_model=Config)
async def get_config(current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.get("/get", response_model=Config, dependencies=[Depends(get_current_user)])
async def get_config():
return settings
@router.patch("/update", response_model=APIResponse)
async def update_config(config: Config, current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.patch("/update", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def update_config(config: Config):
try:
settings.save(config_dict=config.dict())
settings.load()

View File

@@ -1,65 +0,0 @@
from fastapi import APIRouter, Depends
from fastapi.responses import JSONResponse
from module.manager import SeasonCollector
from module.models import Bangumi, RSSItem, APIResponse
from module.rss import RSSAnalyser
from module.security.api import get_current_user, UNAUTHORIZED
router = APIRouter(prefix="/download", tags=["download"])
analyser = RSSAnalyser()
@router.post("/analysis", response_model=Bangumi)
async def analysis(rss: RSSItem, current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
data = analyser.link_to_data(rss)
if data:
return data
else:
return JSONResponse(
status_code=406,
content={"msg_en": "Analysis failed.", "msg_zh": "解析失败。"},
)
@router.post("/collection", response_model=APIResponse)
async def download_collection(data: Bangumi, current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
if data:
with SeasonCollector() as collector:
if collector.collect_season(data, data.rss_link[0]):
return JSONResponse(
status_code=200,
content={"msg_en": "Add torrent successfully.", "msg_zh": "添加种子成功。"},
)
else:
return JSONResponse(
status_code=406,
content={"msg_en": "Add torrent failed.", "msg_zh": "添加种子失败。"},
)
else:
return JSONResponse(
status_code=406,
content={"msg_en": "Add torrent failed.", "msg_zh": "添加种子失败。"},
)
@router.post("/subscribe", response_model=APIResponse)
async def subscribe(data: Bangumi, current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
if data:
with SeasonCollector() as collector:
collector.subscribe_season(data)
return JSONResponse(
status_code=200,
content={"msg_en": "Subscribe successfully.", "msg_zh": "订阅成功。"},
)
else:
return JSONResponse(
status_code=406,
content={"msg_en": "Subscribe failed.", "msg_zh": "订阅失败。"},
)

View File

@@ -8,10 +8,8 @@ from module.models import APIResponse
router = APIRouter(prefix="/log", tags=["log"])
@router.get("")
async def get_log(current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.get("", response_model=str, dependencies=[Depends(get_current_user)])
async def get_log():
if LOG_PATH.exists():
with open(LOG_PATH, "rb") as f:
return Response(f.read(), media_type="text/plain")
@@ -19,10 +17,8 @@ async def get_log(current_user=Depends(get_current_user)):
return Response("Log file not found", status_code=404)
@router.get("/clear", response_model=APIResponse)
async def clear_log(current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.get("/clear", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def clear_log():
if LOG_PATH.exists():
LOG_PATH.write_text("")
return JSONResponse(

View File

@@ -25,10 +25,8 @@ async def shutdown():
program.stop()
@router.get("/restart", response_model=APIResponse)
async def restart(current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.get("/restart", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def restart():
try:
program.restart()
return JSONResponse(

View File

@@ -13,36 +13,28 @@ from module.manager import SeasonCollector
router = APIRouter(prefix="/rss", tags=["rss"])
@router.get(path="", response_model=list[RSSItem])
async def get_rss(current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.get(path="", response_model=list[RSSItem], dependencies=[Depends(get_current_user)])
async def get_rss():
with RSSEngine() as engine:
return engine.rss.search_all()
@router.post(path="/add", response_model=APIResponse)
async def add_rss(rss: RSSItem, current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.post(path="/add", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def add_rss(rss: RSSItem, ):
with RSSEngine() as engine:
result = engine.add_rss(rss.url, rss.name, rss.aggregate)
return u_response(result)
@router.post(path="/enable/many", response_model=APIResponse)
async def enable_many_rss(rss_ids: list[int], current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.post(path="/enable/many", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def enable_many_rss(rss_ids: list[int], ):
with RSSEngine() as engine:
result = engine.enable_list(rss_ids)
return u_response(result)
@router.delete(path="/delete/{rss_id}", response_model=APIResponse)
async def delete_rss(rss_id: int, current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.delete(path="/delete/{rss_id}", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def delete_rss(rss_id: int, ):
with RSSEngine() as engine:
if engine.rss.delete(rss_id):
return JSONResponse(
@@ -56,19 +48,15 @@ async def delete_rss(rss_id: int, current_user=Depends(get_current_user)):
)
@router.post(path="/delete/many", response_model=APIResponse)
async def delete_many_rss(rss_ids: list[int], current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.post(path="/delete/many", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def delete_many_rss(rss_ids: list[int], ):
with RSSEngine() as engine:
result = engine.delete_list(rss_ids)
return u_response(result)
@router.patch(path="/disable/{rss_id}", response_model=APIResponse)
async def disable_rss(rss_id: int, current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.patch(path="/disable/{rss_id}", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def disable_rss(rss_id: int, ):
with RSSEngine() as engine:
if engine.rss.disable(rss_id):
return JSONResponse(
@@ -82,16 +70,14 @@ async def disable_rss(rss_id: int, current_user=Depends(get_current_user)):
)
@router.post(path="/disable/many", response_model=APIResponse)
async def disable_many_rss(rss_ids: list[int], current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.post(path="/disable/many", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def disable_many_rss(rss_ids: list[int], ):
with RSSEngine() as engine:
result = engine.disable_list(rss_ids)
return u_response(result)
@router.patch(path="/update/{rss_id}", response_model=APIResponse)
@router.patch(path="/update/{rss_id}", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def update_rss(
rss_id: int, data: RSSUpdate, current_user=Depends(get_current_user)
):
@@ -110,10 +96,8 @@ async def update_rss(
)
@router.get(path="/refresh/all", response_model=APIResponse)
async def refresh_all(current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.get(path="/refresh/all", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def refresh_all():
with RSSEngine() as engine, DownloadClient() as client:
engine.refresh_rss(client)
return JSONResponse(
@@ -122,10 +106,8 @@ async def refresh_all(current_user=Depends(get_current_user)):
)
@router.get(path="/refresh/{rss_id}", response_model=APIResponse)
async def refresh_rss(rss_id: int, current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.get(path="/refresh/{rss_id}", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def refresh_rss(rss_id: int, ):
with RSSEngine() as engine, DownloadClient() as client:
engine.refresh_rss(client, rss_id)
return JSONResponse(
@@ -134,10 +116,8 @@ async def refresh_rss(rss_id: int, current_user=Depends(get_current_user)):
)
@router.get(path="/torrent/{rss_id}", response_model=list[Torrent])
async def get_torrent(rss_id: int, current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.get(path="/torrent/{rss_id}", response_model=list[Torrent], dependencies=[Depends(get_current_user)])
async def get_torrent(rss_id: int, ):
with RSSEngine() as engine:
return engine.get_rss_torrents(rss_id)
@@ -146,10 +126,8 @@ async def get_torrent(rss_id: int, current_user=Depends(get_current_user)):
analyser = RSSAnalyser()
@router.post("/analysis", response_model=Bangumi)
async def analysis(rss: RSSItem, current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.post("/analysis", response_model=Bangumi, dependencies=[Depends(get_current_user)])
async def analysis(rss: RSSItem, ):
data = analyser.link_to_data(rss)
if isinstance(data, Bangumi):
return data
@@ -157,19 +135,15 @@ async def analysis(rss: RSSItem, current_user=Depends(get_current_user)):
return u_response(data)
@router.post("/collect", response_model=APIResponse)
async def download_collection(data: Bangumi, current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.post("/collect", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def download_collection(data: Bangumi, ):
with SeasonCollector() as collector:
resp = collector.collect_season(data, data.rss_link)
return u_response(resp)
@router.post("/subscribe", response_model=APIResponse)
async def subscribe(data: Bangumi, current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.post("/subscribe", response_model=APIResponse, dependencies=[Depends(get_current_user)])
async def subscribe(data: Bangumi, ):
with SeasonCollector() as collector:
resp = collector.subscribe_season(data)
return u_response(resp)

View File

@@ -9,17 +9,14 @@ from module.models import Bangumi
router = APIRouter(prefix="/search", tags=["search"])
@router.get("/bangumi", response_model=list[Bangumi])
@router.get("/bangumi", response_model=list[Bangumi], dependencies=[Depends(get_current_user)])
async def search_torrents(
site: str = "mikan",
keywords: str = Query(None),
current_user=Depends(get_current_user),
keywords: str = Query(None)
):
"""
Server Send Event for per Bangumi item
"""
if not current_user:
raise UNAUTHORIZED
if not keywords:
return []
keywords = keywords.split(" ")
@@ -29,8 +26,6 @@ async def search_torrents(
)
@router.get("/provider", response_model=list[str])
async def search_provider(current_user=Depends(get_current_user)):
if not current_user:
raise UNAUTHORIZED
@router.get("/provider", response_model=list[str], dependencies=[Depends(get_current_user)])
async def search_provider():
return list(SEARCH_CONFIG.keys())

View File

@@ -1,4 +1,4 @@
from fastapi import Depends, HTTPException, status
from fastapi import Depends, HTTPException, status, Cookie
from fastapi.security import OAuth2PasswordBearer
from module.database import Database
@@ -8,25 +8,21 @@ from .jwt import verify_token
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login")
active_user = []
async def get_current_user(token: str = Depends(oauth2_scheme)):
async def get_current_user(token: str = Cookie(None)):
if not token:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
)
raise UNAUTHORIZED
payload = verify_token(token)
if not payload:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token"
)
raise UNAUTHORIZED
username = payload.get("sub")
with Database() as db:
user = db.user.get_user(username)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid username"
)
return user
if not username:
raise UNAUTHORIZED
if username not in active_user:
raise UNAUTHORIZED
return username
async def get_token_data(token: str = Depends(oauth2_scheme)):
@@ -41,7 +37,7 @@ async def get_token_data(token: str = Depends(oauth2_scheme)):
def update_user_info(user_data: UserUpdate, current_user):
try:
with Database() as db:
db.user.update_user(current_user.username, user_data)
db.user.update_user(current_user, user_data)
return True
except Exception as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
@@ -49,7 +45,9 @@ def update_user_info(user_data: UserUpdate, current_user):
def auth_user(user: User):
with Database() as db:
db.user.auth_user(user)
if db.user.auth_user(user):
active_user.append(user.username)
return True
UNAUTHORIZED = HTTPException(

View File

@@ -3,7 +3,14 @@ from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext
app_pwd_key = "auto_bangumi"
def generate_key():
import secrets
return secrets.token_urlsafe(32)
app_pwd_key = generate_key()
app_pwd_algorithm = "HS256"
# Hashing 密码

View File

@@ -3,15 +3,18 @@ import type { ApiError } from "#/api";
export const axios = Axios.create();
axios.interceptors.request.use((config) => {
const { auth } = useAuth();
// axios.interceptors.request.use((config) => {
// const { auth } = useAuth();
//
// // if (auth.value !== '' && config.headers) {
// // config.headers.Authorization = auth.value;
// // }
//
// return config;
// });
if (auth.value !== '' && config.headers) {
config.headers.Authorization = auth.value;
}
return config;
});
// axios.defaults.baseURL = '/api/v1';
axios.defaults.withCredentials = true;
axios.interceptors.response.use(
(res) => {