This commit is contained in:
admin
2026-05-22 23:05:25 +09:00
parent cea3016fbe
commit e8690d130b
23 changed files with 581 additions and 187 deletions

View File

@@ -2,9 +2,11 @@ from dotenv import load_dotenv
load_dotenv()
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse, RedirectResponse
from fastapi.responses import JSONResponse, RedirectResponse, Response
from pydantic import ValidationError
from fastapi.middleware.cors import CORSMiddleware
from starlette.datastructures import MutableHeaders
from starlette.types import ASGIApp, Receive, Scope, Send
# FastAPI アプリケーション作成
app = FastAPI(
@@ -12,13 +14,37 @@ app = FastAPI(
)
class SecurityHeadersMiddleware:
"""キャッシュ禁止・セキュリティヘッダーを全レスポンスに付与ASGIネイティブ実装"""
def __init__(self, app: ASGIApp) -> None:
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] != "http":
await self.app(scope, receive, send)
return
async def send_with_headers(message):
if message["type"] == "http.response.start":
headers = MutableHeaders(scope=message)
headers["Cache-Control"] = "no-store, no-cache, must-revalidate, private"
headers["Pragma"] = "no-cache"
headers["Expires"] = "0"
headers["X-Frame-Options"] = "DENY"
headers["X-Content-Type-Options"] = "nosniff"
headers["X-XSS-Protection"] = "1; mode=block"
await send(message)
await self.app(scope, receive, send_with_headers)
app.add_middleware(SecurityHeadersMiddleware)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=False,
allow_methods=["*"],
allow_headers=["*"],
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["Content-Type", "Authorization"],
)
# ─────────────────────────

View File

@@ -12,6 +12,12 @@ class LoginRequest(BaseModel):
password: str
class ChangePasswordRequest(BaseModel):
username: str
current_password: str
new_password: str
class LoginResponse(BaseModel):
id: int
username: str
@@ -66,39 +72,48 @@ async def login(request: LoginRequest):
@router.post("/register")
async def register(request: LoginRequest):
"""新規ユーザー登録"""
"""新規ユーザー登録(無効化済み)"""
raise HTTPException(status_code=403, detail="新規ユーザー登録は現在受け付けていません")
@router.post("/change-password")
async def change_password(request: ChangePasswordRequest):
"""パスワード変更"""
if len(request.new_password) < 8:
raise HTTPException(status_code=400, detail="新しいパスワードは8文字以上である必要があります")
try:
if len(request.password) < 6:
raise HTTPException(status_code=400, detail="パスワードは6文字以上である必要があります")
conn = get_connection()
cur = conn.cursor()
# ユーザーが既に存在するか確認
cur.execute("SELECT id FROM users WHERE username = %s", (request.username,))
if cur.fetchone():
cur.execute(
"SELECT id, password FROM users WHERE username = %s AND is_active = TRUE",
(request.username,)
)
user = cur.fetchone()
if not user:
cur.close()
conn.close()
raise HTTPException(status_code=400, detail="このユーザー名は既に使用されています")
# ユーザーを登録
hashed_password = hash_password(request.password)
raise HTTPException(status_code=401, detail="ユーザーが見つかりません")
if user["password"] != hash_password(request.current_password):
cur.close()
conn.close()
raise HTTPException(status_code=401, detail="現在のパスワードが正しくありません")
new_hashed = hash_password(request.new_password)
cur.execute(
"INSERT INTO users (username, password) VALUES (%s, %s) RETURNING id, username",
(request.username, hashed_password)
"UPDATE users SET password = %s WHERE username = %s",
(new_hashed, request.username)
)
new_user = cur.fetchone()
conn.commit()
cur.close()
conn.close()
return {
"id": new_user["id"],
"username": new_user["username"],
"message": "ユーザー登録が完了しました"
}
return {"message": "パスワードを変更しました"}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"登録処理エラー: {str(e)}")
raise HTTPException(status_code=500, detail=f"パスワード変更エラー: {str(e)}")