Implement user authentication and permissions

This commit is contained in:
Oliver Traber 2025-03-13 16:34:41 +00:00
parent 5e9d90ed0b
commit ac8303378a
Signed by: Bluemedia
GPG key ID: C0674B105057136C
26 changed files with 1182 additions and 172 deletions

72
app/routers/auth_v1.py Normal file
View file

@ -0,0 +1,72 @@
from uuid import UUID
from fastapi import APIRouter, HTTPException
from fastapi.params import Depends
from sqlalchemy.orm import Session
from app.database import get_db
from app.schemas.auth_token import (
AccessToken,
TokenRefreshRequest,
TokenResponse,
)
from app.schemas.user import LoginRequest
from app.security.jwt_bearer import JWTBearer
from app.services import session_service, token_service, user_service
from app.util.errors import NotFoundError
router = APIRouter(prefix="/auth", tags=["Authentication (v1)"])
@router.post(path="/login", response_model=TokenResponse)
async def login(
login_request: LoginRequest, db: Session = Depends(get_db)
):
"""
Login to a existing account. Creates a new session and returns a access and refresh token.
"""
user = await user_service.validate_login(
db=db, login=login_request
)
if not user:
raise HTTPException(status_code=403, detail="invalid_email_or_password")
session = await session_service.create_session(db=db, user=user, useragent="")
token, expire = await token_service.create_access_token(
user=user, session_id=session.id
)
return TokenResponse(
access_token=token, refresh_token=session.refresh_token, not_after=expire
)
@router.post(path="/logout", response_model=list[None])
async def logout(
db: Session = Depends(get_db), token: AccessToken = Depends(JWTBearer())
):
"""
Remove the current session based on the access token, effectively invalidating the current refresh token.
"""
await session_service.remove_session(
db=db, id=UUID(token.session), initiator=f"user:{token.subject}"
)
return list()
@router.post(path="/refresh", response_model=TokenResponse)
async def refresh_access_token(
token_request: TokenRefreshRequest,
db: Session = Depends(get_db),
):
"""
Use an existing refresh token to generate a new access token and a new refresh token.
"""
try:
session = await session_service.validate_and_rotate_refresh_token(
db=db, refresh_token=token_request.refresh_token
)
user = await user_service.get_user(db=db, id=session.user_id)
token, expire = await token_service.create_access_token(
user=user, session_id=session.id
)
return TokenResponse(
access_token=token, refresh_token=session.refresh_token, not_after=expire
)
except NotFoundError:
raise HTTPException(status_code=403, detail="invalid_refresh_token")

View file

