From c49880385996b94de0c6e84d7778242c104ba9cf Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Sun, 7 Sep 2025 08:12:25 -0600 Subject: [PATCH] Add INITIAL_ADMIN_API_KEY environment variable support - Add configuration parameter for initial admin API key - Implement getInitialAdminApiToken() method in AbstractMultiPlatformMigration - Create migration to automatically generate admin API token on initial setup - Add CLAUDE.md to .gitignore for local development documentation --- .gitignore | 3 + config/parameters.yaml | 6 ++ migrations/Version20250907000000.php | 71 +++++++++++++++++++ .../AbstractMultiPlatformMigration.php | 25 +++++++ 4 files changed, 105 insertions(+) create mode 100644 migrations/Version20250907000000.php diff --git a/.gitignore b/.gitignore index 76655919..e57afb07 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,6 @@ yarn-error.log ###> phpstan/phpstan ### phpstan.neon ###< phpstan/phpstan ### + +# Claude Code project documentation (local development only) +CLAUDE.md diff --git a/config/parameters.yaml b/config/parameters.yaml index 154fbd8a..a72262ee 100644 --- a/config/parameters.yaml +++ b/config/parameters.yaml @@ -43,6 +43,10 @@ parameters: ###################################################################################################################### partdb.saml.enabled: '%env(bool:SAML_ENABLED)%' # If this is set to true, SAML authentication is enabled + ###################################################################################################################### + # API Configuration + ###################################################################################################################### + partdb.api.initial_admin_key: '%env(trim:string:INITIAL_ADMIN_API_KEY)%' # Initial admin API key for automated access (env only) ###################################################################################################################### # Miscellaneous @@ -104,3 +108,5 @@ parameters: env(SAML_ROLE_MAPPING): '{}' env(DATABASE_EMULATE_NATURAL_SORT): 0 + + env(INITIAL_ADMIN_API_KEY): '' diff --git a/migrations/Version20250907000000.php b/migrations/Version20250907000000.php new file mode 100644 index 00000000..1a2a33bd --- /dev/null +++ b/migrations/Version20250907000000.php @@ -0,0 +1,71 @@ +getInitialAdminApiToken(); + if (empty($apiToken)) { + return; + } + + // Create a proper API token with the 'tcp_' prefix and the provided key + $fullToken = 'tcp_' . $apiToken; + + // Set expiration to 1 year from now + $validUntil = date('Y-m-d H:i:s', strtotime('+1 year')); + $currentDateTime = date('Y-m-d H:i:s'); + + // Insert the API token for the admin user (user_id = 2) + // Level 4 = FULL access (can do everything the user can do) + $sql = "INSERT INTO api_tokens (user_id, name, token, level, valid_until, datetime_added, last_modified) + VALUES (2, 'Initial Admin Token', ?, 4, ?, ?, ?)"; + + $this->addSql($sql, [$fullToken, $validUntil, $currentDateTime, $currentDateTime]); + } + + public function mySQLUp(Schema $schema): void + { + $this->createInitialAdminApiToken(); + } + + public function mySQLDown(Schema $schema): void + { + // Remove the initial admin token if it exists + $this->addSql("DELETE FROM api_tokens WHERE name = 'Initial Admin Token' AND user_id = 2"); + } + + public function sqLiteUp(Schema $schema): void + { + $this->createInitialAdminApiToken(); + } + + public function sqLiteDown(Schema $schema): void + { + // Remove the initial admin token if it exists + $this->addSql("DELETE FROM api_tokens WHERE name = 'Initial Admin Token' AND user_id = 2"); + } + + public function postgreSQLUp(Schema $schema): void + { + $this->createInitialAdminApiToken(); + } + + public function postgreSQLDown(Schema $schema): void + { + // Remove the initial admin token if it exists + $this->addSql("DELETE FROM api_tokens WHERE name = 'Initial Admin Token' AND user_id = 2"); + } +} \ No newline at end of file diff --git a/src/Migration/AbstractMultiPlatformMigration.php b/src/Migration/AbstractMultiPlatformMigration.php index bc2b3f19..1bf42e1d 100644 --- a/src/Migration/AbstractMultiPlatformMigration.php +++ b/src/Migration/AbstractMultiPlatformMigration.php @@ -35,6 +35,7 @@ abstract class AbstractMultiPlatformMigration extends AbstractMigration { final public const ADMIN_PW_LENGTH = 10; protected string $admin_pw = ''; + protected string $admin_api_token = ''; /** @noinspection SenselessProxyMethodInspection * This method is required to redefine the logger type hint to protected @@ -108,6 +109,23 @@ abstract class AbstractMultiPlatformMigration extends AbstractMigration return password_hash((string) $this->admin_pw, PASSWORD_DEFAULT); } + /** + * Returns the initial admin API token if configured via environment variable. + * If not configured, returns empty string (no token will be created). + */ + public function getInitialAdminApiToken(): string + { + if ($this->admin_api_token === '') { + $apiKey = getenv('INITIAL_ADMIN_API_KEY'); + if (!empty($apiKey)) { + // Use the provided API key directly (should be generated with openssl rand -hex 32) + $this->admin_api_token = $apiKey; + } + } + + return $this->admin_api_token; + } + public function postUp(Schema $schema): void { parent::postUp($schema); @@ -117,6 +135,13 @@ abstract class AbstractMultiPlatformMigration extends AbstractMigration $this->logger->warning('The initial password for the "admin" user is: '.$this->admin_pw.''); $this->logger->warning(''); } + + if ($this->admin_api_token !== '') { + $this->logger->warning(''); + $this->logger->warning('Initial admin API token has been created with the provided key'); + $this->logger->warning('Use this token in Authorization header: Bearer tcp_'.$this->admin_api_token.''); + $this->logger->warning(''); + } } /**