feat: v1.7 overhaul - identity hash, triple wallet, financial ledger, and security audit system

This commit is contained in:
2026-02-16 00:42:49 +00:00
parent bb02d4ed59
commit d574d3297d
63 changed files with 3710 additions and 565 deletions

View File

@@ -112,25 +112,23 @@ class AuthService:
async def complete_kyc(db: AsyncSession, user_id: int, kyc_in: UserKYCComplete):
"""
Step 2: Atomi Tranzakció.
Itt dől el minden: Adatok rögzítése, Shadow Identity ellenőrzés,
Flotta és Wallet létrehozás, majd a fiók aktiválása.
Módosított verzió: Meglévő biztonsági logika + Telephely (Branch) integráció.
"""
try:
# 1. User és Person betöltése
stmt = select(User).options(joinedload(User.person)).where(User.id == user_id)
res = await db.execute(stmt)
user = res.scalar_one_or_none()
if not user: return None
# --- 1. BIZTONSÁG: User folder_slug generálása ---
# Ha Google-lel jött vagy még nincs slugja, most kap egyet.
# --- BIZTONSÁG: Slug generálása ---
if not user.folder_slug:
user.folder_slug = generate_secure_slug(length=12)
# Pénznem beállítása
if hasattr(kyc_in, 'preferred_currency') and kyc_in.preferred_currency:
user.preferred_currency = kyc_in.preferred_currency
# --- 2. Shadow Identity keresése (Már létezik-e ez a fizikai személy?) ---
# --- SHADOW IDENTITY ELLENŐRZÉS ---
identity_stmt = select(Person).where(and_(
Person.mothers_last_name == kyc_in.mothers_last_name,
Person.mothers_first_name == kyc_in.mothers_first_name,
@@ -140,15 +138,12 @@ class AuthService:
existing_person = (await db.execute(identity_stmt)).scalar_one_or_none()
if existing_person:
# Ha találtunk egyezést, összekötjük a User-t a meglévő Person-nel
user.person_id = existing_person.id
active_person = existing_person
logger.info(f"Shadow Identity linked: User {user_id} -> Person {existing_person.id}")
else:
# Ha nem, a saját (regisztrációkor létrehozott) Person-t töltjük fel
active_person = user.person
# --- 3. Cím rögzítése GeoService segítségével ---
# --- CÍM RÖGZÍTÉSE ---
addr_id = await GeoService.get_or_create_full_address(
db,
zip_code=kyc_in.address_zip,
@@ -159,30 +154,26 @@ class AuthService:
parcel_id=kyc_in.address_hrsz
)
# --- 4. Személyes adatok frissítése ---
# --- SZEMÉLYES ADATOK FRISSÍTÉSE ---
active_person.mothers_last_name = kyc_in.mothers_last_name
active_person.mothers_first_name = kyc_in.mothers_first_name
active_person.birth_place = kyc_in.birth_place
active_person.birth_date = kyc_in.birth_date
active_person.phone = kyc_in.phone_number
active_person.address_id = addr_id
# Dokumentumok és ICE kontakt mentése JSON-ként
active_person.identity_docs = jsonable_encoder(kyc_in.identity_docs)
active_person.ice_contact = jsonable_encoder(kyc_in.ice_contact)
# A Person most válik aktívvá
active_person.is_active = True
# --- 5. EGYÉNI FLOTTA LÉTREHOZÁSA (A KYC szerves része) ---
# Itt generáljuk a flotta mappáját is (folder_slug)
# --- EGYÉNI FLOTTA LÉTREHOZÁSA ---
new_org = Organization(
full_name=f"{active_person.last_name} {active_person.first_name} Egyéni Flotta",
name=f"{active_person.last_name} Flotta",
folder_slug=generate_secure_slug(length=12), # FLOTTA SLUG
folder_slug=generate_secure_slug(length=12),
org_type=OrgType.individual,
owner_id=user.id,
is_transferable=False,
is_transferable=False, # Step 2: Individual flotta nem átruházható
is_ownership_transferable=False, # A te új meződ
is_active=True,
status="verified",
language=user.preferred_language,
@@ -192,24 +183,36 @@ class AuthService:
db.add(new_org)
await db.flush()
# Flotta tagság (Owner)
# --- ÚJ: MAIN BRANCH (KÖZPONTI TELEPHELY) LÉTREHOZÁSA ---
# Magánszemélynél a megadott cím lesz az első telephely is.
from app.models.address import Branch
new_branch = Branch(
organization_id=new_org.id,
address_id=addr_id,
name="Központ / Otthon",
is_main=True,
postal_code=kyc_in.address_zip,
city=kyc_in.address_city,
street_name=kyc_in.address_street_name,
street_type=kyc_in.address_street_type,
house_number=kyc_in.address_house_number,
hrsz=kyc_in.address_hrsz,
status="active"
)
db.add(new_branch)
await db.flush()
# --- TAGSÁG, WALLET, STATS ---
db.add(OrganizationMember(
organization_id=new_org.id,
user_id=user.id,
role="owner",
permissions={"can_add_asset": True, "can_view_costs": True, "is_admin": True}
))
# --- 6. PÉNZTÁRCA ÉS GAMIFICATION LÉTREHOZÁSA ---
db.add(Wallet(
user_id=user.id,
coin_balance=0,
credit_balance=0,
currency=user.preferred_currency or "HUF"
))
db.add(Wallet(user_id=user.id, currency=user.preferred_currency or "HUF"))
db.add(UserStats(user_id=user.id, total_xp=0, current_level=1))
# --- 7. AKTIVÁLÁS ÉS AUDIT ---
# --- 7. AKTIVÁLÁS ÉS AUDIT (Ami az előzőből kimaradt) ---
user.is_active = True
await security_service.log_event(
@@ -223,7 +226,7 @@ class AuthService:
"status": "active",
"user_folder": user.folder_slug,
"organization_id": new_org.id,
"organization_folder": new_org.folder_slug,
"branch_id": str(new_branch.id), # Új telephely az auditban
"wallet_created": True
}
)
@@ -231,6 +234,7 @@ class AuthService:
await db.commit()
await db.refresh(user)
return user
except Exception as e:
await db.rollback()
logger.error(f"KYC Atomi Tranzakció Hiba: {str(e)}")

View File

@@ -45,22 +45,42 @@ class GeoService:
"""), {"n": street_type.lower()})
# 4. Központi Address rekord rögzítése
full_text = f"{zip_code} {city}, {street_name} {street_type} {house_number}"
addr_res = await db.execute(text("""
INSERT INTO data.addresses (postal_code_id, street_name, street_type, house_number, parcel_id, full_address_text)
VALUES (:zid, :sn, :st, :hn, :pid, :txt)
full_text = f"{zip_code} {city}, {street_name} {street_type} {house_number}."
if stairwell: full_text += f" {stairwell}. lph,"
if floor: full_text += f" {floor}. em,"
if door: full_text += f" {door}. ajtó"
query = text("""
INSERT INTO data.addresses (
postal_code_id, street_name, street_type, house_number,
stairwell, floor, door, parcel_id, full_address_text
)
VALUES (
(SELECT id FROM data.geo_postal_codes WHERE zip_code = :z AND city = :c LIMIT 1),
:sn, :st, :hn, :sw, :fl, :dr, :pid, :txt
)
ON CONFLICT DO NOTHING
RETURNING id
"""), {
"zid": zip_id, "sn": street_name, "st": street_type, "hn": house_number, "pid": parcel_id, "txt": full_text
})
addr_id = addr_res.scalar()
""")
params = {
"z": zip_code, "c": city, "sn": street_name, "st": street_type,
"hn": house_number, "sw": stairwell, "fl": floor, "dr": door,
"pid": parcel_id, "txt": full_text
}
res = await db.execute(query, params)
addr_id = res.scalar()
if not addr_id:
# Ha már létezett, lekérjük az azonosítót
# Ha már létezett ilyen részletes cím, lekérjük
addr_id = (await db.execute(text("""
SELECT id FROM data.addresses
WHERE postal_code_id = :zid AND street_name = :sn AND street_type = :st AND house_number = :hn
"""), {"zid": zip_id, "sn": street_name, "st": street_type, "hn": house_number})).scalar()
WHERE street_name = :sn AND house_number = :hn
AND (stairwell IS NOT DISTINCT FROM :sw)
AND (floor IS NOT DISTINCT FROM :fl)
AND (door IS NOT DISTINCT FROM :dr)
LIMIT 1
"""), params)).scalar()
return addr_id