This commit is contained in:
2025-08-05 12:10:56 +03:00
parent 18b13fdb57
commit a5c512c7d4
5 changed files with 56 additions and 18 deletions

View File

@ -5,29 +5,66 @@ from psycopg2._psycopg import connection
import db.accounts as db import db.accounts as db
import settings.settings as settings import settings.settings as settings
from api.utils import get_password_hash from api.utils import encrypt_str, get_current_user
from api.models import User, Account
from db.internal import get_db_connection from db.internal import get_db_connection
accounts_router = APIRouter(prefix="/api/accounts", tags=["anon"]) accounts_router = APIRouter(prefix="/api/accounts", tags=["anon"])
@accounts_router.post("/add/user") @accounts_router.post("/account")
async def get_account(
account_id: int,
conn: Annotated[connection, Depends(get_db_connection)],
current_user: Annotated[User, Depends(get_current_user)]
):
if current_user.role not in settings.settings.admin_roles:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not allowed",
)
account_data = db.get_account(conn, account_id)
if account_data is None:
return HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="No such account",
)
account = Account()
account.fill(account_data)
return account
@accounts_router.post("/add")
async def add_account( async def add_account(
platform: str, platform: str,
login: str, login: str,
password: str, password: str,
metadata: dict, metadata: dict,
conn: Annotated[connection, Depends(get_db_connection)] conn: Annotated[connection, Depends(get_db_connection)],
current_user: Annotated[User, Depends(get_current_user)]
): ):
if not settings.settings.allow_create_users:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not allowed",
)
if db.check_account_existence(conn, platform, login, password): if db.check_account_existence(conn, platform, login, password):
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, status_code=status.HTTP_409_CONFLICT,
detail="Account already exists", detail="Account already exists",
) )
hashed_password = get_password_hash(password) hashed_password = encrypt_str(password)
return db.create_user(conn, username, hashed_password, "user") return db.create_account(conn, login, hashed_password, metadata)
# TODO: add author to check editing rights?
@accounts_router.post("/update")
async def update_account(
account_id: int,
platform: str,
login: str,
password: str,
metadata: dict,
conn: Annotated[connection, Depends(get_db_connection)],
current_user: Annotated[User, Depends(get_current_user)]
):
if current_user.role not in settings.settings.admin_roles:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not allowed",
)
return db.update_account(conn, account_id, platform, login, password, metadata)

View File

@ -1,6 +1,7 @@
from datetime import datetime from datetime import datetime
from pydantic import BaseModel from pydantic import BaseModel
from api.utils import decrypt_str
class Token(BaseModel): class Token(BaseModel):
@ -103,6 +104,7 @@ class Account(BaseModel):
self.id = params["id"] self.id = params["id"]
self.platform = params["platform"] self.platform = params["platform"]
self.login = params["login"] self.login = params["login"]
self.password = decrypt_str(params["password"])
self.metadata = params["metadata"] self.metadata = params["metadata"]
self.created_at = params["created_at"] self.created_at = params["created_at"]
id: int = -1 id: int = -1

View File

@ -4,7 +4,6 @@ from typing import Annotated
import bcrypt import bcrypt
import jwt import jwt
from Crypto.Cipher import AES from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad from Crypto.Util.Padding import pad, unpad
from fastapi import Depends, HTTPException, status from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer from fastapi.security import OAuth2PasswordBearer
@ -20,14 +19,14 @@ from db.internal import get_db_connection
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def encrypt_str(string, key_hex): def encrypt_str(string):
key = bytes.fromhex(key_hex) key = bytes.fromhex(startup_settings.secret_key)
cipher = AES.new(key, AES.MODE_ECB) # Простейший режим (небезопасно для больших данных!) cipher = AES.new(key, AES.MODE_ECB)
encrypted_string = cipher.encrypt(pad(string.encode(), AES.block_size)) encrypted_string = cipher.encrypt(pad(string.encode(), AES.block_size))
return encrypted_string return encrypted_string
def decrypt_str(encrypted_string, key_hex): def decrypt_str(encrypted_string):
key = bytes.fromhex(key_hex) key = bytes.fromhex(startup_settings.secret_key)
cipher = AES.new(key, AES.MODE_ECB) cipher = AES.new(key, AES.MODE_ECB)
string = unpad(cipher.decrypt(encrypted_string), AES.block_size) string = unpad(cipher.decrypt(encrypted_string), AES.block_size)
return string.decode() return string.decode()

View File

@ -23,8 +23,7 @@ def create_account(
""", """,
(platform, login, password, json.dumps(metadata)), (platform, login, password, json.dumps(metadata)),
) )
conn.commit() return conn.commit()
return cur.rowcount > 0
def delete_account( def delete_account(

View File

@ -68,6 +68,7 @@ create table picrinth.swipes (
constraint swipes_unique unique (username, feed_id, picture_id) constraint swipes_unique unique (username, feed_id, picture_id)
); );
-- TODO: add author?
create table picrinth.accounts ( create table picrinth.accounts (
id serial not null, id serial not null,
platform text not null, platform text not null,