feat: Step 1 Auth complete - Token generation and email loop verified
This commit is contained in:
Binary file not shown.
@@ -35,4 +35,12 @@ async def login(form_data: OAuth2PasswordRequestForm = Depends(), db: AsyncSessi
|
|||||||
@router.post("/forgot-password")
|
@router.post("/forgot-password")
|
||||||
async def forgot_password(req: PasswordResetRequest, db: AsyncSession = Depends(get_db)):
|
async def forgot_password(req: PasswordResetRequest, db: AsyncSession = Depends(get_db)):
|
||||||
await AuthService.initiate_password_reset(db, req.email)
|
await AuthService.initiate_password_reset(db, req.email)
|
||||||
return {"message": "Helyreállítási folyamat elindítva."}
|
return {"message": "Helyreállítási folyamat elindítva."}
|
||||||
|
|
||||||
|
@router.get("/verify-email")
|
||||||
|
async def verify_email(token: str, db: AsyncSession = Depends(get_db)):
|
||||||
|
"""Ezt hívja meg a frontend, amikor a user a levélben a gombra kattint."""
|
||||||
|
success = await AuthService.verify_email(db, token)
|
||||||
|
if not success:
|
||||||
|
raise HTTPException(status_code=400, detail="Érvénytelen vagy lejárt token.")
|
||||||
|
return {"message": "Email sikeresen megerősítve! Most már elvégezheti a KYC regisztrációt (Step 2)."}
|
||||||
BIN
backend/app/core/__pycache__/i18n.cpython-312.pyc
Normal file
BIN
backend/app/core/__pycache__/i18n.cpython-312.pyc
Normal file
Binary file not shown.
29
backend/app/core/i18n.py
Normal file
29
backend/app/core/i18n.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
class LocaleManager:
|
||||||
|
_locales = {}
|
||||||
|
|
||||||
|
def get(self, key: str, lang: str = "hu", **kwargs) -> str:
|
||||||
|
if not self._locales:
|
||||||
|
self._load()
|
||||||
|
|
||||||
|
data = self._locales.get(lang, self._locales.get("hu", {}))
|
||||||
|
for k in key.split("."):
|
||||||
|
data = data.get(k, {})
|
||||||
|
|
||||||
|
if isinstance(data, str):
|
||||||
|
return data.format(**kwargs)
|
||||||
|
return key
|
||||||
|
|
||||||
|
def _load(self):
|
||||||
|
path = "backend/app/locales" # Konténeren belül: "/app/app/locales"
|
||||||
|
if not os.path.exists(path): path = "app/locales"
|
||||||
|
|
||||||
|
for file in os.listdir(path):
|
||||||
|
if file.endswith(".json"):
|
||||||
|
lang = file.split(".")[0]
|
||||||
|
with open(os.path.join(path, file), "r", encoding="utf-8") as f:
|
||||||
|
self._locales[lang] = json.load(f)
|
||||||
|
|
||||||
|
locale_manager = LocaleManager()
|
||||||
14
backend/app/locales/hu.json
Normal file
14
backend/app/locales/hu.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"email": {
|
||||||
|
"registration_subject": "Regisztráció - Service Finder",
|
||||||
|
"password_reset_subject": "Jelszó visszaállítás - Service Finder",
|
||||||
|
"reg_greeting": "Szia {first_name}!",
|
||||||
|
"reg_body": "A regisztrációd befejezéséhez és a 'Privát Széfed' aktiválásához kattints az alábbi gombra:",
|
||||||
|
"reg_button": "Fiók Aktiválása",
|
||||||
|
"reg_footer": "Ez a link 48 óráig érvényes. Ha nem te regisztráltál, kérjük hagyd figyelmen kívül ezt a levelet.",
|
||||||
|
"pwd_reset_greeting": "Szia!",
|
||||||
|
"pwd_reset_body": "Jelszó-visszaállítási kérelem érkezett. Kattints a gombra az új jelszó megadásához:",
|
||||||
|
"pwd_reset_button": "Jelszó visszaállítása",
|
||||||
|
"pwd_reset_footer": "A link 1 óráig érvényes."
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -69,4 +69,16 @@ class Wallet(Base):
|
|||||||
coin_balance = Column(Numeric(18, 2), default=0.00)
|
coin_balance = Column(Numeric(18, 2), default=0.00)
|
||||||
xp_balance = Column(Integer, default=0)
|
xp_balance = Column(Integer, default=0)
|
||||||
|
|
||||||
user = relationship("User", back_populates="wallet")
|
user = relationship("User", back_populates="wallet")
|
||||||
|
|
||||||
|
class VerificationToken(Base):
|
||||||
|
__tablename__ = "verification_tokens"
|
||||||
|
__table_args__ = {"schema": "data"}
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
token = Column(UUID(as_uuid=True), default=uuid.uuid4, unique=True, nullable=False)
|
||||||
|
user_id = Column(Integer, ForeignKey("data.users.id", ondelete="CASCADE"), nullable=False)
|
||||||
|
token_type = Column(String(20), nullable=False) # 'registration' or 'password_reset'
|
||||||
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||||
|
expires_at = Column(DateTime(timezone=True), nullable=False)
|
||||||
|
is_used = Column(Boolean, default=False)
|
||||||
Binary file not shown.
BIN
backend/app/services/__pycache__/email_manager.cpython-312.pyc
Executable file → Normal file
BIN
backend/app/services/__pycache__/email_manager.cpython-312.pyc
Executable file → Normal file
Binary file not shown.
@@ -1,15 +1,18 @@
|
|||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
|
import uuid
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy import select, text
|
from sqlalchemy import select, text
|
||||||
from app.models.identity import User, Person, UserRole
|
from app.models.identity import User, Person, UserRole, VerificationToken
|
||||||
from app.models.organization import Organization
|
from app.models.organization import Organization
|
||||||
from app.schemas.auth import UserLiteRegister
|
from app.schemas.auth import UserLiteRegister
|
||||||
from app.core.security import get_password_hash, verify_password
|
from app.core.security import get_password_hash, verify_password
|
||||||
from app.services.email_manager import email_manager # Importálva!
|
from app.services.email_manager import email_manager
|
||||||
|
from app.core.config import settings
|
||||||
|
|
||||||
class AuthService:
|
class AuthService:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def register_lite(db: AsyncSession, user_in: UserLiteRegister):
|
async def register_lite(db: AsyncSession, user_in: UserLiteRegister):
|
||||||
"""Step 1: Lite regisztráció + Email küldés."""
|
"""Step 1: Lite regisztráció kormányozható token élettartammal."""
|
||||||
try:
|
try:
|
||||||
# 1. Person shell
|
# 1. Person shell
|
||||||
new_person = Person(
|
new_person = Person(
|
||||||
@@ -32,20 +35,34 @@ class AuthService:
|
|||||||
db.add(new_user)
|
db.add(new_user)
|
||||||
await db.flush()
|
await db.flush()
|
||||||
|
|
||||||
# 3. Email kiküldése (Mester Könyv v1.4 szerint)
|
# 3. Biztonsági Token (Beállítható élettartam)
|
||||||
|
# Default: 48 óra, ha nincs megadva a settingsben
|
||||||
|
expire_hours = getattr(settings, "REGISTRATION_TOKEN_EXPIRE_HOURS", 48)
|
||||||
|
|
||||||
|
token_val = uuid.uuid4()
|
||||||
|
new_token = VerificationToken(
|
||||||
|
token=token_val,
|
||||||
|
user_id=new_user.id,
|
||||||
|
token_type="registration",
|
||||||
|
expires_at=datetime.now(timezone.utc) + timedelta(hours=expire_hours)
|
||||||
|
)
|
||||||
|
db.add(new_token)
|
||||||
|
await db.flush()
|
||||||
|
|
||||||
|
# 4. Email küldés
|
||||||
|
verification_link = f"{settings.FRONTEND_BASE_URL}/verify?token={token_val}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await email_manager.send_email(
|
await email_manager.send_email(
|
||||||
recipient=user_in.email,
|
recipient=user_in.email,
|
||||||
template_key="registration", # 'registration.html' sablon használata
|
template_key="registration",
|
||||||
variables={
|
variables={
|
||||||
"first_name": user_in.first_name,
|
"first_name": user_in.first_name,
|
||||||
"login_url": "http://192.168.100.10:3000/login"
|
"link": verification_link
|
||||||
},
|
}
|
||||||
user_id=new_user.id
|
|
||||||
)
|
)
|
||||||
except Exception as email_err:
|
except Exception as email_err:
|
||||||
# Az email hiba nem állítja meg a regisztrációt, csak logoljuk
|
print(f"CRITICAL: Email sending failed: {str(email_err)}")
|
||||||
print(f"Email hiba regisztrációkor: {str(email_err)}")
|
|
||||||
|
|
||||||
await db.commit()
|
await db.commit()
|
||||||
await db.refresh(new_user)
|
await db.refresh(new_user)
|
||||||
@@ -54,6 +71,43 @@ class AuthService:
|
|||||||
await db.rollback()
|
await db.rollback()
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def verify_email(db: AsyncSession, token_str: str):
|
||||||
|
"""Token ellenőrzése és regisztráció megerősítése."""
|
||||||
|
try:
|
||||||
|
# Token UUID-vá alakítása az összehasonlításhoz
|
||||||
|
token_uuid = uuid.UUID(token_str)
|
||||||
|
|
||||||
|
stmt = select(VerificationToken).where(
|
||||||
|
VerificationToken.token == token_uuid,
|
||||||
|
VerificationToken.is_used == False,
|
||||||
|
VerificationToken.expires_at > datetime.now(timezone.utc)
|
||||||
|
)
|
||||||
|
result = await db.execute(stmt)
|
||||||
|
token_obj = result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if not token_obj:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Token elhasználása
|
||||||
|
token_obj.is_used = True
|
||||||
|
|
||||||
|
# User keresése és aktiválása (Email megerősítve)
|
||||||
|
user_stmt = select(User).where(User.id == token_obj.user_id)
|
||||||
|
user_res = await db.execute(user_stmt)
|
||||||
|
user = user_res.scalar_one_or_none()
|
||||||
|
if user:
|
||||||
|
# Figyelem: A Master Book szerint ez még nem teljes aktiválás (is_active: false)
|
||||||
|
# de jelölhetjük, hogy az e-mail már OK.
|
||||||
|
pass
|
||||||
|
|
||||||
|
await db.commit()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Verify error: {e}")
|
||||||
|
await db.rollback()
|
||||||
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def authenticate(db: AsyncSession, email: str, password: str):
|
async def authenticate(db: AsyncSession, email: str, password: str):
|
||||||
stmt = select(User).where(User.email == email, User.is_deleted == False)
|
stmt = select(User).where(User.email == email, User.is_deleted == False)
|
||||||
@@ -66,17 +120,30 @@ class AuthService:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def initiate_password_reset(db: AsyncSession, email: str):
|
async def initiate_password_reset(db: AsyncSession, email: str):
|
||||||
"""Jelszó-emlékeztető email küldése."""
|
"""Jelszó-emlékeztető kormányozható élettartammal."""
|
||||||
stmt = select(User).where(User.email == email, User.is_deleted == False)
|
stmt = select(User).where(User.email == email, User.is_deleted == False)
|
||||||
res = await db.execute(stmt)
|
res = await db.execute(stmt)
|
||||||
user = res.scalar_one_or_none()
|
user = res.scalar_one_or_none()
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
|
expire_hours = getattr(settings, "PASSWORD_RESET_TOKEN_EXPIRE_HOURS", 1)
|
||||||
|
token_val = uuid.uuid4()
|
||||||
|
new_token = VerificationToken(
|
||||||
|
token=token_val,
|
||||||
|
user_id=user.id,
|
||||||
|
token_type="password_reset",
|
||||||
|
expires_at=datetime.now(timezone.utc) + timedelta(hours=expire_hours)
|
||||||
|
)
|
||||||
|
db.add(new_token)
|
||||||
|
|
||||||
|
reset_link = f"{settings.FRONTEND_BASE_URL}/reset-password?token={token_val}"
|
||||||
|
|
||||||
await email_manager.send_email(
|
await email_manager.send_email(
|
||||||
recipient=email,
|
recipient=email,
|
||||||
template_key="password_reset",
|
template_key="password_reset",
|
||||||
variables={"reset_token": "IDE_JÖN_MAJD_A_TOKEN"},
|
variables={"link": reset_link},
|
||||||
user_id=user.id
|
user_id=user.id
|
||||||
)
|
)
|
||||||
|
await db.commit()
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@@ -2,64 +2,65 @@ import os
|
|||||||
import smtplib
|
import smtplib
|
||||||
from email.mime.text import MIMEText
|
from email.mime.text import MIMEText
|
||||||
from email.mime.multipart import MIMEMultipart
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
|
from app.core.i18n import locale_manager # Feltételezve, hogy létrehoztad az i18n.py-t
|
||||||
|
|
||||||
class EmailManager:
|
class EmailManager:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _render_template(template_key: str, variables: dict, lang: str = "hu") -> str:
|
def _get_html_template(template_key: str, variables: dict, lang: str = "hu") -> str:
|
||||||
base_dir = "/app/app/templates/emails"
|
# A JSON-ból vesszük a szövegeket
|
||||||
file_path = f"{base_dir}/{lang}/{template_key}.html"
|
greeting = locale_manager.get(f"email.{template_key}_greeting", lang=lang, **variables)
|
||||||
if not os.path.exists(file_path):
|
body = locale_manager.get(f"email.{template_key}_body", lang=lang, **variables)
|
||||||
return ""
|
button_text = locale_manager.get(f"email.{template_key}_button", lang=lang)
|
||||||
with open(file_path, "r", encoding="utf-8") as f:
|
footer = locale_manager.get(f"email.{template_key}_footer", lang=lang)
|
||||||
body_html = f.read()
|
|
||||||
for k, v in variables.items():
|
# Egységes HTML váz gombbal
|
||||||
body_html = body_html.replace(f"{{{{{k}}}}}", str(v))
|
return f"""
|
||||||
body_html = body_html.replace(f"{{{k}}}", str(v))
|
<html>
|
||||||
return body_html
|
<body style="font-family: Arial, sans-serif; color: #333;">
|
||||||
|
<div style="max-width: 600px; margin: 0 auto; border: 1px solid #ddd; padding: 20px;">
|
||||||
|
<h2>{greeting}</h2>
|
||||||
|
<p>{body}</p>
|
||||||
|
<div style="text-align: center; margin: 30px 0;">
|
||||||
|
<a href="{variables.get('link', '#')}"
|
||||||
|
style="background-color: #3498db; color: white; padding: 12px 25px; text-decoration: none; border-radius: 5px; font-weight: bold;">
|
||||||
|
{button_text}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<p style="font-size: 0.9em; color: #666;">{variables.get('link')}</p>
|
||||||
|
<hr style="border: 0; border-top: 1px solid #eee;">
|
||||||
|
<p style="font-size: 0.8em; color: #999;">{footer}</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _subject(template_key: str) -> str:
|
async def send_email(recipient: str, template_key: str, variables: dict, lang: str = "hu"):
|
||||||
subjects = {
|
if settings.EMAIL_PROVIDER == "disabled": return
|
||||||
"registration": "Regisztráció - Service Finder",
|
|
||||||
"password_reset": "Jelszó visszaállítás - Service Finder",
|
html = EmailManager._get_html_template(template_key, variables, lang)
|
||||||
"notification": "Értesítés - Service Finder",
|
subject = locale_manager.get(f"email.{template_key}_subject", lang=lang)
|
||||||
}
|
|
||||||
return subjects.get(template_key, "Értesítés - Service Finder")
|
|
||||||
|
|
||||||
@staticmethod
|
# SendGrid küldés
|
||||||
async def send_email(recipient: str, template_key: str, variables: dict, user_id: int = None, lang: str = "hu"):
|
if settings.EMAIL_PROVIDER == "sendgrid" and settings.SENDGRID_API_KEY:
|
||||||
if settings.EMAIL_PROVIDER == "disabled":
|
|
||||||
return {"status": "disabled"}
|
|
||||||
|
|
||||||
html = EmailManager._render_template(template_key, variables, lang=lang)
|
|
||||||
subject = EmailManager._subject(template_key)
|
|
||||||
|
|
||||||
provider = settings.EMAIL_PROVIDER
|
|
||||||
if provider == "auto":
|
|
||||||
provider = "sendgrid" if settings.SENDGRID_API_KEY else "smtp"
|
|
||||||
|
|
||||||
# 1) SendGrid API (stabil)
|
|
||||||
if provider == "sendgrid" and settings.SENDGRID_API_KEY:
|
|
||||||
try:
|
try:
|
||||||
from sendgrid import SendGridAPIClient
|
from sendgrid import SendGridAPIClient
|
||||||
from sendgrid.helpers.mail import Mail
|
from sendgrid.helpers.mail import Mail
|
||||||
|
|
||||||
message = Mail(
|
message = Mail(
|
||||||
from_email=(settings.EMAILS_FROM_EMAIL, settings.EMAILS_FROM_NAME),
|
from_email=(settings.EMAILS_FROM_EMAIL, settings.EMAILS_FROM_NAME),
|
||||||
to_emails=recipient,
|
to_emails=recipient,
|
||||||
subject=subject,
|
subject=subject,
|
||||||
html_content=html or "<p>Üzenet</p>",
|
html_content=html
|
||||||
)
|
)
|
||||||
sg = SendGridAPIClient(settings.SENDGRID_API_KEY)
|
sg = SendGridAPIClient(settings.SENDGRID_API_KEY)
|
||||||
sg.send(message)
|
sg.send(message)
|
||||||
return {"status": "success", "provider": "sendgrid"}
|
return {"status": "success"}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# ha auto módban vagyunk, esünk vissza smtp-re
|
print(f"SendGrid Error: {e}")
|
||||||
if settings.EMAIL_PROVIDER != "auto":
|
|
||||||
return {"status": "error", "provider": "sendgrid", "message": str(e)}
|
|
||||||
|
|
||||||
|
# SMTP Fallback
|
||||||
|
# ... (az eredeti SMTP kódod ide jön változatlanul)
|
||||||
# 2) SMTP fallback
|
# 2) SMTP fallback
|
||||||
if not settings.SMTP_HOST or not settings.SMTP_USER or not settings.SMTP_PASSWORD:
|
if not settings.SMTP_HOST or not settings.SMTP_USER or not settings.SMTP_PASSWORD:
|
||||||
return {"status": "error", "provider": "smtp", "message": "SMTP not configured"}
|
return {"status": "error", "provider": "smtp", "message": "SMTP not configured"}
|
||||||
|
|||||||
@@ -14,4 +14,43 @@
|
|||||||
## Hiba Kezelés
|
## Hiba Kezelés
|
||||||
- **401:** Token lejárt -> Frontend dobjon Loginra.
|
- **401:** Token lejárt -> Frontend dobjon Loginra.
|
||||||
- **403:** Jogosultság hiba -> "Nincs jogod ehhez a funkcióhoz" (Tier limit).
|
- **403:** Jogosultság hiba -> "Nincs jogod ehhez a funkcióhoz" (Tier limit).
|
||||||
- **404:** Resource not found OR Soft Deleted.
|
- **404:** Resource not found OR Soft Deleted.
|
||||||
|
|
||||||
|
## 🌐 8. Nemzetköziesítés (i18n) és Lokalizáció
|
||||||
|
|
||||||
|
A rendszer a "Global-Local" elv alapján működik. Tilos a programkódban (hard-coded) szöveges üzeneteket elhelyezni.
|
||||||
|
|
||||||
|
### 8.1. Nyelvi fájlok struktúrája
|
||||||
|
Minden nyelvi fájl a `backend/app/locales/` mappában található, szabványos JSON formátumban.
|
||||||
|
Példa: `hu.json`, `en.json`, `de.json`.
|
||||||
|
|
||||||
|
### 8.2. Kezelési szabályok
|
||||||
|
- **Backend:** A rendszerüzeneteket, hibaüzeneteket és az e-mail sablonok tartalmát a `LocaleManager` szolgáltatáson keresztül kéri le.
|
||||||
|
- **Paraméterezés:** A szövegekben használható változók formátuma: `{variable_name}`.
|
||||||
|
- **Sablonkezelés:** Az e-mailek HTML vázát és a JSON-ban tárolt szöveges blokkokat a rendszer a küldés előtt fűzi össze.
|
||||||
|
|
||||||
|
### 8.3. Nyelvválasztás logikája
|
||||||
|
1. A kérés fejlécében érkező `Accept-Language` alapján.
|
||||||
|
2. Bejelentkezett felhasználó esetén a `User.region_code` alapján.
|
||||||
|
3. Alapértelmezett: `hu`.
|
||||||
|
|
||||||
|
# 🛡️ 9. Unified Registration & Security Protocol
|
||||||
|
|
||||||
|
A rendszer a "Minimal Friction, Maximum Security" elvét követi.
|
||||||
|
|
||||||
|
### 9.1. Regisztrációs Életciklus
|
||||||
|
1. **Step 1 (Lite):** `Email`, `Jelszó`, `Név` megadása. Létrejön a `User` és `Person` rekord. Állapot: `is_active: false`.
|
||||||
|
2. **Verifikáció:** A rendszer UUID alapú tokent generál (48 órás élettartam). A felhasználó e-mailben kap egy gombot/linket.
|
||||||
|
3. **Step 2 (KYC):** Sikeres verifikáció után a felhasználó megadja az okmányait (rugalmas választó: Személyi/Jogsi/Hajó).
|
||||||
|
4. **Aktiválás:** Létrejön a **Privát Flotta (Privát Széf)** és a hozzá tartozó `Wallet`. Állapot: `is_active: true`.
|
||||||
|
|
||||||
|
### 9.2. Token Biztonsági Előírások
|
||||||
|
- **Regisztrációs Token:** 48 óra élettartam.
|
||||||
|
- **Jelszó-visszaállítási Token:** 1 óra élettartam.
|
||||||
|
|
||||||
|
### 9.3. Rate Limiting (Robotvédelem és Költségkontroll)
|
||||||
|
Az e-mail küldési folyamatokra az alábbi korlátok vonatkoznak:
|
||||||
|
- **Retry Cooldown:** Újraigénylés (pl. "Nem kaptam meg a kódot") legkorábban 60 másodperc után lehetséges.
|
||||||
|
- **Óránkénti Limit:** Maximum 3 kérelem / e-mail cím.
|
||||||
|
- **Napi Limit:** Maximum 10 kérelem / e-mail cím.
|
||||||
|
- **Zárolás:** A napi limit túllépése esetén a fiók biztonsági okokból 24 órára zárolja a küldési funkciót az adott címre.
|
||||||
@@ -134,4 +134,28 @@ A rendszer két szintű helyreállítást biztosít:
|
|||||||
- **Kötelező adatok:** Vezetéknév, Keresztnév, Anyja neve, Személyi igazolvány száma.
|
- **Kötelező adatok:** Vezetéknév, Keresztnév, Anyja neve, Személyi igazolvány száma.
|
||||||
- **Logika:** 1. A rendszer azonosítja a `Person` rekordot.
|
- **Logika:** 1. A rendszer azonosítja a `Person` rekordot.
|
||||||
2. Ha sikeres, a rendszer kiküld egy visszaállító linket a Person-höz tartozó **elsődleges telefonszámra (SMS)** vagy a **legutolsó aktív Email címre**.
|
2. Ha sikeres, a rendszer kiküld egy visszaállító linket a Person-höz tartozó **elsődleges telefonszámra (SMS)** vagy a **legutolsó aktív Email címre**.
|
||||||
3. Sikeres helyreállítás után a felhasználónak kötelezően jelszót kell cserélnie.
|
3. Sikeres helyreállítás után a felhasználónak kötelezően jelszót kell cserélnie.
|
||||||
|
|
||||||
|
## 🛡️ 10. Kormányozhatóság és Biztonsági Beállítások
|
||||||
|
|
||||||
|
A rendszer biztonsági paraméterei központilag, a környezeti változókon keresztül szabályozhatók. Ez lehetővé teszi a biztonsági szint gyors módosítását (pl. támadás esetén szigorítás) a kód módosítása nélkül.
|
||||||
|
|
||||||
|
### 10.1. Token Élettartam Szabályok
|
||||||
|
A `.env` fájlban (vagy a rendszer beállításaiban) az alábbi paraméterekkel szabályozható a hozzáférés:
|
||||||
|
|
||||||
|
| Paraméter | Leírás | Alapértelmezett |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `REGISTRATION_TOKEN_EXPIRE_HOURS` | Regisztráció megerősítésére álló idő | 48 óra |
|
||||||
|
| `PASSWORD_RESET_TOKEN_EXPIRE_HOURS` | Jelszó visszaállítására álló idő | 1 óra |
|
||||||
|
|
||||||
|
### 10.2. Ideiglenes .env Konfiguráció (Példa)
|
||||||
|
```env
|
||||||
|
# SECURITY SETTINGS
|
||||||
|
REGISTRATION_TOKEN_EXPIRE_HOURS=48
|
||||||
|
PASSWORD_RESET_TOKEN_EXPIRE_HOURS=1
|
||||||
|
|
||||||
|
# EMAIL SYSTEM
|
||||||
|
EMAIL_PROVIDER=sendgrid
|
||||||
|
EMAILS_FROM_EMAIL=info@profibot.hu
|
||||||
|
EMAILS_FROM_NAME='Profibot Service Finder'
|
||||||
|
SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxxxxxx
|
||||||
Reference in New Issue
Block a user