-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Refactor user endpoints and authentication handling - Renamed endpoint file to "endpoints" - Updated routes in API router - Added new auth module for user authentication - Created user model with fields and history entries * Refactor authentication and user creation logic, update dependencies, and improve error handling. Add token generation functionality and enhance user data retrieval by email. Include object ID conversion utilities and enable debug logging for the application. * Add work-in-progress note to README.md Added a work-in-progress note to the README file. This commit also removed an empty line in the Admin collection section. * updated license
- Loading branch information
Showing
14 changed files
with
568 additions
and
735 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
from typing import Annotated | ||
from fastapi import Depends | ||
from fastapi.exceptions import ValidationException | ||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm | ||
from pydantic import BaseModel | ||
from authly.db.mongo import MongoDBManager | ||
|
||
from authly.db.redis import RedisManager | ||
from authly.api.security.authentication import token_module | ||
from authly.core.utils import hashing | ||
from authly.api.user import model | ||
from authly.api.user import user as userManager | ||
|
||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/user/token") | ||
|
||
|
||
class TokenModel(BaseModel): | ||
access_token: str | ||
token_type: str | ||
|
||
|
||
async def authenticate_user( | ||
user_data: OAuth2PasswordRequestForm, mongo_client: MongoDBManager | ||
) -> str: | ||
try: | ||
success, data, details = await userManager.get_user_data_by_email( | ||
user_data.username, mongo_client | ||
) | ||
|
||
user = model.User(**data) | ||
|
||
if not hashing.verify_password(user_data.password, user.password): | ||
raise ValidationException("invalid password") | ||
|
||
redis_manager = RedisManager(redis_db=1) | ||
redis_manager.connect() | ||
|
||
new_token = token_module.get_new_token( | ||
str(data.id), redis_manager | ||
) # TODO: test with redis active | ||
|
||
except ValueError: | ||
raise | ||
except ValidationException: | ||
raise | ||
except Exception: | ||
raise | ||
|
||
else: | ||
return new_token | ||
|
||
finally: | ||
redis_manager.close() | ||
|
||
|
||
async def get_current_user_id( | ||
token: Annotated[str, Depends(oauth2_scheme)] | ||
) -> str: | ||
redis_manager = RedisManager() | ||
try: | ||
redis_manager.connect() | ||
user_id = token_module.get_user_id(token, redis_manager) | ||
|
||
except Exception: | ||
raise ValidationException("invalid token") | ||
|
||
else: | ||
return user_id | ||
|
||
finally: | ||
redis_manager.close() |
File renamed without changes.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
from typing import Annotated | ||
|
||
import fastapi | ||
from fastapi import APIRouter, Depends, HTTPException | ||
from fastapi.security import OAuth2PasswordRequestForm | ||
from pydantic import ValidationError | ||
|
||
|
||
from authly.api.security.authentication import auth | ||
from authly.api.user import model as userModel | ||
from authly.api.user import user as userManager | ||
|
||
from authly.core.utils.log import Logger, LogLevel | ||
from authly.api.security.authentication import token_module as TokenManager | ||
from authly.db.mongo import MongoDBManager | ||
from authly.db.redis import RedisManager | ||
|
||
user = APIRouter() | ||
|
||
|
||
@user.get("/") | ||
async def user_router_hello_world(Depends): | ||
return { | ||
"msg": f"Hello World from user Route", | ||
"name": "current_user", | ||
} | ||
|
||
|
||
@user.get("/me") | ||
async def get_current_user( | ||
current_user: Annotated[userModel.User, Depends(auth.get_current_user_id)], | ||
): | ||
return current_user | ||
|
||
|
||
@user.post("/", response_model=userModel.CreateUserResponse) | ||
async def create_user( | ||
form_data: Annotated[OAuth2PasswordRequestForm, Depends()], | ||
) -> userModel.CreateUserResponse: | ||
try: | ||
mongo_client = MongoDBManager("user") | ||
|
||
data = await userManager.create_user( | ||
email=str(form_data.username), | ||
password=str(form_data.password), | ||
mongo_client=mongo_client, | ||
role=[userModel.UserRole.USER], | ||
) | ||
|
||
Logger.log(LogLevel.DEBUG, data) | ||
|
||
return userModel.CreateUserResponse( | ||
id=data.id, username=data.username, email=data.email | ||
) | ||
|
||
except ValueError as e: | ||
raise HTTPException( | ||
status_code=400, detail=str(e) | ||
) # Return a 400 Bad Request with the error message | ||
|
||
except ValidationError as e: | ||
raise HTTPException( | ||
status_code=422, detail=e.errors() | ||
) # Return a 422 Unprocessable Entity with the validation errors | ||
|
||
finally: | ||
( | ||
success, | ||
results, | ||
status, | ||
) = await mongo_client.close_connection() | ||
|
||
if not success: | ||
Logger.log( | ||
LogLevel.ERROR, | ||
f"close_connection failed: ", | ||
success, | ||
results, | ||
status, | ||
) | ||
|
||
|
||
@user.post("/token", response_model=auth.TokenModel) | ||
async def login( | ||
form_data: Annotated[OAuth2PasswordRequestForm, Depends()] | ||
) -> auth.TokenModel: | ||
try: | ||
mongo_client = MongoDBManager("user") | ||
|
||
token = await auth.authenticate_user(form_data, mongo_client) | ||
|
||
Logger.log(LogLevel.DEBUG, user) | ||
|
||
except FileNotFoundError as e: | ||
Logger.log( | ||
LogLevel.ERROR, "email/user not found", "FileNotFoundError", e | ||
) | ||
raise HTTPException( | ||
status_code=fastapi.status.HTTP_401_UNAUTHORIZED, | ||
detail="email/user not found", | ||
headers={"WWW-Authenticate": "Bearer"}, | ||
) | ||
# modify if you want to exclude specific infos | ||
|
||
except ValidationError as e: | ||
Logger.log(LogLevel.ERROR, "password missmatch", "ValidationErr", e) | ||
raise HTTPException( | ||
status_code=fastapi.status.HTTP_401_UNAUTHORIZED, | ||
detail="password missmatch", | ||
headers={"WWW-Authenticate": "Bearer"}, | ||
) | ||
# modify if you want to exclude specific infos | ||
|
||
except Exception as e: | ||
Logger.log(LogLevel.ERROR, e) | ||
raise HTTPException(status_code=500, detail="Internal Server Error") | ||
|
||
else: | ||
return token | ||
|
||
finally: | ||
( | ||
success, | ||
results, | ||
status, | ||
) = await mongo_client.close_connection() | ||
|
||
if not success: | ||
Logger.log( | ||
LogLevel.ERROR, | ||
f"close_connection failed: ", | ||
success, | ||
results, | ||
status, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
from typing import List, Optional | ||
from pydantic import BaseModel | ||
from enum import Enum | ||
|
||
|
||
class UserRole(str, Enum): | ||
ADMIN = "ADMIN" | ||
USER = "USER" | ||
MODERATOR = "MODERATOR" | ||
|
||
|
||
class CreateUserResponse(BaseModel): | ||
id: Optional[str] | ||
username: str | ||
email: str | ||
|
||
|
||
class UsernameHistoryEntry(BaseModel): | ||
from_date: str | ||
to_date: Optional[str] = None | ||
|
||
|
||
class EmailHistoryEntry(BaseModel): | ||
from_date: str | ||
to_date: Optional[str] = None | ||
|
||
|
||
class UserKey(BaseModel): | ||
from_date: str | ||
to_date: str | ||
banned: bool | ||
|
||
|
||
class User(BaseModel): | ||
id: Optional[str] = None | ||
username: str | ||
email: str | ||
password: str | ||
role: List[UserRole] | ||
disabled: bool | ||
geo_location: Optional[str] = None | ||
username_history: Optional[List[UsernameHistoryEntry]] = [] | ||
email_history: Optional[List[EmailHistoryEntry]] = [] | ||
# keys: Optional[List[UserKey]] = [] | ||
# settings: Optional[List] = [] | ||
# add last seen and last login |
Oops, something went wrong.