Source code for app.model.api_key

import os
from datetime import datetime
from typing import Optional
from uuid import uuid4

from pydantic import ConfigDict
from sqlalchemy import Boolean
from sqlalchemy.dialects.postgresql import UUID
from sqlmodel import Column, DateTime, Field, ForeignKey, SQLModel, String, func

from app.util.generate_api_key import extract_prefix, hash_api_key


[docs] class ApiKey(SQLModel, table=True): """ Represents an API key used for authenticating and authorizing access to the system. Plaintext keys are never persisted. Each row stores: * ``apiKey`` -- the public prefix (e.g. ``gme_live_abc12345``). Safe to log and referenced by ``apiKey_used`` FK columns across the schema. * ``apiKeyHash`` -- ``sha256(plaintext)`` hex digest, used to authenticate requests in O(1) without ever storing the secret. Attributes: apiKey (str): Public key prefix. apiKeyHash (str): sha256 hex digest of the key plaintext. description (str) (optional): A description of the API key. active (bool): Flag indicating whether the API key is active. createdBy (str): Keycloak userId of the creator. """ id: str = Field( default_factory=uuid4, sa_column=Column(UUID(as_uuid=True), primary_key=True, index=True), ) created_at: datetime = Field( sa_column=Column(DateTime(timezone=True), default=func.now()) ) updated_at: datetime = Field( sa_column=Column( DateTime(timezone=True), default=func.now(), onupdate=func.now() ) ) apiKey: str = Field(sa_column=Column(String, unique=True, index=True)) apiKeyHash: str = Field(sa_column=Column(String, unique=True, index=True)) client: str = Field(sa_column=Column(String)) description: str = Field(sa_column=Column(String, nullable=True)) active: bool = Field(sa_column=Column(Boolean, default=True)) createdBy: str = Field(sa_column=Column(String)) oauth_user_id: str = Field( sa_column=Column( String, ForeignKey("oauthusers.provider_user_id"), nullable=True ) ) model_config = ConfigDict(from_attributes=True) def __str__(self): return ( f"ApiKey: (id={self.id}, created_at={self.created_at}, " f"updated_at={self.updated_at}, apiKey={self.apiKey}, " f"description={self.description}, active={self.active}, " f"createdBy={self.createdBy})" ) def __repr__(self): return ( f"ApiKey: (id={self.id}, created_at={self.created_at}, " f"updated_at={self.updated_at}, apiKey={self.apiKey}, " f"description={self.description}, active={self.active}, " f"createdBy={self.createdBy})" ) def __eq__(self, other): return ( isinstance(other, ApiKey) and self.apiKey == other.apiKey and self.description == other.description and self.active == other.active and self.createdBy == other.createdBy )
[docs] @staticmethod def get_e2e_seed_api_key() -> Optional[str]: """ Returns the E2E seed API key plaintext from environment variable `E2E_API_KEY_GAME`, or None when missing/empty. """ seed_value = os.getenv("E2E_API_KEY_GAME") if seed_value is None: return None normalized_seed = seed_value.strip() if not normalized_seed: return None return normalized_seed
[docs] @classmethod def build_e2e_seed( cls, *, created_by: str, client: str = "e2e-seeded-client", description: str = "Seeded API key from E2E_API_KEY_GAME", oauth_user_id: Optional[str] = None, ) -> Optional["ApiKey"]: """ Builds an ApiKey row from the plaintext stored in ``E2E_API_KEY_GAME``. The row only carries the derived prefix and hash; the plaintext is not persisted. Returns None when the env variable is not configured. """ seed_plaintext = cls.get_e2e_seed_api_key() if seed_plaintext is None: return None return cls( apiKey=extract_prefix(seed_plaintext), apiKeyHash=hash_api_key(seed_plaintext), client=client, description=description, active=True, createdBy=created_by, oauth_user_id=oauth_user_id, )