Source code for flip_api.scripts.generate_trust_api_keys

# Copyright (c) Guy's and St Thomas' NHS Foundation Trust & King's College London
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#     http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""Generate per-trust API keys and write them into an environment file.

Trust names are read from the ``TRUST_NAMES`` env var (a JSON list).  Both
plaintext keys (``TRUST_API_KEYS``) and their hashes (``TRUST_API_KEY_HASHES``)
are written as JSON dicts into the environment file.

Usage:
    make generate-trust-api-keys
    make generate-trust-api-keys ENV_FILE=.env.stag
"""

import argparse
import hashlib
import json
import sys
from pathlib import Path

from flip_api.scripts.env_utils import read_env_value, update_or_append
from flip_api.scripts.generate_trust_key import generate_trust_key

REPO_ROOT = Path(__file__).resolve().parents[4]


[docs] def _parse_trust_names(lines: list[str]) -> list[str]: """Extract trust names from the TRUST_NAMES env var line. Args: lines (list[str]): Lines of the environment file. Returns: list[str]: List of trust names, e.g. ``["Trust_1", "Trust_2"]``. """ for line in lines: if line.startswith("TRUST_NAMES="): return json.loads(line.split("=", 1)[1]) return []
[docs] def main() -> None: """Generate per-trust API keys and write them into the specified environment file. Reads trust names from ``TRUST_NAMES``, generates a cryptographically secure key for each trust that doesn't already have a value in the ``TRUST_API_KEYS`` dict (or all if ``--force``), and updates both ``TRUST_API_KEYS`` and ``TRUST_API_KEY_HASHES`` JSON dicts in the env file. Raises: SystemExit: If the env file is missing or contains no ``TRUST_NAMES`` entry. """ parser = argparse.ArgumentParser(description="Generate per-trust API keys and update an environment file.") parser.add_argument( "--env-file", type=Path, default=REPO_ROOT / ".env.development", help="Path to the environment file to update (default: .env.development)", ) parser.add_argument( "--force", action="store_true", default=False, help="Regenerate all keys even if they already exist in the env file.", ) args = parser.parse_args() env_file: Path = args.env_file if not env_file.exists(): print(f"Error: {env_file} not found.") sys.exit(1) lines = env_file.read_text().splitlines() trust_names = _parse_trust_names(lines) if not trust_names: print(f"Error: no TRUST_NAMES entry found in {env_file.name}.") sys.exit(1) # Parse existing TRUST_API_KEYS dict (if any) existing_keys_json = read_env_value(lines, "TRUST_API_KEYS") existing_keys: dict[str, str] = json.loads(existing_keys_json) if existing_keys_json else {} # Generate keys — existing values in the dict are preserved unless --force keys_dict: dict[str, str] = {} actions: dict[str, str] = {} for trust_name in trust_names: existing_key = existing_keys.get(trust_name) if not args.force and existing_key: keys_dict[trust_name] = existing_key actions[trust_name] = "skipped" else: key, _ = generate_trust_key() keys_dict[trust_name] = key actions[trust_name] = "generated" # Build both JSON dicts hashes_dict = {name: hashlib.sha256(keys_dict[name].encode()).hexdigest() for name in trust_names} lines = update_or_append(lines, "TRUST_API_KEYS", json.dumps(keys_dict)) lines = update_or_append(lines, "TRUST_API_KEY_HASHES", json.dumps(hashes_dict)) env_file.write_text("\n".join(lines) + "\n") generated = sum(1 for a in actions.values() if a == "generated") skipped = sum(1 for a in actions.values() if a == "skipped") print(f"Updated {env_file.name}: {generated} trust keys generated, {skipped} skipped (already set).") for name, action in actions.items(): print(f" {name}: {action}") if generated: print(f" TRUST_API_KEYS and TRUST_API_KEY_HASHES updated with {len(trust_names)} entries")
if __name__ == "__main__": main()