Skip to content

Commit 26ca7dd

Browse files
committed
add crud users
1 parent b3eaa85 commit 26ca7dd

13 files changed

Lines changed: 146 additions & 111 deletions

File tree

api_v1/auth/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from .permissions import active_user, superuser
2+
3+
4+
__all__ = ('active_user',
5+
'superuser',
6+
)

api_v1/auth/backends.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,34 @@
11
from fastapi_users.authentication import (
22
BearerTransport,
33
AuthenticationBackend,
4+
Authenticator,
45
JWTStrategy,
56
)
67

8+
from api_v1.users.user_manager import get_user_manager
79
from config import settings
810

911

10-
bearer_transport = BearerTransport(settings.API_PREFIX +
12+
bearer_transport = BearerTransport(settings.CURRENT_ORIGIN +
13+
settings.API_PREFIX +
1114
settings.JWT.JWT_PATH +
1215
'/login')
1316

1417

1518
def get_jwt_strategy() -> JWTStrategy:
16-
return JWTStrategy(secret=settings.JWT.SECRET,
17-
lifetime_seconds=settings.JWT.RESET_LIFESPAN_TOKEN_SECONDS,
18-
)
19+
return JWTStrategy(
20+
secret=settings.JWT.SECRET,
21+
lifetime_seconds=settings.JWT.RESET_LIFESPAN_TOKEN_SECONDS,
22+
)
1923

2024

2125
auth_backend = AuthenticationBackend(
2226
name=settings.JWT.NAME,
2327
transport=bearer_transport,
2428
get_strategy=get_jwt_strategy,
2529
)
30+
31+
authenticator = Authenticator(
32+
(auth_backend,),
33+
get_user_manager,
34+
)

api_v1/auth/permissions.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from .backends import authenticator
2+
3+
4+
active_user = authenticator.current_user(
5+
active=True,
6+
verified=True,
7+
)
8+
9+
superuser = authenticator.current_user(
10+
active=True,
11+
verified=True,
12+
superuser=True,
13+
)

api_v1/auth/tokens.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from fastapi_users.authentication.strategy import JWTStrategy
2+
from fastapi_users.jwt import generate_jwt
3+
4+
5+
class JWTStrategyWithEmail(JWTStrategy):
6+
"""
7+
JWT стратегия с дополнительными полями
8+
"""
9+
10+
async def write_token(self, user):
11+
data = {"sub": str(user.id),
12+
"aud": self.token_audience,
13+
"email": str(user.email)}
14+
return generate_jwt(
15+
data,
16+
self.encode_key,
17+
self.lifetime_seconds,
18+
algorithm=self.algorithm,
19+
)

api_v1/auth/views.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from config.models import User
55
from config import settings
66
from .backends import auth_backend
7-
from .schemas import UserRead, UserCreate, UserUpdate
7+
from .schemas import UserRead, UserCreate
88
from api_v1.users.user_manager import get_user_manager
99

1010

@@ -31,11 +31,3 @@
3131
tags=['Auth'],
3232
prefix='/auth',
3333
)
34-
router.include_router(fastapi_users.get_reset_password_router(),
35-
tags=['Auth'],
36-
prefix='/auth',
37-
)
38-
router.include_router(fastapi_users.get_users_router(UserRead, UserUpdate),
39-
tags=['Users'],
40-
prefix='/users',
41-
)

api_v1/users/exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class PasswordNotValidError(FastAPIUsersException):
1111

1212
class UserNotVerified(FastAPIUsersException):
1313
"""
14-
Исключение не вурифицинованного пользователя
14+
Исключение не верифицинованного пользователя
1515
"""
1616

1717
pass

api_v1/users/middlewares.py

Lines changed: 0 additions & 25 deletions
This file was deleted.

api_v1/users/mixins.py

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,66 @@
1-
import jwt
2-
from fastapi_users.jwt import decode_jwt
3-
from fastapi_users import exceptions
1+
from fastapi import status
2+
from loguru import logger
43

5-
from .exceptions import UserNotVerified, PasswordNotValidError
4+
from .exceptions import PasswordNotValidError
65

76

8-
class AuthenticationUserManagerMixin:
7+
class ActionUserManagerMixin:
98
"""
10-
Миксин добавляющий аутентификацию в логику менеджера
9+
Миксин для поддержки методов UserManager которые отвечают
10+
за дополнительную логику
1111
"""
12+
async def on_after_request_verify(self, user, token, request):
13+
"""
14+
Здесь должна быть отправка на E-mail `token` который уже
15+
вмещает в себя дополнительное поле `email` для верификации.
16+
Этот токен нужно вписать в end-point `verify`.
1217
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()
18+
Для эмуляции отправки сюда в консоль выведется сам токен.
2419
25-
try:
26-
user_id = data["sub"]
27-
email = data["email"]
28-
except KeyError:
29-
raise exceptions.InvalidVerifyToken()
20+
Если в консоли вы не видете токена значит:
21+
- Вы уже верифицированы
22+
- Не правильные данные
23+
- Не активный пользователь
3024
31-
try:
32-
user = await self.get_by_email(email)
33-
except exceptions.UserNotExists:
34-
return
25+
Args:
26+
user (_type_): пользователь
27+
token (_type_): Токен с email
28+
request (_type_): сущность request
29+
"""
30+
logger.warning('TOKEN VERIFY ^^^^^------^^^^^ TOKEN VERIFY\n')
31+
logger.warning(token)
32+
logger.warning('\nEND TOKEN ^^^^^------^^^^^ END TOKEN')
3533

