aboutsummaryrefslogtreecommitdiff
path: root/jb/models
diff options
context:
space:
mode:
authorstuppie2026-09-01 11:56:05 -0600
committerstuppie2026-09-01 11:56:05 -0600
commitc991d4f51254b1c81fa6e0e19b0ba94b8c5f61c4 (patch)
tree34598a150db7c85b7e0ee50d1ced31bda74d271c /jb/models
parent3f8d178d38686c1a5d71d94ebccdb61701469e95 (diff)
downloadamt-jb-c991d4f51254b1c81fa6e0e19b0ba94b8c5f61c4.tar.gz
amt-jb-c991d4f51254b1c81fa6e0e19b0ba94b8c5f61c4.zip
add GRApiManager. py-utils to generalresearch
Diffstat (limited to 'jb/models')
-rw-r--r--jb/models/auth.py47
-rw-r--r--jb/models/bonus.py2
-rw-r--r--jb/models/hit.py2
3 files changed, 42 insertions, 9 deletions
diff --git a/jb/models/auth.py b/jb/models/auth.py
index 5f31bd3..8b48088 100644
--- a/jb/models/auth.py
+++ b/jb/models/auth.py
@@ -1,5 +1,6 @@
import hashlib
import hmac
+from typing import Any
from pydantic import (
BaseModel,
@@ -8,6 +9,8 @@ from pydantic import (
Field,
TypeAdapter,
computed_field,
+ field_validator,
+ model_validator,
)
from jb.config import settings
@@ -30,29 +33,59 @@ def email_to_product_user_id(email: str) -> str:
return hmac.new(
key=salt_bytes,
msg=normalized_email.encode("utf-8"),
- digestmod=hashlib.sha256,
+ digestmod=hashlib.sha1,
).hexdigest()
-class AuthenticatedUser(BaseModel):
+class User(BaseModel):
"""A user that has been authenticated and exists in THL"""
email: EmailStr = Field()
+ display_name: str | None = Field(default=None, max_length=255)
+
+ @field_validator("email", mode="before")
+ @classmethod
+ def normalize_email(cls, value: Any) -> Any:
+ if not isinstance(value, str):
+ return value
+ if not value.isascii():
+ raise ValueError("email must contain ASCII characters only")
+ return value.lower()
+
+ @model_validator(mode="before")
+ @classmethod
+ def validate_product_user_id(cls, data: Any) -> Any:
+ if not isinstance(data, dict) or "product_user_id" not in data:
+ return data
+
+ provided_id = data["product_user_id"]
+ email = data.get("email")
+ if email is None:
+ return data
+
+ expected_id = email_to_product_user_id(str(email))
+ if not isinstance(provided_id, str) or not hmac.compare_digest(
+ provided_id, expected_id
+ ):
+ raise ValueError(f"product_user_id {provided_id} does not match email {email}")
+
+ # The computed field is authoritative; do not retain the input value.
+ validated_data = dict(data)
+ validated_data.pop("product_user_id")
+ return validated_data
+
@computed_field
+ @property
def product_user_id(self) -> str:
return email_to_product_user_id(self.email)
-class AccountCreate(BaseModel):
- email: EmailStr = Field()
-
-
class AccountLogin(BaseModel):
+ # There is no practical difference between an Account "login" and "create"
email: EmailStr = Field()
-
class MagicLinkExchangeRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
diff --git a/jb/models/bonus.py b/jb/models/bonus.py
index a536dd1..5f81add 100644
--- a/jb/models/bonus.py
+++ b/jb/models/bonus.py
@@ -3,7 +3,7 @@ from typing import Optional, Dict, Any
from pydantic import BaseModel, Field, ConfigDict, PositiveInt
from typing_extensions import Self
-from generalresearchutils.currency import USDCent
+from generalresearch.currency import USDCent
from jb.models.custom_types import AMTBoto3ID, AwareDatetimeISO, UUIDStr
diff --git a/jb/models/hit.py b/jb/models/hit.py
index fba2ecf..45478fc 100644
--- a/jb/models/hit.py
+++ b/jb/models/hit.py
@@ -13,7 +13,7 @@ from pydantic import (
)
from typing_extensions import Self
-from generalresearchutils.currency import USDCent
+from generalresearch.currency import USDCent
from jb.models.custom_types import AMTBoto3ID, HttpsUrlStr, AwareDatetimeISO
from jb.models.definitions import HitStatus, HitReviewStatus