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