36-
try:
37-
parsed_id = self.parse_id(user_id)
38-
except exceptions.InvalidID:
39-
raise exceptions.InvalidVerifyToken()
34+
async def on_after_forgot_password(self, user, token, request):
35+
"""
36+
Здесь должна быть отправка на E-mail `token` который уже
37+
вмещает в себя дополнительное поле `password_fgpt` для верификации.
38+
Этот токен нужно вписать в end-point `reset-password`.
4039
41-
if parsed_id != user.id:
42-
raise exceptions.InvalidVerifyToken()
40+
Для эмуляции отправки сюда в консоль выведется сам токен.
4341
44-
if not user.is_verified:
45-
raise UserNotVerified()
42+
Если в консоли вы не видете токена значит:
43+
- Не правильные данные
44+
- Не активный пользователь
4645
47-
return user
46+
Args:
47+
user (_type_): пользователь
48+
token (_type_): Токен с password_fgpt
49+
request (_type_): сущность request
50+
"""
51+
logger.warning('TOKEN RESET ^^^^^------^^^^^ TOKEN RESET\n')
52+
logger.warning(token)
53+
logger.warning('\nEND TOKEN ^^^^^------^^^^^ END TOKEN')
54+
55+
async def on_after_reset_password(self, user, request):
56+
"""
57+
Здесь должна быть отправка на E-mail cообщения о изменения пароля.
58+
59+
Args:
60+
user (_type_): пользователь
61+
request (_type_): сущность request
62+
"""
63+
return await super().on_after_reset_password(user, request)
4864

4965

5066
class PasswordValidationMixin:
@@ -54,4 +70,7 @@ class PasswordValidationMixin:
5470

5571
async def validate_password(self, password, user):
5672
if not len(password) > 7:
57-
raise PasswordNotValidError('Password is not valid')
73+
raise PasswordNotValidError(
74+
status_code=status.HTTP_400_BAD_REQUEST,
75+
detail='Password is to short',
76+
)

api_v1/users/user_manager.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
from fastapi_users.db import SQLAlchemyUserDatabase
44
from sqlalchemy.ext.asyncio import AsyncSession
55

6-
from .mixins import AuthenticationUserManagerMixin, PasswordValidationMixin
6+
from .mixins import ActionUserManagerMixin, PasswordValidationMixin
77
from config.models import User
88
from config import settings
99
from config import db_connection
1010

1111

12-
class UserManager(AuthenticationUserManagerMixin,
12+
class UserManager(ActionUserManagerMixin,
1313
PasswordValidationMixin,
1414
IntegerIDMixin,
1515
BaseUserManager[User, int]):
@@ -18,6 +18,9 @@ class UserManager(AuthenticationUserManagerMixin,
1818
"""
1919

2020
verification_token_secret = settings.JWT.SECRET
21+
verification_token_audience = 'fastapi-users:auth'
22+
23+
reset_password_token_secret = settings.JWT.SECRET
2124
reset_password_token_lifetime_seconds = settings.JWT.RESET_LIFESPAN_TOKEN_SECONDS
2225

2326

api_v1/users/views.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,31 @@
1-
from fastapi import APIRouter, Request
1+
from fastapi import APIRouter, Depends
2+
from fastapi_users import FastAPIUsers
23

4+
from config.models import User
5+
from api_v1.auth.backends import auth_backend
6+
from api_v1.auth import active_user
7+
from api_v1.auth.schemas import UserRead, UserUpdate
8+
from api_v1.users.user_manager import get_user_manager
39

4-
router = APIRouter(prefix='/users',
5-
tags=['Users'],
6-
)
710

11+
fastapi_users = FastAPIUsers[User, int](
12+
get_user_manager,
13+
(auth_backend,)
14+
)
815

9-
@router.get(path='/get',
10-
description='Test end point',
16+
17+
router = APIRouter()
18+
19+
20+
@router.get(path='/test',
21+
response_model=UserRead,
22+
dependencies=[Depends(active_user)],
1123
)
12-
async def get_user(request: Request):
13-
return request.user
24+
async def test_end_point(user: User = Depends(active_user)):
25+
return user
26+
27+
28+
router.include_router(fastapi_users.get_users_router(UserRead, UserUpdate),
29+
tags=['Users'],
30+
prefix='/users',
31+
)

0 commit comments

Comments
 (0)