aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/api/users.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/users.py')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/users.py15
1 files changed, 15 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/users.py b/packages/meshbay-hub/src/meshbay_hub/api/users.py
index 9326bfb..795a902 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/users.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/users.py
@@ -183,6 +183,7 @@ async def register(
request: Request,
db: AsyncSession = Depends(get_db),
):
+ """Create an account. It stays inactive until its e-mail address is verified."""
eh = hash_email_blind(body.email)
# Unique regardless of case: invitations and member management name people
@@ -485,6 +486,10 @@ async def login(
request: Request,
db: AsyncSession = Depends(get_db),
):
+ """
+ Sign in with the auth key derived from the passphrase. Returns the session and the bundle
+ pepper.
+ """
ip = client_ip(request)
if not body.auth_key and not body.password:
@@ -662,6 +667,7 @@ async def list_devices(
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
+ """The account's registered devices."""
result = await db.execute(
select(UserDevice).where(UserDevice.user_id == current_user.id)
.order_by(UserDevice.created_at))
@@ -777,6 +783,7 @@ async def token_refresh(
request: Request,
db: AsyncSession = Depends(get_db),
):
+ """Exchange a refresh token for a new session. Reusing a spent one revokes the whole family."""
rt_hash = hash_refresh_token(body.refresh_token)
result = await db.execute(
select(RefreshToken).where(RefreshToken.token_hash == rt_hash))
@@ -840,6 +847,7 @@ async def get_current_user_info(
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
+ """The signed-in account: id, name, e-mail, role, status."""
email = ""
try:
email = decrypt_email(current_user.email) if current_user.email else ""
@@ -1144,6 +1152,7 @@ async def change_password(
current_user: User = Depends(require_user_scope),
db: AsyncSession = Depends(get_db),
):
+ """Change the passphrase, proving the current one."""
await _take_login_attempt(db, _session_counter(current_user))
if not await verify_password_off_loop(body.old_auth_key, current_user.pw_hash,
current_user.pw_salt, current_user.pw_version):
@@ -1231,6 +1240,7 @@ async def password_reset_request(
request: Request,
db: AsyncSession = Depends(get_db),
):
+ """Send a reset code by e-mail, when the username and the address match."""
if _cfg and _cfg.captcha.enabled:
await _verify_captcha_or_raise(body.captcha_token, request)
@@ -1310,6 +1320,7 @@ async def password_reset(
request: Request,
db: AsyncSession = Depends(get_db),
):
+ """Set a new passphrase with the code received by e-mail."""
now = datetime.now(UTC)
result = await db.execute(select(User).where(User.username == body.username))
user = result.scalar_one_or_none()
@@ -1407,6 +1418,7 @@ async def get_preferences(
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
+ """The account's stored interface preferences."""
result = await db.execute(
select(UserPreference).where(UserPreference.user_id == current_user.id))
prefs = {p.key: p.value for p in result.scalars().all()}
@@ -1424,6 +1436,7 @@ async def set_preference(
current_user: User = Depends(require_user_scope),
db: AsyncSession = Depends(get_db),
):
+ """Store one interface preference."""
if not _valid_pref_key(key):
raise HTTPException(status_code=400, detail=f"Unknown preference key: {key[:80]}")
if len(body.value) > MAX_PREFERENCE_VALUE:
@@ -1454,6 +1467,7 @@ async def delete_preference(
current_user: User = Depends(require_user_scope),
db: AsyncSession = Depends(get_db),
):
+ """Remove one interface preference."""
result = await db.execute(
select(UserPreference).where(
UserPreference.user_id == current_user.id,
@@ -1624,6 +1638,7 @@ async def get_user_pubkeys(
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
+ """Resolve a username to its account id, and its node's linking key. Not a key directory."""
result = await db.execute(select(User).where(User.username == username))
target = result.scalar_one_or_none()
if not target: