Skip to content

Commit 1cdb7c9

Browse files
committed
add mixins and jwt strategy
1 parent 2e77ffc commit 1cdb7c9

9 files changed

Lines changed: 97 additions & 51 deletions

File tree

api_v1/auth/__init__.py

Whitespace-only changes.

api_v1/auth/backends.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from fastapi_users.authentication import (
2+
BearerTransport,
3+
AuthenticationBackend,
4+
JWTStrategy,
5+
)
6+
7+
from config import settings
8+
9+
10+
bearer_transport = BearerTransport()
11+
12+
13+
def get_jwt_strategy() -> JWTStrategy:
14+
return JWTStrategy(secret=settings.SECRET)

api_v1/auth/schemas.py

Whitespace-only changes.

api_v1/auth/views.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from fastapi import APIRouter
2+
3+
4+
router = APIRouter(prefix='/auth',
5+
tags=['Auth'],
6+
)
7+
8+
9+
10+
@router.post(path='/login',
11+
)

api_v1/routers.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from config import settings
44
from api_v1.users.views import router as users
5+
from api_v1.auth.views import router as auth
56

67

78
def register_routers(app: FastAPI) -> None:
@@ -13,7 +14,7 @@ def register_routers(app: FastAPI) -> None:
1314
1415
## Returns:
1516
None
16-
17+
1718
## Example
1819
```python
1920
from fastapi import FastAPI
@@ -39,3 +40,8 @@ def register_routers(app: FastAPI) -> None:
3940
router=users,
4041
prefix=settings.API_PREFIX,
4142
)
43+
44+
app.include_router(
45+
router=auth,
46+
prefix=settings.API_PREFIX,
47+
)

api_v1/users/mixins.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import jwt
2+
from fastapi_users.jwt import decode_jwt
3+
from fastapi_users import exceptions
4+
5+
from .exceptions import UserNotVerified, PasswordNotValidError
6+
7+
8+
class AuthenticationUserManagerMixin:
9+
"""
10+
Миксин добавляющий аутентификацию в логику менеджера
11+
"""
12+
13+
async def authenticate_user(self, token: str | None):
14+
if not token:
15+
return
16+
try:
17+
data = decode_jwt(
18+
token,
19+
self.verification_token_secret,
20+
[self.verification_token_audience],
21+
)
22+
except jwt.PyJWTError:
23+
raise exceptions.InvalidVerifyToken()
24+
25+
try:
26+
user_id = data["sub"]
27+
email = data["email"]
28+
except KeyError:
29+
raise exceptions.InvalidVerifyToken()
30+
31+
try:
32+
user = await self.get_by_email(email)
33+
except exceptions.UserNotExists:
34+
return
35+
36+
try:
37+
parsed_id = self.parse_id(user_id)
38+
except exceptions.InvalidID:
39+
raise exceptions.InvalidVerifyToken()
40+
41+
if parsed_id != user.id:
42+
raise exceptions.InvalidVerifyToken()
43+
44+
if not user.is_verified:
45+
raise UserNotVerified()
46+
47+
return user
48+
49+
50+
class PasswordValidationMixin:
51+
"""
52+
Миксин добавляющий валидацию пароля
53+
"""
54+
55+
async def validate_password(self, password, user):
56+
if not len(password) > 7:
57+
raise PasswordNotValidError('Password is not valid')

api_v1/users/user_manager.py

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,17 @@
1-
import jwt
21
from fastapi_users import BaseUserManager, IntegerIDMixin
3-
from fastapi_users.jwt import decode_jwt
4-
from fastapi_users import exceptions
52

6-
from .exceptions import PasswordNotValidError, UserNotVerified
3+
from .mixins import AuthenticationUserManagerMixin, PasswordValidationMixin
74
from config.models import User
85
from config import settings
96

107

11-
class UserManager(IntegerIDMixin, BaseUserManager[User, int]):
8+
class UserManager(AuthenticationUserManagerMixin,
9+
PasswordValidationMixin,
10+
IntegerIDMixin,
11+
BaseUserManager[User, int]):
1212
"""
1313
UserManager для работы с пользователем
1414
"""
1515

1616
verification_token_secret = settings.SECRET
17-
18-
async def validate_password(self, password, user):
19-
if not len(password) > 7:
20-
raise PasswordNotValidError('Password is not valid')
21-
22-
async def authenticate_user(self, token: str | None):
23-
if not token:
24-
return
25-
try:
26-
data = decode_jwt(
27-
token,
28-
self.verification_token_secret,
29-
[self.verification_token_audience],
30-
)
31-
except jwt.PyJWTError:
32-
raise exceptions.InvalidVerifyToken()
33-
34-
try:
35-
user_id = data["sub"]
36-
email = data["email"]
37-
except KeyError:
38-
raise exceptions.InvalidVerifyToken()
39-
40-
try:
41-
user = await self.get_by_email(email)
42-
except exceptions.UserNotExists:
43-
return
44-
45-
try:
46-
parsed_id = self.parse_id(user_id)
47-
except exceptions.InvalidID:
48-
raise exceptions.InvalidVerifyToken()
49-
50-
if parsed_id != user.id:
51-
raise exceptions.InvalidVerifyToken()
52-
53-
if not user.is_verified:
54-
raise UserNotVerified()
55-
56-
return user
17+
reset_password_token_lifetime_seconds = settings.RESET_LIFESPAN_TOKEN_SECONDS

api_v1/users/views.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
from fastapi import APIRouter, Depends, status, Request
2-
from sqlalchemy.ext.asyncio import AsyncSession
3-
4-
from config import db_connection
5-
from api_v1.exeptions import ValidationError
1+
from fastapi import APIRouter, Request
62

73

84
router = APIRouter(prefix='/users',

config/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ class Settings(BaseSettings):
9797
LOG_DIR: Path = log_dir
9898
CURRENT_ORIGIN: str = config('CURRENT_ORIGIN')
9999
SECRET: str = config('SECRET')
100+
RESET_LIFESPAN_TOKEN_SECONDS: int = 3600
100101

101102

102103
settings = Settings()

0 commit comments

Comments
 (0)