@ -2,7 +2,7 @@ import random
import string
from datetime import datetime, timedelta, UTC
from uuid import UUID
from fastapi import APIRouter, HTTPException, Security
from fastapi import APIRouter, HTTPException
from fastapi.params import Depends
from sqlalchemy.orm import Session
@ -10,6 +10,7 @@ from ocpp.v201.call import Reset, SetVariables
from app.database import get_db
from app.ocpp_proto import chargepoint_manager
from app.schemas.auth_token import AccessToken
from app.schemas.chargepoint import (
ChargePoint,
ChargePointCreate,
@ -30,7 +31,7 @@ from app.schemas.chargepoint_variable import (
from app.models.chargepoint import ChargePoint as DbChargePoint
from app.models.user import User as DbUser
from app.models.chargepoint_variable import ChargepointVariable as DbChargepointVariable
from app.security import get_api_key
from app.security.jwt_bearer import JWTBearer
router = APIRouter(
prefix="/chargepoints",
@ -41,16 +42,16 @@ router = APIRouter(
async def get_chargepoints(
skip: int = 0,
limit: int = 20,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer()),
):
return db.query(DbChargePoint).offset(skip).limit(limit).all()
return db.query(DbChargePoint).order_by(DbChargePoint.identity).offset(skip).limit(limit).all()
@router.get(path="/{chargepoint_id}", response_model=ChargePoint)
async def get_chargepoint(
chargepoint_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer()),
):
chargepoint = db.get(DbChargePoint, chargepoint_id)
if chargepoint is None:
@ -60,8 +61,8 @@ async def get_chargepoint(
@router.get(path="/{chargepoint_id}/password", response_model=ChargePointPassword)
async def get_chargepoint_password(
chargepoint_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
chargepoint = db.get(DbChargePoint, chargepoint_id)
if chargepoint is None:
@ -71,8 +72,8 @@ async def get_chargepoint_password(
@router.delete(path="/{chargepoint_id}/password", response_model=ChargePointPassword)
async def reset_chargepoint_password(
chargepoint_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
chargepoint = db.get(DbChargePoint, chargepoint_id)
if chargepoint is None:
@ -84,8 +85,8 @@ async def reset_chargepoint_password(
@router.post(path="", status_code=201, response_model=ChargePoint)
async def create_chargepoint(
chargepoint: ChargePointCreate,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
chargepoint_db = DbChargePoint(
identity=chargepoint.identity,
@ -102,8 +103,8 @@ async def create_chargepoint(
async def update_chargepoint(
chargepoint_id: UUID,
chargepoint_update: ChargePointUpdate,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
chargepoint = db.get(DbChargePoint, chargepoint_id)
if chargepoint is None:
@ -116,8 +117,8 @@ async def update_chargepoint(
@router.delete(path="/{chargepoint_id}", response_model=None)
async def delete_chargepoint(
chargepoint_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
chargepoint = db.get(DbChargePoint, chargepoint_id)
if chargepoint is None:
@ -129,7 +130,7 @@ async def delete_chargepoint(
@router.get(path="/{chargepoint_id}/status", response_model=ChargePointConnectionInfo)
async def get_chargepoint_status(
chargepoint_id: UUID,
api_key: str = Security(get_api_key)
token: AccessToken = Depends(JWTBearer()),
):
return ChargePointConnectionInfo(
connected=chargepoint_manager.is_connected(chargepoint_id)
@ -139,7 +140,7 @@ async def get_chargepoint_status(
async def reset_chargepoint(
chargepoint_id: UUID,
reset_request: ChargePointResetRequest,
api_key: str = Security(get_api_key)
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
if chargepoint_manager.is_connected(chargepoint_id) == False:
raise HTTPException(status_code=503, detail="Chargepoint not connected.")
@ -156,8 +157,8 @@ async def reset_chargepoint(
async def create_id_token_learn_request(
chargepoint_id: UUID,
learn_request: IdTokenLearnRequest,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
chargepoint = db.get(DbChargePoint, chargepoint_id)
if chargepoint is None:
@ -188,8 +189,8 @@ async def create_id_token_learn_request(
@router.get(path="/{chargepoint_id}/token-learning", response_model=IdTokenLearnResponse)
async def get_id_token_learn_request(
chargepoint_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
chargepoint = db.get(DbChargePoint, chargepoint_id)
if chargepoint is None:
@ -206,8 +207,8 @@ async def get_id_token_learn_request(
@router.delete(path="/{chargepoint_id}/token-learning", response_model=[])
async def get_id_token_learn_request(
chargepoint_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
chargepoint = db.get(DbChargePoint, chargepoint_id)
if chargepoint is None:
@ -225,8 +226,8 @@ async def get_id_token_learn_request(
@router.get(path="/{chargepoint_id}/variables", response_model=list[ChargepointVariable])
async def get_chargepoint_variables(
chargepoint_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
chargepoint = db.get(DbChargePoint, chargepoint_id)
if chargepoint is None:
@ -239,8 +240,8 @@ async def update_chargepoint_variable(
chargepoint_id: UUID,
variable_id: UUID,
variable_update: ChargepointVariableUpdate,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
chargepoint = db.get(DbChargePoint, chargepoint_id)
if chargepoint is None:

View file

@ -1,14 +1,16 @@
from uuid import UUID
from fastapi import APIRouter, HTTPException, Security
from fastapi.exceptions import RequestValidationError
from fastapi import APIRouter, HTTPException
from fastapi.params import Depends
from sqlalchemy import select
from sqlalchemy.orm import Session
from app.database import get_db
from app.schemas.auth_token import AccessToken
from app.schemas.id_token import IdToken, IdTokenCreate, IdTokenUpdate
from app.models.id_token import IdToken as DbIdToken
from app.models.user import User as DbUser
from app.security import get_api_key
from app.schemas.user import Role
from app.security.jwt_bearer import JWTBearer
router = APIRouter(
prefix="/id-tokens",
@ -16,32 +18,43 @@ router = APIRouter(
)
@router.get(path="", response_model=list[IdToken])
async def get_it_tokens(
async def get_id_tokens(
skip: int = 0,
limit: int = 20,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer()),
):
return db.query(DbIdToken).offset(skip).limit(limit).all()
stmt = select(Session)
if token.role != Role.ADMINISTRATOR:
stmt = stmt.where(DbIdToken.owner_id == token.subject)
stmt = stmt.order_by(DbIdToken.id).offset(skip).limit(limit)
result = db.execute(stmt)
return result.scalars().all()
@router.get(path="/{id_token_id}", response_model=IdToken)
async def get_id_token(
id_token_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer()),
):
id_token = db.get(DbIdToken, id_token_id)
stmt = select(DbIdToken).where(DbIdToken.id == id_token_id)
result = db.execute(stmt)
id_token = result.scalars().first()
if id_token == None:
raise HTTPException(status_code=404, detail="IdToken not found")
if token.role != Role.ADMINISTRATOR & id_token.owner_id != token.subject:
raise HTTPException(status_code=404, detail="IdToken not found")
return id_token
@router.post(path="", status_code=201, response_model=IdToken)
async def create_id_token(
create_id_token: IdTokenCreate,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
owner = db.get(DbUser, create_id_token.owner_id)
stmt = select(DbUser).where(DbUser.id == create_id_token.owner_id)
result = db.execute(stmt)
owner = result.scalars().first()
if owner == None:
raise HTTPException(status_code=422, detail=[{
"loc": ["body", "owner_id"],
@ -63,15 +76,19 @@ async def create_id_token(
async def update_id_token(
id_token_id: UUID,
id_token_update: IdTokenUpdate,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
id_token = db.get(DbIdToken, id_token_id)
stmt = select(DbIdToken).where(DbIdToken.id == id_token_id)
result = db.execute(stmt)
id_token = result.scalars().first()
if id_token is None:
raise HTTPException(status_code=404, detail="IdToken not found")
for key, value in id_token_update.model_dump(exclude_unset=True).items():
if key == "owner_id":
owner = db.get(DbUser, value)
stmt = select(DbUser).where(DbUser.id == id_token_update.owner_id)
result = db.execute(stmt)
owner = result.scalars().first()
if owner == None:
raise HTTPException(status_code=422, detail=[{
"loc": ["body", "owner_id"],
@ -85,10 +102,12 @@ async def update_id_token(
@router.delete(path="/{id_token_id}", response_model=None)
async def delete_id_token(
id_token_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
id_token = db.get(DbIdToken, id_token_id)
stmt = select(DbIdToken).where(DbIdToken.id == id_token_id)
result = db.execute(stmt)
id_token = result.scalars().first()
if id_token == None:
raise HTTPException(status_code=404, detail="IdToken not found")
db.delete(id_token)

111
app/routers/me_v1.py Normal file
View file

@ -0,0 +1,111 @@
from uuid import UUID
from fastapi import APIRouter, HTTPException
from fastapi.params import Depends
from sqlalchemy.orm import Session as DbSession
from app.database import get_db
from app.schemas.session import Session
from app.schemas.auth_token import AccessToken
from app.schemas.user import PasswordUpdate, UserUpdate, User
from app.security.jwt_bearer import JWTBearer
from app.services import session_service, user_service
from app.util.errors import InvalidStateError, NotFoundError
router = APIRouter(prefix="/me", tags=["Me (v1)"])
@router.get(path="", response_model=User)
async def get_myself(
db: DbSession = Depends(get_db), token: AccessToken = Depends(JWTBearer())
):
"""
Get the currently authenticated user.
"""
user = await user_service.get_user(db=db, id=UUID(token.subject))
if not user:
raise HTTPException(status_code=404, detail="user_not_found")
else:
return user
@router.patch(path="", response_model=User)
async def update_myself(
user_update: UserUpdate,
db: DbSession = Depends(get_db),
token: AccessToken = Depends(JWTBearer()),
):
"""
Update the currently authenticated user. Changing the email address automatically marks it as not verified
and starts a new verification workflow.
"""
try:
return await user_service.update_user(
db, UUID(token.subject), user_update
)
except NotFoundError:
raise HTTPException(status_code=404, detail="user_not_found")
@router.post(path="/password", response_model=list[None])
async def change_password(
update: PasswordUpdate,
db: DbSession = Depends(get_db),
token: AccessToken = Depends(JWTBearer()),
):
"""
Change the password of the currently authenticated user.
"""
try:
await user_service.change_user_password(
db=db, id=UUID(token.subject), update=update
)
return list()
except NotFoundError:
raise HTTPException(status_code=404, detail="user_not_found")
except InvalidStateError:
raise HTTPException(status_code=409, detail="incorrect_password")
@router.get(path="/sessions", response_model=list[Session])
async def get_user_sessions(
db: DbSession = Depends(get_db), token: AccessToken = Depends(JWTBearer())
):
"""
List the active sessions of the currently authenticated user.
"""
return await session_service.get_sessions_by_user(
db=db, user_id=UUID(token.subject)
)
@router.delete(path="/sessions", response_model=list[None])
async def clear_user_sessions(
db: DbSession = Depends(get_db), token: AccessToken = Depends(JWTBearer())
):
"""
Clear all sessions of the currently authenticated user.
"""
await session_service.remove_all_sessions_for_user(
db=db, user_id=UUID(token.subject),
)
return list()
@router.delete(path="/sessions/{session_id}", response_model=list[None])
async def delete_user_session(
session_id: UUID,
db: DbSession = Depends(get_db),
token: AccessToken = Depends(JWTBearer()),
):
"""
Invalidate a specific session of the currently authenticated user.
"""
try:
await session_service.remove_session_for_user(
db=db,
id=session_id,
user_id=UUID(token.subject),
)
except NotFoundError:
raise HTTPException(status_code=404, detail="session_not_found")
return list()

View file

@ -1,11 +1,12 @@
from fastapi import APIRouter, Depends
from fastapi.params import Security
from sqlalchemy import select
from sqlalchemy.orm import Session
from app.security import get_api_key
from app.schemas.auth_token import AccessToken
from app.database import get_db
from app.schemas.meter_value import MeterValue
from app.models.meter_value import MeterValue as DbMeterValue
from app.security.jwt_bearer import JWTBearer
router = APIRouter(
prefix="/meter-values",
@ -16,7 +17,9 @@ router = APIRouter(
async def get_meter_values(
skip: int = 0,
limit: int = 20,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
return db.query(DbMeterValue).offset(skip).limit(limit).all()
stmt = select(DbMeterValue).order_by(DbMeterValue.timestamp).offset(skip).limit(limit)
result = db.execute(stmt)
return result.scalars().all()

View file

@ -1,14 +1,18 @@
from fastapi import APIRouter, Depends, HTTPException
from fastapi.params import Security
from sqlalchemy import select
from sqlalchemy.orm import Session
from ocpp.v201.call import RequestStopTransaction
from app.ocpp_proto import chargepoint_manager
from app.security import get_api_key
from app.schemas.auth_token import AccessToken
from app.database import get_db
from app.schemas.meter_value import MeterValue
from app.schemas.transaction import Transaction, RemoteTransactionStartStopResponse, TransactionStatus, RemoteTransactionStartStopStatus
from app.models.transaction import Transaction as DbTransaction
from app.models.meter_value import MeterValue as DbMeterValue
from app.schemas.user import Role
from app.security.jwt_bearer import JWTBearer
router = APIRouter(
prefix="/transactions",
@ -19,31 +23,61 @@ router = APIRouter(
async def get_transactions(
skip: int = 0,
limit: int = 20,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer()),
):
return db.query(DbTransaction).offset(skip).limit(limit).all()
stmt = select(DbTransaction)
if (token.role != Role.ADMINISTRATOR):
stmt = stmt.where(DbTransaction.user_id == token.subject)
stmt = stmt.order_by(DbTransaction.started_at).offset(skip).limit(limit)
result = db.execute(stmt)
return result.scalars().all()
@router.get(path="/{transaction_id}", response_model=Transaction)
async def get_transaction(
transaction_id: str,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer()),
):
transaction = db.get(DbTransaction, transaction_id)
stmt = select(DbTransaction).where(DbTransaction.id == transaction_id)
result = db.execute(stmt)
transaction = result.scalars().first()
if transaction == None:
raise HTTPException(404, "Transaction not found")
if token.role != Role.ADMINISTRATOR & transaction.user_id != token.subject:
raise HTTPException(404, "Transaction not found")
return transaction
@router.get(path="/{transaction_id}/meter-values", response_model=list[MeterValue])
async def get_transaction_meter_values(
transaction_id: str,
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer()),
):
stmt = select(DbTransaction).where(DbTransaction.id == transaction_id)
result = db.execute(stmt)
transaction = result.scalars().first()
if transaction == None:
raise HTTPException(404, "Transaction not found")
if token.role != Role.ADMINISTRATOR & transaction.user_id != token.subject:
raise HTTPException(404, "Transaction not found")
stmt = select(DbMeterValue).where(DbMeterValue.transaction_id == transaction_id).order_by(DbMeterValue.timestamp)
result = db.execute(stmt)
return result.scalars().all()
@router.post(path="/{transaction_id}/remote-stop", response_model=RemoteTransactionStartStopResponse)
async def remote_stop_transaction(
transaction_id: str,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
token: AccessToken = Depends(JWTBearer()),
):
transaction = db.get(DbTransaction, transaction_id)
stmt = select(DbTransaction).where(DbTransaction.id == transaction_id)
result = db.execute(stmt)
transaction = result.scalars().first()
if transaction == None:
raise HTTPException(404, "Transaction not found")
if token.role != Role.ADMINISTRATOR & transaction.user_id != token.subject:
raise HTTPException(404, "Transaction not found")
if transaction.status != TransactionStatus.ONGOING:
raise HTTPException(status_code=422, detail=[{
"loc": ["path", "transaction_id"],
@ -51,7 +85,7 @@ async def remote_stop_transaction(
"type": "invalid_transaction_state"
}])
if chargepoint_manager.is_connected(transaction.chargepoint_id) == False:
raise HTTPException(status_code=503, detail="Chargepoint not connected.")
raise HTTPException(status_code=503, detail="chargepoint_offline")
try:
result = await chargepoint_manager.call(
transaction.chargepoint_id,
@ -63,4 +97,4 @@ async def remote_stop_transaction(
raise HTTPException(status_code=500, detail=result.status)
return RemoteTransactionStartStopResponse(status=result.status)
except TimeoutError:
raise HTTPException(status_code=503, detail="Chargepoint didn't respond in time.")
raise HTTPException(status_code=503, detail="chargepoint_operation_timeout")

View file

@ -1,12 +1,15 @@
from uuid import UUID
from fastapi import APIRouter, HTTPException, Security
from fastapi import APIRouter, HTTPException
from fastapi.params import Depends
from sqlalchemy.orm import Session
from sqlalchemy.orm import Session as DbSession
from app.database import get_db
from app.schemas.session import Session
from app.schemas.auth_token import AccessToken
from app.schemas.user import User, UserCreate, UserUpdate
from app.models.user import User as DbUser
from app.security import get_api_key
from app.security.jwt_bearer import JWTBearer
from app.services import session_service, user_service
from app.util.errors import NotFoundError
router = APIRouter(
prefix="/users",
@ -15,63 +18,99 @@ router = APIRouter(
@router.get(path="", response_model=list[User])
async def get_users(
email: str = None,
skip: int = 0,
limit: int = 20,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: DbSession = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
return db.query(DbUser).offset(skip).limit(limit).all()
@router.get(path="/{user_id}", response_model=User)
async def get_user(
user_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
):
user = db.get(DbUser, user_id)
if user == None:
raise HTTPException(status_code=404, detail="User not found")
return user
return await user_service.get_users(db, skip, limit, email)
@router.post(path="", status_code=201, response_model=User)
async def create_user(
create_user: UserCreate,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: DbSession = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
db_user = DbUser(
friendly_name=create_user.friendly_name,
is_active=create_user.is_active
user = await user_service.create_user(
db=db, user=create_user
)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
return user
@router.patch(path="/{user_id}", response_model=User)
async def update_user(
user_id: UUID,
user_update: UserUpdate,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: DbSession = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
user = db.get(DbUser, user_id)
if user is None:
raise HTTPException(status_code=404, detail="User not found")
for key, value in user_update.model_dump(exclude_unset=True).items():
setattr(user, key, value)
db.commit()
return user
try:
return await user_service.update_user(
db, user_id, user_update
)
except NotFoundError:
raise HTTPException(status_code=404, detail="user_not_found")
@router.delete(path="/{user_id}", response_model=None)
async def delete_user(
user_id: UUID,
api_key: str = Security(get_api_key),
db: Session = Depends(get_db)
db: DbSession = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
user = db.get(DbUser, user_id)
if user == None:
raise HTTPException(status_code=404, detail="User not found")
db.delete(user)
db.commit()
return []
try:
await user_service.remove_user(db, user_id)
return list()
except NotFoundError:
raise HTTPException(status_code=404, detail="user_not_found")
@router.get(
path="/{user_id}/sessions", response_model=list[Session]
)
async def get_user_sessions(
user_id: UUID,
db: DbSession = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
"""
Query sessions of the specified user. Requires the "administrator" role.
"""
return await session_service.get_sessions_by_user(db=db, user_id=user_id)
@router.delete(
path="/{user_id}/sessions", response_model=list[None]
)
async def remove_all_user_session(
user_id: UUID,
db: DbSession = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
"""
Delete all sessions of the specified user. Requires the "administrator" role.
"""
await session_service.remove_all_sessions_for_user(
db=db, user_id=user_id
)
return list()
@router.delete(
path="/{user_id}/sessions/{session_id}",
response_model=list[None],
)
async def remove_user_session(
user_id: UUID,
session_id: UUID,
db: DbSession = Depends(get_db),
token: AccessToken = Depends(JWTBearer(required_roles=["administrator"])),
):
"""
Delete the specified session of the specified user. Requires the "administrator" role.
"""
try:
await session_service.remove_session_for_user(
db=db, id=session_id, user_id=user_id
)
except NotFoundError:
raise HTTPException(status_code=404, detail="session_not_found")
return list()