+
-
-
+
+
-
-
+
+
diff --git a/fuware-fe/src/utils/enum.js b/fuware-fe/src/utils/enum.js
index 885263d..0c96433 100644
--- a/fuware-fe/src/utils/enum.js
+++ b/fuware-fe/src/utils/enum.js
@@ -1,5 +1,5 @@
-const PRODUCTION = import.meta.env.NODE_ENV === 'production'
+// const PRODUCTION = import.meta.env.NODE_ENV === 'production'
export const SECRET_KEY = 'bGV0IGRvIGl0IGZvciBlbmNyeXRo'
export const STORE_KEY = 'dXNlciBsb2dpbiBpbmZv'
-export const LOGIN_KEY = PRODUCTION ? import.meta.env.VITE_LOGIN_KEY : 'logcook'
+export const LOGIN_KEY = '7fo24CMyIc'
diff --git a/fuware-fe/src/utils/helper.js b/fuware-fe/src/utils/helper.js
index 72293aa..570b48d 100644
--- a/fuware-fe/src/utils/helper.js
+++ b/fuware-fe/src/utils/helper.js
@@ -2,6 +2,13 @@ import { AES, enc } from 'crypto-js'
import { LOGIN_KEY, SECRET_KEY, STORE_KEY } from './enum'
export class Helpers {
+ static setCookie = (cname, cvalue, exdays) => {
+ const d = new Date()
+ d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000)
+ let expires = `expires=${d.toUTCString()}`
+ document.cookie = `${cname}=${cvalue};${expires};path=/`
+ }
+
static getCookie = (cname) => {
let name = cname + '='
let ca = document.cookie.split(';')
@@ -21,11 +28,16 @@ export class Helpers {
document.cookie = `${cname}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`
}
- static clearCookie = () => {
- this.deleteCookie(LOGIN_KEY)
+ static clearStorage = () => {
+ localStorage.removeItem(LOGIN_KEY)
localStorage.removeItem(STORE_KEY)
}
+ static checkTokenExpired = (exp) => {
+ const currentTime = Math.floor(new Date().getTime() / 1000)
+ return exp < currentTime
+ }
+
static checkAuth = () => {
return !!this.getCookie(LOGIN_KEY) && !!localStorage.getItem(STORE_KEY)
}
diff --git a/fuware-fe/tailwind.config.js b/fuware-fe/tailwind.config.js
new file mode 100644
index 0000000..b624e18
--- /dev/null
+++ b/fuware-fe/tailwind.config.js
@@ -0,0 +1,10 @@
+import daisyui from 'daisyui'
+
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: ['./src/**/*.{js,jsx}'],
+ theme: {
+ extend: {},
+ },
+ plugins: [daisyui],
+}
diff --git a/fuware/core/__init__.py b/fuware/core/__init__.py
index e69de29..4f329e5 100644
--- a/fuware/core/__init__.py
+++ b/fuware/core/__init__.py
@@ -0,0 +1 @@
+from .message_code import *
diff --git a/fuware/core/dependencies/dependencies.py b/fuware/core/dependencies/dependencies.py
index cf579b9..8202e1f 100644
--- a/fuware/core/dependencies/dependencies.py
+++ b/fuware/core/dependencies/dependencies.py
@@ -1,20 +1,76 @@
-from fastapi import Depends, HTTPException, Request
-from sqlalchemy.orm import Session
+from fastapi import Depends, HTTPException, Request, status
+from fastapi.security import OAuth2PasswordBearer
from fuware.core.config import get_app_settings
-from fuware.db.db_setup import generate_session
+from fuware.core import MessageCode
+import jwt
+from fuware.services.user.user_service import UserService
+
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/token")
+oauth2_scheme_soft_fail = OAuth2PasswordBearer(tokenUrl="/api/auth/token", auto_error=False)
+ALGORITHM = "HS256"
settings = get_app_settings()
-async def get_auth_user(request: Request, db: Session = Depends(generate_session)):
+credentials_exception = HTTPException(
+ status_code=status.HTTP_401_UNAUTHORIZED,
+ detail="Could not validate credentials",
+ headers={"WWW-Authenticate": "Bearer"},
+)
+
+async def is_logged_in(token: str = Depends(oauth2_scheme_soft_fail)) -> bool:
+ try:
+ payload = jwt.decode(token, settings.SECRET, algorithms=[ALGORITHM])
+ user_id: str = payload.get("sub")
+ exp: int = payload.get("exp")
+
+ if exp is not None:
+ try:
+ user_service = UserService()
+ user = user_service.get_by_id(user_id)
+ if not user:
+ raise credentials_exception
+ if user.is_lock is True:
+ raise HTTPException(status_code=status.HTTP_423_LOCKED, detail=MessageCode.ACCOUNT_LOCK)
+ except Exception:
+ return credentials_exception
+
+ return user
+ except Exception:
+ raise credentials_exception
+
+async def get_current_user(request: Request, token: str | None = Depends(oauth2_scheme_soft_fail)):
"""verify that user has a valid session"""
- session_id = request.cookies.get(settings.COOKIE_KEY)
- if not session_id:
- raise HTTPException(status_code=401, detail="Unauthorized")
- # decrypt_user = decryptString(session_id).split(',')
- # db_user = get_user_by_username(db, decrypt_user[0])
- # if not db_user:
- # raise HTTPException(status_code=403)
- # if not verify_password(decrypt_user[1], db_user.password):
- # raise HTTPException(status_code=401, detail="Your username or password input is wrong!")
- return True
+ if token is None and settings.COOKIE_KEY in request.cookies:
+ # Try extract from cookie
+ token = request.cookies.get(settings.COOKIE_KEY, "")
+ else:
+ token = token or ""
+
+ try:
+ payload = jwt.decode(token, settings.SECRET, algorithms=[ALGORITHM])
+ user_id: str = payload.get("sub")
+ exp: int = payload.get("exp")
+
+ if user_id is None or exp is None:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="credentials have expired",
+ )
+
+ user_service = UserService()
+ user = user_service.get_by_id(user_id)
+
+ if not user:
+ raise credentials_exception
+ if user.is_lock is True:
+ raise HTTPException(status_code=status.HTTP_423_LOCKED, detail=MessageCode.ACCOUNT_LOCK)
+
+ return user
+ except jwt.ExpiredSignatureError:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="credentials have expired",
+ )
+ except Exception:
+ raise credentials_exception
diff --git a/fuware/core/message_code.py b/fuware/core/message_code.py
index efb5094..11abb5a 100644
--- a/fuware/core/message_code.py
+++ b/fuware/core/message_code.py
@@ -1,8 +1,6 @@
-class MessageCode:
+
+class MessageCode():
CREATED_USER: str = 'CREATED_USER'
WRONG_INPUT: str = 'LOGIN_WRONG'
ACCOUNT_LOCK: str = 'USER_LOCK'
-
-
-def message_code():
- return MessageCode()
+ REFRESH_TOKEN_EXPIRED: str = 'REFRESH_TOKEN_EXPIRED'
diff --git a/fuware/core/security/__init__.py b/fuware/core/security/__init__.py
index 040aee3..cfec0e0 100644
--- a/fuware/core/security/__init__.py
+++ b/fuware/core/security/__init__.py
@@ -1 +1 @@
-from .hasher import get_hasher
+from .security import *
diff --git a/fuware/core/security/security.py b/fuware/core/security/security.py
new file mode 100644
index 0000000..558bb54
--- /dev/null
+++ b/fuware/core/security/security.py
@@ -0,0 +1,46 @@
+import secrets
+from datetime import datetime, timedelta, timezone
+from pathlib import Path
+
+import jwt
+
+from fuware.core.config import get_app_settings
+from fuware.core import root_logger
+from fuware.core.security.hasher import get_hasher
+
+ALGORITHM = "HS256"
+
+logger = root_logger.get_logger("security")
+settings = get_app_settings()
+
+def create_access_token(data: dict, expires_delta: timedelta | None = None) -> str:
+ settings = get_app_settings()
+
+ to_encode = data.copy()
+ expires_delta = expires_delta or timedelta(minutes=settings.EXP_TOKEN)
+
+ expire = datetime.now(timezone.utc) + expires_delta
+
+ to_encode["exp"] = expire
+ return jwt.encode(to_encode, settings.SECRET, algorithm=ALGORITHM)
+
+def create_refresh_token(data: dict) -> str:
+ return create_access_token(data, expires_delta=timedelta(days=settings.EXP_REFRESH))
+
+def create_file_token(file_path: Path) -> str:
+ token_data = {"file": str(file_path)}
+ return create_access_token(token_data, expires_delta=timedelta(minutes=30))
+
+
+def hash_password(password: str) -> str:
+ """Takes in a raw password and hashes it. Used prior to saving a new password to the database."""
+ return get_hasher().hash(password)
+
+
+def url_safe_token() -> str:
+ """Generates a cryptographic token without embedded data. Used for password reset tokens and invitation tokens"""
+ return secrets.token_urlsafe(24)
+
+def verify_token(exp: int):
+ expried = datetime.fromtimestamp(exp / 1e3)
+ return expried < datetime.now(timezone.utc)
diff --git a/fuware/core/settings/settings.py b/fuware/core/settings/settings.py
index ec4901c..63fbb13 100644
--- a/fuware/core/settings/settings.py
+++ b/fuware/core/settings/settings.py
@@ -7,7 +7,7 @@ def determine_secrets(production: bool) -> str:
if not production:
return "shh-secret-test-key"
- return "oWNhXlfo666JlMHk6UHYxeNB6z_CA2MisDDZJe4N0yc="
+ return "1d00e664fb3b07aff5a191755ea72f9c4bc85a3f36868308d0b2c417aed3419e"
def determine_cookie(production: bool) -> str:
if not production:
@@ -31,6 +31,10 @@ class AppSettings(BaseSettings):
SECRET: str
COOKIE_KEY: str
+ EXP_TOKEN: int = 30
+ """in minutes, default is 30 minutes"""
+ EXP_REFRESH: int = 7
+ """in days, default is 7 days"""
LOG_CONFIG_OVERRIDE: Path | None = None
"""path to custom logging configuration file"""
diff --git a/fuware/db/models/users/users.py b/fuware/db/models/users/users.py
index 672240d..42aff88 100644
--- a/fuware/db/models/users/users.py
+++ b/fuware/db/models/users/users.py
@@ -16,19 +16,5 @@ class User(SqlAlchemyBase):
is_admin: Mapped[bool | None] = mapped_column(Boolean, default=False)
is_lock: Mapped[bool | None] = mapped_column(Boolean, default=False)
- session_login = relationship("SessionLogin", back_populates="user", uselist=False)
-
def __repr__(self):
return f"{self.__class__.__name__}, name: {self.name}, username: {self.username}"
-
-class SessionLogin(SqlAlchemyBase):
- __tablename__ = 'session_login'
-
- id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
- session: Mapped[str] = mapped_column(String, unique=True, index=True, nullable=False)
- user_id: Mapped[str] = mapped_column(ForeignKey("users.id"), unique=True, index=True, nullable=False)
-
- user = relationship("User", back_populates="session_login")
-
- def __repr__(self):
- return f"{self.__class__.__name__}, session: {self.session}, user_id: {self.user_id}"
diff --git a/fuware/db/seeder.py b/fuware/db/seeder.py
deleted file mode 100644
index 1e28774..0000000
--- a/fuware/db/seeder.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from fuware.core.security import get_hasher
-
-hasher = get_hasher()
-
-INITIAL_DATA = {
- 'users': [
- {
- 'username': 'sam',
- 'password': hasher.hash('admin'),
- 'name': 'Sam',
- 'is_admin': 1,
- 'is_lock': 0,
- },
- {
- 'username': 'sam1',
- 'password': hasher.hash('admin'),
- 'name': 'Sam1',
- 'is_admin': 0,
- 'is_lock': 1
- },
- ]
-}
-
-# This method receives a table, a connection and inserts data to that table.
-def initialize_table(target, connection, **kwargs):
- tablename = str(target)
- if tablename in INITIAL_DATA and len(INITIAL_DATA[tablename]) > 0:
- connection.execute(target.insert(), INITIAL_DATA[tablename])
diff --git a/fuware/repos/repository_users.py b/fuware/repos/repository_users.py
index fac39ee..443f2d0 100644
--- a/fuware/repos/repository_users.py
+++ b/fuware/repos/repository_users.py
@@ -1,16 +1,17 @@
from fuware.core.config import get_app_settings
-from fuware.core.security.hasher import get_hasher
-from fuware.db.models import SessionLogin, User
+from fuware.core.security.security import hash_password
+from fuware.db.models import User
from fuware.schemas import UserCreate
from sqlalchemy.orm import Session
-from uuid import uuid4
+from uuid import UUID
+
+from fuware.schemas.user.user import UserSeeds
settings = get_app_settings()
class RepositoryUsers:
def __init__(self):
self.user = User()
- self.sessionLogin = SessionLogin()
def get_all(self, skip: int = 0, limit: int = 100):
return self.user.query.offset(skip).limit(limit).all()
@@ -18,10 +19,13 @@ class RepositoryUsers:
def get_by_username(self, username: str):
return self.user.query.filter_by(username=username).first()
- def create(self, db: Session, user: UserCreate):
+ def get_by_id(self, user_id: str):
+ return self.user.query.filter_by(id=UUID(user_id)).first()
+
+ def create(self, db: Session, user: UserCreate | UserSeeds):
try:
- hasher = get_hasher()
- db_user = User(username=user.username, password=hasher.hash(user.password), name=user.name)
+ password = getattr(user, "password")
+ db_user = User(**user.dict(exclude={"password"}), password=hash_password(password))
db.add(db_user)
db.commit()
except Exception:
@@ -30,36 +34,3 @@ class RepositoryUsers:
db.refresh(db_user)
return db_user
-
- def get_session_by_user_id(self, user_id: str):
- return self.sessionLogin.query.filter_by(user_id=user_id).first()
-
- def create_session(self, db: Session, user_id: str):
- try:
- bhash = uuid4().hex[:10]
- db_ss = SessionLogin(session=bhash,user_id=user_id)
- db.add(db_ss)
- db.commit()
- except Exception:
- db.rollback()
- raise
-
- db.refresh(db_ss)
- return db_ss
-
- def login(self, db: Session, user_id: str):
- db_ss = self.get_session_by_user_id(user_id)
- if not db_ss:
- db_ss = self.create_session(db=db, user_id=user_id)
- return db_ss
-
- def logout(self, db: Session, user_ss: str):
- print(f"Logout: {user_ss}")
- db_ss = self.sessionLogin.query.filter_by(session=user_ss).first()
- print(f"db_ss: {db_ss}")
- try:
- db.delete(db_ss)
- db.commit()
- except Exception as e:
- db.rollback()
- raise e
diff --git a/fuware/repos/seeder/init_users.py b/fuware/repos/seeder/init_users.py
index 93dfde0..2be0506 100644
--- a/fuware/repos/seeder/init_users.py
+++ b/fuware/repos/seeder/init_users.py
@@ -3,7 +3,7 @@ from fuware.core.root_logger import get_logger
from fuware.repos.repository_users import RepositoryUsers
from sqlalchemy.orm import Session
-from fuware.schemas.user.user import UserCreate
+from fuware.schemas.user import UserSeeds
logger = get_logger("init_users")
@@ -15,19 +15,19 @@ def dev_users() -> list[dict]:
"username": "sam",
"password": "admin",
"name": "Sam",
- "is_admin": 1,
- "is_lock": 0,
+ "is_admin": True,
+ "is_lock": False,
},
{
"username": "sam1",
"password": "admin",
"name": "Sam1",
- "is_admin": 0,
- "is_lock": 1
+ "is_admin": False,
+ "is_lock": False,
},
]
def default_users_init(session: Session):
users = RepositoryUsers()
for user in dev_users():
- users.create(session, UserCreate(**user))
+ users.create(session, UserSeeds(**user))
diff --git a/fuware/routes/__init__.py b/fuware/routes/__init__.py
index 5ca2a2d..2b9470e 100644
--- a/fuware/routes/__init__.py
+++ b/fuware/routes/__init__.py
@@ -1,7 +1,9 @@
from fastapi import APIRouter
-from . import (auth)
+
+from . import (auth, user)
router = APIRouter(prefix='/api')
router.include_router(auth.router)
+router.include_router(user.router)
diff --git a/fuware/routes/auth/__init__.py b/fuware/routes/auth/__init__.py
index e89c859..4c6c29b 100644
--- a/fuware/routes/auth/__init__.py
+++ b/fuware/routes/auth/__init__.py
@@ -4,4 +4,4 @@ from . import auth
router = APIRouter(prefix='/auth')
-router.include_router(auth.public_router)
+router.include_router(auth.auth_router)
diff --git a/fuware/routes/auth/auth.py b/fuware/routes/auth/auth.py
index d946616..314fc65 100644
--- a/fuware/routes/auth/auth.py
+++ b/fuware/routes/auth/auth.py
@@ -1,43 +1,70 @@
-from typing import Any
-from fastapi import APIRouter, Depends, HTTPException, Response, Request
+from datetime import datetime, timedelta, timezone
+from typing import Annotated, Any
+from fastapi import APIRouter, Depends, HTTPException, Response, status
-from fastapi.encoders import jsonable_encoder
+# from fastapi.encoders import jsonable_encoder
+from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from fuware.core.config import get_app_settings
-from fuware.core.message_code import message_code
-from fuware.core.security.hasher import get_hasher
+from fuware.core.dependencies.dependencies import get_current_user
+from fuware.core import MessageCode
from fuware.db.db_setup import generate_session
-from fuware.schemas import ReturnValue, UserRequest, PrivateUser, UserCreate
-from fuware.services import UserService
+from fuware.schemas import ReturnValue, UserRequest, LoginResponse, UserCreate, PrivateUser
+from fuware.services.user import UserService
-public_router = APIRouter(tags=["Users: Authentication"])
+auth_router = APIRouter(tags=["Users: Authentication"])
user_service = UserService()
-hasher = get_hasher()
settings = get_app_settings()
-message = message_code()
-@public_router.put('/register')
-def register_user(user: UserCreate, db: Session = Depends(generate_session)) -> ReturnValue[Any]:
+db_dependency = Annotated[Session, Depends(generate_session)]
+current_user_token = Annotated[PrivateUser, Depends(get_current_user)]
+
+@auth_router.post('/token')
+async def get_token(form_data: Annotated[OAuth2PasswordRequestForm, Depends()], db: db_dependency):
+ user = user_service.check_exist(user=UserRequest(username=form_data.username, password=form_data.password))
+ if not user:
+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=MessageCode.WRONG_INPUT)
+ token = user_service.get_access_token(user_id=user.id)
+ return {'access_token': token, 'token_type': 'bearer'}
+
+
+@auth_router.put('/register')
+def register_user(user: UserCreate, db: db_dependency) -> ReturnValue[Any]:
db_user = user_service.get_by_username(username=user.username)
if db_user:
- raise HTTPException(status_code=400, detail=message.CREATED_USER)
- user_return = user_service.create(db=db, user=user)
- return ReturnValue(status=200, data=jsonable_encoder(user_return))
+ raise HTTPException(status_code=400, detail=MessageCode.CREATED_USER)
+ user_service.create(db=db, user=user)
+ return ReturnValue(status=200, data="created")
-@public_router.post('/login', response_model=ReturnValue[PrivateUser])
-def user_login(user: UserRequest, response: Response, db: Session = Depends(generate_session)) -> ReturnValue[Any]:
+@auth_router.post('/login', response_model=ReturnValue[LoginResponse])
+def user_login(user: UserRequest, response: Response) -> ReturnValue[Any]:
db_user = user_service.check_exist(user=user)
- cookieEncode = user_service.check_login(db=db, user_id=db_user.id)
- response.set_cookie(key=settings.COOKIE_KEY, value=cookieEncode, max_age=86400, httponly=True)
- return ReturnValue(status=200, data=db_user)
+ if not db_user:
+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=MessageCode.WRONG_INPUT)
+ if db_user.is_lock is True:
+ raise HTTPException(status_code=status.HTTP_423_LOCKED, detail=MessageCode.ACCOUNT_LOCK)
+ access_token, refresh_token = user_service.generate_token(user_id=db_user.id)
+ duration_access = datetime.now(timezone.utc) + timedelta(minutes=settings.EXP_TOKEN)
+ duration_refresh = int(timedelta(days=settings.EXP_REFRESH).total_seconds())
+ response.set_cookie(
+ key=settings.COOKIE_KEY,
+ value=refresh_token,
+ max_age=duration_refresh,
+ expires=duration_refresh,
+ httponly=True,
+ samesite="strict",
+ )
+ return ReturnValue(status=200, data=dict(access_token=access_token, exp=int(duration_access.timestamp()), name=db_user.name))
-@public_router.get('/logout', response_model=ReturnValue[Any])
-def user_logout(request: Request, response: Response, db: Session = Depends(generate_session)) -> ReturnValue[Any]:
- session_id = request.cookies.get(settings.COOKIE_KEY)
- if not session_id:
+@auth_router.get('/refresh')
+def user_check(current_user: current_user_token) -> ReturnValue[Any]:
+ access_token = user_service.get_access_token(user_id=current_user.id)
+ duration_access = datetime.now(timezone.utc) + timedelta(minutes=settings.EXP_TOKEN)
+ return ReturnValue(status=200, data=dict(accessToken=access_token, exp=int(duration_access.timestamp())))
+
+@auth_router.get('/logout')
+def user_logout(response: Response, current_user: current_user_token) -> ReturnValue[Any]:
+ if current_user:
response.delete_cookie(key=settings.COOKIE_KEY)
- return ReturnValue(status=200, data='Logged out')
- user_service.delete_session(db=db, user_ss=session_id)
- response.delete_cookie(key=settings.COOKIE_KEY)
return ReturnValue(status=200, data='Logged out')
diff --git a/fuware/routes/user/__init__.py b/fuware/routes/user/__init__.py
new file mode 100644
index 0000000..0200558
--- /dev/null
+++ b/fuware/routes/user/__init__.py
@@ -0,0 +1,7 @@
+
+from fastapi import APIRouter
+from . import user
+
+router = APIRouter(prefix='/user')
+
+router.include_router(user.public_router)
diff --git a/fuware/routes/user/user.py b/fuware/routes/user/user.py
new file mode 100644
index 0000000..e864627
--- /dev/null
+++ b/fuware/routes/user/user.py
@@ -0,0 +1,21 @@
+from typing import Annotated, Any
+from fastapi import APIRouter, Depends
+from sqlalchemy.orm import Session
+from fuware.core.config import get_app_settings
+from fuware.core.dependencies import is_logged_in
+from fuware.db.db_setup import generate_session
+from fuware.schemas.common import ReturnValue
+from fuware.schemas.user import ProfileResponse
+from fuware.services.user import UserService
+
+
+public_router = APIRouter(tags=["Users: Info"])
+user_service = UserService()
+settings = get_app_settings()
+
+db_dependency = Annotated[Session, Depends(generate_session)]
+current_user_token = Annotated[ProfileResponse, Depends(is_logged_in)]
+
+@public_router.get("/me", response_model=ReturnValue[ProfileResponse])
+def get_user(current_user: current_user_token) -> ReturnValue[Any]:
+ return ReturnValue(status=200, data=current_user)
diff --git a/fuware/schemas/user/user.py b/fuware/schemas/user/user.py
index 0a7eb62..bf43af0 100644
--- a/fuware/schemas/user/user.py
+++ b/fuware/schemas/user/user.py
@@ -12,9 +12,12 @@ class UserRequest(UserBase):
password: str = Form(...)
class UserCreate(UserRequest):
- password: str = Form(...)
name: str
+class UserSeeds(UserCreate):
+ is_admin: bool
+ is_lock: bool
+
class PrivateUser(UserBase):
id: UUID
name: str
@@ -23,3 +26,16 @@ class PrivateUser(UserBase):
created_at: datetime
updated_at: datetime
model_config = ConfigDict(from_attributes=True)
+
+class ProfileResponse(UserBase):
+ name: str
+ is_admin: bool
+ is_lock: bool
+ created_at: datetime
+ updated_at: datetime
+ model_config = ConfigDict(from_attributes=True)
+
+class LoginResponse(FuwareModel):
+ access_token: str
+ exp: int
+ name: str
diff --git a/fuware/services/__init__.py b/fuware/services/__init__.py
index f4a2da0..e69de29 100644
--- a/fuware/services/__init__.py
+++ b/fuware/services/__init__.py
@@ -1 +0,0 @@
-from .user import *
diff --git a/fuware/services/user/user_service.py b/fuware/services/user/user_service.py
index 0e65323..9377cea 100644
--- a/fuware/services/user/user_service.py
+++ b/fuware/services/user/user_service.py
@@ -1,15 +1,11 @@
-
-from fastapi import HTTPException
from sqlalchemy.orm import Session
-from fuware.core.message_code import message_code
from fuware.core.security.hasher import get_hasher
+from fuware.core.security import create_access_token
+from fuware.core.security.security import create_refresh_token
from fuware.repos import RepositoryUsers
from fuware.schemas import UserRequest, UserCreate
from fuware.services._base_service import BaseService
-hasher = get_hasher()
-message = message_code()
-
class UserService(BaseService):
def __init__(self):
self.repos = RepositoryUsers()
@@ -20,22 +16,24 @@ class UserService(BaseService):
def get_by_username(self, username: str):
return self.repos.get_by_username(username)
+ def get_by_id(self, user_id: str):
+ return self.repos.get_by_id(user_id)
+
def create(self, db: Session, user: UserCreate):
return self.repos.create(db=db, user=user)
def check_exist(self, user: UserRequest):
db_user = self.get_by_username(username=user.username)
if not db_user:
- raise HTTPException(status_code=401, detail=message.WRONG_INPUT)
- if not hasher.verify(password=user.password, hashed=db_user.password):
- raise HTTPException(status_code=401, detail=message.WRONG_INPUT)
- if db_user.is_lock is True:
- raise HTTPException(status_code=401, detail=message.ACCOUNT_LOCK)
+ return False
+ if not get_hasher().verify(password=user.password, hashed=db_user.password):
+ return False
return db_user
- def check_login(self, db: Session, user_id: str):
- db_session = self.repos.login(db=db, user_id=user_id)
- return db_session.session
+ def generate_token(self, user_id: str):
+ access_token = create_access_token(data={"sub": str(user_id)})
+ refresh_token = create_refresh_token(data={"sub": str(user_id)})
+ return access_token, refresh_token
- def delete_session(self, db: Session, user_ss: str):
- self.repos.logout(db=db, user_ss=user_ss)
+ def get_access_token(self, user_id: str):
+ return create_access_token(data={"sub": str(user_id)})
diff --git a/poetry.lock b/poetry.lock
index 0d55904..e5d40fc 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -784,6 +784,23 @@ files = [
{file = "pyhumps-3.8.0.tar.gz", hash = "sha256:498026258f7ee1a8e447c2e28526c0bea9407f9a59c03260aee4bd6c04d681a3"},
]
+[[package]]
+name = "pyjwt"
+version = "2.8.0"
+description = "JSON Web Token implementation in Python"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"},
+ {file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"},
+]
+
+[package.extras]
+crypto = ["cryptography (>=3.4.0)"]
+dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
+docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
+tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
+
[[package]]
name = "pylint"
version = "3.1.0"
@@ -827,6 +844,20 @@ files = [
[package.extras]
cli = ["click (>=5.0)"]
+[[package]]
+name = "python-multipart"
+version = "0.0.9"
+description = "A streaming multipart parser for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"},
+ {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"},
+]
+
+[package.extras]
+dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"]
+
[[package]]
name = "pyyaml"
version = "6.0.1"
@@ -1331,4 +1362,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
-content-hash = "88c334ca5304e6f095e81628512c132bb419f9ee5bb7e1e278a0b9d5f94b84c5"
+content-hash = "4763f2b3b35e1b674b8636d573beb91e38da9e4a2b52634e6c6840dbca49f538"
diff --git a/pyproject.toml b/pyproject.toml
index 46beb45..5ce63f1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -19,6 +19,8 @@ text-unidecode = "^1.3"
pyhumps = "^3.8.0"
bcrypt = "^4.1.3"
alembic = "^1.13.1"
+python-multipart = "^0.0.9"
+pyjwt = "^2.8.0"
[tool.poetry.group.dev.dependencies]
ruff = "^0.4.1"