feat: create new user with password
This commit is contained in:
parent
22064d2b52
commit
692461e266
@ -6,9 +6,10 @@ from typing import Optional
|
||||
from sqlalchemy import func, insert, select
|
||||
from sqlalchemy.ext.asyncio import AsyncConnection
|
||||
|
||||
from api.db.logic.keyring import create_password_key
|
||||
from api.db.tables.account import account_table
|
||||
from api.schemas.account.account import User
|
||||
from api.schemas.endpoints.account import all_user_adapter, AllUserResponse, UserUpdate
|
||||
from api.schemas.endpoints.account import all_user_adapter, AllUser, AllUserResponse, UserCreate, UserUpdate
|
||||
|
||||
|
||||
async def get_user_accaunt_page(connection: AsyncConnection, page, limit) -> Optional[AllUserResponse]:
|
||||
@ -54,7 +55,7 @@ async def get_user_accaunt_page(connection: AsyncConnection, page, limit) -> Opt
|
||||
)
|
||||
|
||||
|
||||
async def get_user_by_id(connection: AsyncConnection, user_id: int) -> Optional[UserUpdate]:
|
||||
async def get_user_by_id(connection: AsyncConnection, user_id: int) -> Optional[AllUser]:
|
||||
"""
|
||||
Получает юзера по id.
|
||||
"""
|
||||
@ -66,7 +67,7 @@ async def get_user_by_id(connection: AsyncConnection, user_id: int) -> Optional[
|
||||
if not user:
|
||||
return None
|
||||
|
||||
return UserUpdate.model_validate(user)
|
||||
return AllUser.model_validate(user)
|
||||
|
||||
|
||||
async def get_user_by_login(connection: AsyncConnection, login: str) -> Optional[User]:
|
||||
@ -102,7 +103,7 @@ async def update_user_by_id(connection: AsyncConnection, update_values, user) ->
|
||||
await connection.commit()
|
||||
|
||||
|
||||
async def create_user(connection: AsyncConnection, user: UserUpdate, creator_id: int) -> Optional[UserUpdate]:
|
||||
async def create_user(connection: AsyncConnection, user: UserCreate, creator_id: int) -> Optional[AllUser]:
|
||||
"""
|
||||
Создает нове поле в таблице account_table.
|
||||
"""
|
||||
@ -112,7 +113,7 @@ async def create_user(connection: AsyncConnection, user: UserUpdate, creator_id:
|
||||
email=user.email,
|
||||
bind_tenant_id=user.bind_tenant_id,
|
||||
role=user.role.value,
|
||||
meta=user.meta,
|
||||
meta=user.meta or {},
|
||||
creator_id=creator_id,
|
||||
created_at=datetime.now(timezone.utc),
|
||||
status=user.status.value,
|
||||
@ -121,6 +122,7 @@ async def create_user(connection: AsyncConnection, user: UserUpdate, creator_id:
|
||||
res = await connection.execute(query)
|
||||
|
||||
await connection.commit()
|
||||
user = await get_user_by_id(connection, res.lastrowid)
|
||||
new_user = await get_user_by_id(connection, res.lastrowid)
|
||||
await create_password_key(connection, user.password, new_user.id)
|
||||
|
||||
return user
|
||||
return new_user
|
||||
|
@ -8,13 +8,14 @@ from api.db.tables.account import account_table, account_keyring_table, KeyType,
|
||||
|
||||
from api.schemas.account.account import User
|
||||
from api.schemas.account.account_keyring import AccountKeyring
|
||||
from api.schemas.endpoints.account import AllUser
|
||||
|
||||
from api.utils.key_id_gen import KeyIdGenerator
|
||||
|
||||
from datetime import datetime, timezone
|
||||
|
||||
|
||||
async def get_user(connection: AsyncConnection, login: str) -> Optional[User]:
|
||||
async def get_user(connection: AsyncConnection, login: str) -> tuple[Optional[AllUser], Optional[AccountKeyring]]:
|
||||
query = (
|
||||
select(account_table, account_keyring_table)
|
||||
.join(account_keyring_table, account_table.c.id == account_keyring_table.c.owner_id)
|
||||
@ -45,7 +46,7 @@ async def get_user(connection: AsyncConnection, login: str) -> Optional[User]:
|
||||
for column in account_keyring_table.columns
|
||||
}
|
||||
|
||||
user = User.model_validate(user_data)
|
||||
user = AllUser.model_validate(user_data)
|
||||
password = AccountKeyring.model_validate(password_data)
|
||||
return user, password
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
from typing import Optional
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import insert, select
|
||||
from sqlalchemy.ext.asyncio import AsyncConnection
|
||||
|
||||
from api.db.tables.account import account_keyring_table
|
||||
|
||||
from api.db.tables.account import account_keyring_table, KeyStatus, KeyType
|
||||
from api.schemas.account.account_keyring import AccountKeyring
|
||||
from api.utils.hasher import hasher
|
||||
from api.utils.key_id_gen import KeyIdGenerator
|
||||
|
||||
|
||||
async def get_key_by_id(connection: AsyncConnection, key_id: str) -> Optional[AccountKeyring]:
|
||||
@ -67,3 +68,19 @@ async def create_key(connection: AsyncConnection, key: AccountKeyring, key_id: i
|
||||
await connection.commit()
|
||||
|
||||
return key
|
||||
|
||||
|
||||
async def create_password_key(connection: AsyncConnection, password: str | None, owner_id: int):
|
||||
if password is None:
|
||||
password = hasher.generate_password()
|
||||
stmt = insert(account_keyring_table).values(
|
||||
owner_id=owner_id,
|
||||
key_type=KeyType.PASSWORD.value,
|
||||
key_id=KeyIdGenerator(),
|
||||
key_value=hasher.hash_data(password),
|
||||
created_at=datetime.now(timezone.utc),
|
||||
expiry=datetime.now(timezone.utc) + timedelta(days=365),
|
||||
status=KeyStatus.ACTIVE.value,
|
||||
)
|
||||
await connection.execute(stmt)
|
||||
await connection.commit()
|
||||
|
@ -17,7 +17,7 @@ from api.db.logic.account import (
|
||||
from api.db.tables.account import AccountStatus
|
||||
from api.schemas.account.account import User
|
||||
from api.schemas.base import bearer_schema
|
||||
from api.schemas.endpoints.account import AllUserResponse, UserUpdate
|
||||
from api.schemas.endpoints.account import AllUser, AllUserResponse, UserCreate, UserUpdate
|
||||
from api.services.auth import get_current_user
|
||||
from api.services.update_data_validation import update_user_data_changes
|
||||
from api.services.user_role_validation import db_user_role_validation
|
||||
@ -61,9 +61,9 @@ async def get_account(
|
||||
return user
|
||||
|
||||
|
||||
@api_router.post("", dependencies=[Depends(bearer_schema)], response_model=UserUpdate)
|
||||
@api_router.post("", dependencies=[Depends(bearer_schema)], response_model=AllUser)
|
||||
async def create_account(
|
||||
user: UserUpdate,
|
||||
user: UserCreate,
|
||||
connection: AsyncConnection = Depends(get_connection_dep),
|
||||
current_user=Depends(get_current_user),
|
||||
):
|
||||
|
@ -1,9 +1,9 @@
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import EmailStr, Field, TypeAdapter
|
||||
|
||||
from api.db.tables.account import AccountRole, AccountStatus
|
||||
|
||||
from api.schemas.base import Base
|
||||
|
||||
|
||||
@ -20,6 +20,17 @@ class UserUpdate(Base):
|
||||
status: Optional[AccountStatus] = None
|
||||
|
||||
|
||||
class UserCreate(Base):
|
||||
name: Optional[str] = Field(None, max_length=100)
|
||||
login: Optional[str] = Field(None, max_length=100)
|
||||
email: Optional[EmailStr] = None
|
||||
password: Optional[str] = None
|
||||
bind_tenant_id: Optional[str] = Field(None, max_length=40)
|
||||
role: Optional[AccountRole] = None
|
||||
meta: Optional[dict] = None
|
||||
status: Optional[AccountStatus] = None
|
||||
|
||||
|
||||
class AllUser(Base):
|
||||
id: int
|
||||
name: str
|
||||
|
@ -1,27 +1,25 @@
|
||||
from fastapi import Request, HTTPException
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import HTTPException, Request
|
||||
from sqlalchemy.ext.asyncio import AsyncConnection
|
||||
|
||||
from api.db.logic.auth import get_user
|
||||
|
||||
# # from backend.schemas.users.token import TokenData
|
||||
from api.schemas.account.account import User
|
||||
from api.db.tables.account import AccountStatus
|
||||
|
||||
from api.utils.hasher import Hasher
|
||||
from api.schemas.endpoints.account import AllUser
|
||||
from api.utils.hasher import hasher
|
||||
|
||||
|
||||
async def get_current_user(request: Request) -> Optional[User]:
|
||||
async def get_current_user(request: Request) -> str | HTTPException:
|
||||
if not hasattr(request.state, "current_user"):
|
||||
return HTTPException(status_code=401, detail="Unauthorized")
|
||||
return request.state.current_user
|
||||
|
||||
|
||||
async def authenticate_user(connection: AsyncConnection, username: str, password: str) -> Optional[User]:
|
||||
async def authenticate_user(connection: AsyncConnection, username: str, password: str) -> Optional[AllUser]:
|
||||
sql_user, sql_password = await get_user(connection, username)
|
||||
|
||||
if not sql_user or sql_user.status != AccountStatus.ACTIVE:
|
||||
return None
|
||||
hasher = Hasher()
|
||||
if not hasher.verify_data(password, sql_password.key_value):
|
||||
return None
|
||||
return sql_user
|
||||
|
@ -1,4 +1,6 @@
|
||||
import hashlib
|
||||
import secrets
|
||||
|
||||
|
||||
# Хешер для работы с паролем.
|
||||
|
||||
@ -14,3 +16,10 @@ class Hasher:
|
||||
def verify_data(self, password: str, hashed: str) -> bool:
|
||||
# Проверяет пароль путем сравнения его хеша с сохраненным хешем.
|
||||
return self.hash_data(password) == hashed
|
||||
|
||||
@staticmethod
|
||||
def generate_password() -> str:
|
||||
return secrets.token_urlsafe(20)
|
||||
|
||||
|
||||
hasher = Hasher()
|
||||
|
@ -1,32 +1,23 @@
|
||||
import os
|
||||
import asyncio
|
||||
import hashlib
|
||||
import secrets
|
||||
import os
|
||||
|
||||
from api.db.connection.session import get_connection
|
||||
from api.db.tables.account import account_table, account_keyring_table, AccountRole, KeyType, KeyStatus
|
||||
from api.db.tables.account import account_keyring_table, account_table, AccountRole, KeyStatus, KeyType
|
||||
from api.utils.hasher import hasher
|
||||
from api.utils.key_id_gen import KeyIdGenerator
|
||||
|
||||
INIT_LOCK_FILE = "../init.lock"
|
||||
DEFAULT_LOGIN = "vorkout"
|
||||
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
return hashlib.sha256(password.encode()).hexdigest()
|
||||
|
||||
|
||||
def generate_password() -> str:
|
||||
return secrets.token_urlsafe(20)
|
||||
|
||||
|
||||
async def init():
|
||||
if os.path.exists(INIT_LOCK_FILE):
|
||||
print("Sorry, service is already initialized")
|
||||
return
|
||||
|
||||
async with get_connection() as conn:
|
||||
password = generate_password()
|
||||
hashed_password = hash_password(password)
|
||||
password = hasher.generate_password()
|
||||
hashed_password = hasher.hash_data(password)
|
||||
|
||||
create_user_query = account_table.insert().values(
|
||||
name=DEFAULT_LOGIN,
|
||||
|
@ -1,10 +1,9 @@
|
||||
import axios from 'axios';
|
||||
// import { Auth, Tokens } from '../types/auth';
|
||||
import axiosRetry from 'axios-retry';
|
||||
import { Auth, Tokens } from '@/types/auth';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { AuthService } from '@/services/authService';
|
||||
import { User, UserUpdate } from '@/types/user';
|
||||
import { User, UserCreate } from '@/types/user';
|
||||
|
||||
const baseURL = `${import.meta.env.VITE_APP_HTTP_PROTOCOL}://${
|
||||
import.meta.env.VITE_APP_API_URL
|
||||
@ -115,10 +114,13 @@ const api = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
async createUser(user: UserUpdate): Promise<User> {
|
||||
async createUser(user: UserCreate): Promise<User> {
|
||||
const response = await base.post<User>('/account', user);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// keyrings
|
||||
async setPassword(userId: number, password: string): Promise<any> {},
|
||||
};
|
||||
|
||||
export default api;
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useUserSelector } from '@/store/userStore';
|
||||
import { UserUpdate } from '@/types/user';
|
||||
import { UserCreate as NewUserCreate } from '@/types/user';
|
||||
import { UserService } from '@/services/userService';
|
||||
import { LoadingOutlined } from '@ant-design/icons';
|
||||
|
||||
@ -55,9 +55,10 @@ export default function UserCreate({ closeDrawer }: UserCreateProps) {
|
||||
const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) =>
|
||||
setFileList(newFileList);
|
||||
|
||||
const onFinish = async (values: UserUpdate) => {
|
||||
const onFinish = async (values: NewUserCreate) => {
|
||||
setLoading(true);
|
||||
await UserService.createUser(values);
|
||||
|
||||
closeDrawer();
|
||||
setLoading(false);
|
||||
message.info(t('createdAccountMessage'), 4);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import api from '@/api/api';
|
||||
import { AllUserResponse, User, UserUpdate } from '@/types/user';
|
||||
import { AllUserResponse, User, UserCreate } from '@/types/user';
|
||||
|
||||
export class UserService {
|
||||
static async getProfile(): Promise<User> {
|
||||
@ -24,7 +24,7 @@ export class UserService {
|
||||
return user;
|
||||
}
|
||||
|
||||
static async createUser(user: UserUpdate): Promise<User> {
|
||||
static async createUser(user: UserCreate): Promise<User> {
|
||||
console.log('createUser');
|
||||
const createdUser = api.createUser(user);
|
||||
return createdUser;
|
||||
|
Loading…
Reference in New Issue
Block a user