Files
gatehouse-api/tests/integration/client/auth.py
T
nexgen_mirrors 015c622016 test: add comprehensive integration test suite for IAM platform
Add 162 integration tests covering authentication flows, TOTP MFA,
SSH key/certificate management, organization workflows, multi-org
access, self-service features, admin operations, authorization,
security edge cases, department/principal management, CA management,
policy compliance, WebAuthn passkeys, and ZeroTier network access.

Includes:
- Reusable API client library with session management
- Test fixtures for users, organizations, memberships, and CAs
- Helper functions for SSH key generation and verification
- Documentation for running and writing tests

Also update test configuration to disable conflicting maas plugins
and configure WebAuthn/session settings for localhost testing.
2026-04-23 15:41:37 +09:30

126 lines
4.9 KiB
Python

"""Auth client for integration tests."""
import logging
logger = logging.getLogger(__name__)
class AuthClient:
"""Wraps authentication-related API calls.
Provides convenience methods for register, login, logout, and
session management. Automatically stores the token on the parent
SecuirdClient when login / register succeed.
"""
def __init__(self, client):
self._client = client
# ------------------------------------------------------------------
# Registration
# ------------------------------------------------------------------
def register(self, email: str, password: str, full_name: str | None = None) -> dict:
"""Register a new user and return the response payload.
Args:
email: User's email address.
password: Plain-text password (>= 8 chars).
full_name: Optional display name.
Returns:
API response dict containing ``user``, ``token``, ``expires_at``.
Raises:
ApiError: On validation failure or duplicate email.
"""
logger.info(f"[AuthClient] Registering user: email={email}")
payload = {"email": email, "password": password, "password_confirm": password}
if full_name:
payload["full_name"] = full_name
result = self._client.post("/auth/register", data=payload)
token = result.get("data", {}).get("token")
if token:
self._client.set_token(token)
logger.info(f"[AuthClient] Registration successful — token stored")
return result
# ------------------------------------------------------------------
# Login / Logout
# ------------------------------------------------------------------
def login(self, email: str, password: str, remember_me: bool = False) -> dict:
"""Authenticate with email and password.
Args:
email: Registered email address.
password: Plain-text password.
remember_me: Request a long-lived session.
Returns:
API response dict. If TOTP / WebAuthn is required the
response contains ``requires_totp`` or ``requires_webauthn``
instead of a token.
"""
logger.info(f"[AuthClient] Logging in: email={email}")
result = self._client.post(
"/auth/login",
data={"email": email, "password": password, "remember_me": remember_me},
)
token = result.get("data", {}).get("token")
if token:
self._client.set_token(token)
logger.info(f"[AuthClient] Login successful — token stored")
return result
def logout(self) -> dict:
"""Log out the current user and clear the stored token."""
logger.info("[AuthClient] Logging out")
result = self._client.post("/auth/logout")
self._client.clear_token()
return result
# ------------------------------------------------------------------
# Current user
# ------------------------------------------------------------------
def me(self) -> dict:
"""Return the current authenticated user's profile."""
return self._client.get("/auth/me")
# ------------------------------------------------------------------
# Sessions
# ------------------------------------------------------------------
def list_sessions(self) -> dict:
"""Return active sessions for the current user."""
return self._client.get("/auth/sessions")
def revoke_session(self, session_id: str) -> dict:
"""Revoke a specific session belonging to the current user."""
return self._client.delete(f"/auth/sessions/{session_id}")
# ------------------------------------------------------------------
# Password recovery
# ------------------------------------------------------------------
def forgot_password(self, email: str) -> dict:
"""Request a password-reset email."""
return self._client.post("/auth/forgot-password", data={"email": email})
def reset_password(self, token: str, new_password: str, new_password_confirm: str) -> dict:
"""Reset password using a token from the forgot-password flow."""
return self._client.post(
"/auth/reset-password",
data={
"token": token,
"password": new_password,
"password_confirm": new_password_confirm,
},
)
# ------------------------------------------------------------------
# Email verification
# ------------------------------------------------------------------
def verify_email(self, token: str) -> dict:
"""Verify an email address using the token sent by email."""
return self._client.post("/auth/verify-email", data={"token": token})
def resend_verification(self, email: str) -> dict:
"""Re-send the verification email."""
return self._client.post("/auth/resend-verification", data={"email": email})