mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
resolve OTP phone from customer profile with fallback option
This commit is contained in:
@@ -53,9 +53,13 @@ CUSTOMER_PASSWORD = os.getenv("CUSTOMER_PASSWORD", "")
|
|||||||
ADMIN_EMAIL = os.getenv("ADMIN_EMAIL", "")
|
ADMIN_EMAIL = os.getenv("ADMIN_EMAIL", "")
|
||||||
ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD", "")
|
ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD", "")
|
||||||
|
|
||||||
# Phone the sign-OTP is sent to and read back from Postgres. Independent of the
|
# Phone the sign-OTP is sent to and read back from Postgres. Resolved at runtime
|
||||||
# logged-in user — the sign endpoint keys the OTP purely on this number.
|
# from the customer's own IAM profile (GET /api/me → phoneNumber). OTP_PHONE is an
|
||||||
|
# optional override / fallback used only when the customer has no phone on file.
|
||||||
|
# The sign endpoint keys the OTP purely on this number, so it just has to be the
|
||||||
|
# same value for "send" and "read".
|
||||||
OTP_PHONE = os.getenv("OTP_PHONE", "")
|
OTP_PHONE = os.getenv("OTP_PHONE", "")
|
||||||
|
OTP_PHONE_FALLBACK = os.getenv("OTP_PHONE_FALLBACK", "251900000000")
|
||||||
|
|
||||||
# DB connection used ONLY to read the plaintext sign-OTP from freight.otp_verifications.
|
# DB connection used ONLY to read the plaintext sign-OTP from freight.otp_verifications.
|
||||||
DB_HOST = os.getenv("DB_HOST", "localhost")
|
DB_HOST = os.getenv("DB_HOST", "localhost")
|
||||||
@@ -202,6 +206,25 @@ def login(email: str, password: str, who: str) -> Client:
|
|||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
# OTP — send + read from Postgres
|
# OTP — send + read from Postgres
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
|
def resolve_otp_phone(customer: Client) -> str:
|
||||||
|
"""Phone the sign-OTP is sent to. Prefer the customer's own IAM profile phone
|
||||||
|
(GET /api/me → phoneNumber); fall back to OTP_PHONE, then OTP_PHONE_FALLBACK.
|
||||||
|
The value only has to be consistent between send + DB read."""
|
||||||
|
phone = ""
|
||||||
|
try:
|
||||||
|
me = customer.get("/me") or {}
|
||||||
|
phone = (me.get("phoneNumber") or "").strip()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
phone = phone or OTP_PHONE or OTP_PHONE_FALLBACK
|
||||||
|
if not phone:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Could not resolve an OTP phone (customer has none, and neither "
|
||||||
|
"OTP_PHONE nor OTP_PHONE_FALLBACK is set)."
|
||||||
|
)
|
||||||
|
return phone
|
||||||
|
|
||||||
|
|
||||||
def send_otp(customer: Client, phone: str) -> None:
|
def send_otp(customer: Client, phone: str) -> None:
|
||||||
# POST /api/otp/send is @Public — no token needed, but sending one is harmless.
|
# POST /api/otp/send is @Public — no token needed, but sending one is harmless.
|
||||||
customer.post_json("/otp/send", {"phone": phone})
|
customer.post_json("/otp/send", {"phone": phone})
|
||||||
@@ -321,7 +344,7 @@ def waafi_file_tuple(field_name: str) -> tuple:
|
|||||||
|
|
||||||
|
|
||||||
def drive_flow(
|
def drive_flow(
|
||||||
flow: Flow, idx: int, customer: Client, admin: Client, ref: RefData
|
flow: Flow, idx: int, customer: Client, admin: Client, ref: RefData, otp_phone: str
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
result: dict[str, Any] = {"flow": flow.label, "n": idx, "status": None}
|
result: dict[str, Any] = {"flow": flow.label, "n": idx, "status": None}
|
||||||
|
|
||||||
@@ -349,9 +372,9 @@ def drive_flow(
|
|||||||
admin.post_json(f"/contracts/{cid}/contract/generate")
|
admin.post_json(f"/contracts/{cid}/contract/generate")
|
||||||
|
|
||||||
# S6 — customer sign (needs OTP) → SIGNED_CUSTOMER
|
# S6 — customer sign (needs OTP) → SIGNED_CUSTOMER
|
||||||
send_otp(customer, OTP_PHONE)
|
send_otp(customer, otp_phone)
|
||||||
time.sleep(1.0) # let the OTP row land
|
time.sleep(1.0) # let the OTP row land
|
||||||
otp = read_otp_from_db(OTP_PHONE)
|
otp = read_otp_from_db(otp_phone)
|
||||||
customer.post_json(
|
customer.post_json(
|
||||||
f"/contracts/{cid}/contract/sign",
|
f"/contracts/{cid}/contract/sign",
|
||||||
{
|
{
|
||||||
@@ -360,7 +383,7 @@ def drive_flow(
|
|||||||
"signerDisplayName": "Seed Customer",
|
"signerDisplayName": "Seed Customer",
|
||||||
"consentText": "I agree.",
|
"consentText": "I agree.",
|
||||||
"otp": otp,
|
"otp": otp,
|
||||||
"otpPhone": OTP_PHONE,
|
"otpPhone": otp_phone,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -429,7 +452,6 @@ def require_config() -> None:
|
|||||||
("CUSTOMER_PASSWORD", CUSTOMER_PASSWORD),
|
("CUSTOMER_PASSWORD", CUSTOMER_PASSWORD),
|
||||||
("ADMIN_EMAIL", ADMIN_EMAIL),
|
("ADMIN_EMAIL", ADMIN_EMAIL),
|
||||||
("ADMIN_PASSWORD", ADMIN_PASSWORD),
|
("ADMIN_PASSWORD", ADMIN_PASSWORD),
|
||||||
("OTP_PHONE", OTP_PHONE),
|
|
||||||
]
|
]
|
||||||
if not val
|
if not val
|
||||||
]
|
]
|
||||||
@@ -453,6 +475,9 @@ def main() -> None:
|
|||||||
customer = login(CUSTOMER_EMAIL, CUSTOMER_PASSWORD, "customer")
|
customer = login(CUSTOMER_EMAIL, CUSTOMER_PASSWORD, "customer")
|
||||||
admin = login(ADMIN_EMAIL, ADMIN_PASSWORD, "admin")
|
admin = login(ADMIN_EMAIL, ADMIN_PASSWORD, "admin")
|
||||||
|
|
||||||
|
otp_phone = resolve_otp_phone(customer)
|
||||||
|
print(f"OTP phone: {otp_phone}")
|
||||||
|
|
||||||
print("Loading reference data...")
|
print("Loading reference data...")
|
||||||
ref = load_ref_data(admin)
|
ref = load_ref_data(admin)
|
||||||
|
|
||||||
@@ -461,7 +486,7 @@ def main() -> None:
|
|||||||
for n in range(1, CONTRACTS_PER_FLOW + 1):
|
for n in range(1, CONTRACTS_PER_FLOW + 1):
|
||||||
tag = f"[{flow.label} #{n}]"
|
tag = f"[{flow.label} #{n}]"
|
||||||
try:
|
try:
|
||||||
res = drive_flow(flow, n, customer, admin, ref)
|
res = drive_flow(flow, n, customer, admin, ref, otp_phone)
|
||||||
print(f" OK {tag} {res['reference']} -> {res['status']}")
|
print(f" OK {tag} {res['reference']} -> {res['status']}")
|
||||||
results.append(res)
|
results.append(res)
|
||||||
except Exception as exc: # noqa: BLE001 — report and continue
|
except Exception as exc: # noqa: BLE001 — report and continue
|
||||||
|
|||||||
Reference in New Issue
Block a user