From c49880385996b94de0c6e84d7778242c104ba9cf Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Sun, 7 Sep 2025 08:12:25 -0600 Subject: [PATCH 1/2] 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(''); + } } /** From 99cd18096bb67c4ee5cc10b9a1cf9fd86ceb067a Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Sun, 7 Sep 2025 08:42:17 -0600 Subject: [PATCH 2/2] Add INITIAL_ADMIN_API_KEY documentation - Document environment variable in configuration.md - Add section to API authentication documentation - Include examples in Docker installation guide for both SQLite and MySQL setups - Provide usage instructions for CI/CD and automated deployments --- docs/api/authentication.md | 8 ++++++++ docs/configuration.md | 5 +++++ docs/installation/installation_docker.md | 10 ++++++++++ 3 files changed, 23 insertions(+) diff --git a/docs/api/authentication.md b/docs/api/authentication.md index b386c0cd..f30e4276 100644 --- a/docs/api/authentication.md +++ b/docs/api/authentication.md @@ -32,6 +32,14 @@ tokens as you want and also delete them again. When deleting a token, it is immediately invalidated and can not be used anymore, which means that the application can not access the API anymore with this token. +### Initial Admin API Token + +For automated deployments and CI/CD pipelines, Part-DB supports automatically creating an initial admin API token +during database setup. Set the `INITIAL_ADMIN_API_KEY` environment variable to a 64-character random string +(generate with `openssl rand -hex 32`) before running database migrations. Part-DB will create an API token named +"Initial Admin Token" with FULL scope that expires after 1 year. The token can be used immediately with the format +`Bearer tcp_` in the Authorization header. + ### Token permissions and scopes API tokens are ultimately limited by the permissions of the user, which belongs to the token. That means that the token diff --git a/docs/configuration.md b/docs/configuration.md index d4b21781..04653ec3 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -114,6 +114,11 @@ bundled with Part-DB. Set `DATABASE_MYSQL_SSL_VERIFY_CERT` if you want to accept particularly for securing and protecting various aspects of your application. It's a secret key that is used for cryptographic operations and security measures (session management, CSRF protection, etc..). Therefore this value should be handled as confidential data and not shared publicly. +* `INITIAL_ADMIN_API_KEY` (env only): When set to a 64-character random string (generate with `openssl rand -hex 32`), + Part-DB will automatically create an API token named "Initial Admin Token" for the admin user during database + migrations. This token will have FULL scope and expire after 1 year. This is useful for automated deployments, + CI/CD pipelines, and Docker setups where you need immediate API access without manual token creation. The token + can be used with the format `Bearer tcp_` in the Authorization header. * `SHOW_PART_IMAGE_OVERLAY`: Set to 0 to disable the part image overlay, which appears if you hover over an image in the part image gallery diff --git a/docs/installation/installation_docker.md b/docs/installation/installation_docker.md index 232633ab..cf0039f7 100644 --- a/docs/installation/installation_docker.md +++ b/docs/installation/installation_docker.md @@ -75,6 +75,11 @@ services: # Use gravatars for user avatars, when user has no own avatar defined - USE_GRAVATAR=0 + # Automatically create an admin API token during database setup (useful for CI/CD pipelines) + # Generate a 64-character random string with: openssl rand -hex 32 + # The token will be available as: Bearer tcp_ + #- INITIAL_ADMIN_API_KEY=your_64_character_random_string_here + # Override value if you want to show a given text on homepage. # When this is empty the content of config/banner.md is used as banner #- BANNER=This is a test banner
with a line break @@ -146,6 +151,11 @@ services: # However you can add add any other environment configuration you want here # See .env file for all available options or https://docs.part-db.de/configuration.html + # Automatically create an admin API token during database setup (useful for CI/CD pipelines) + # Generate a 64-character random string with: openssl rand -hex 32 + # The token will be available as: Bearer tcp_ + #- INITIAL_ADMIN_API_KEY=your_64_character_random_string_here + # Override value if you want to show to show a given text on homepage. # When this is outcommented the webUI can be used to configure the banner #- BANNER=This is a test banner
with a line break