<?php
/*
 * mint-apikey.php <username> <output-file>
 *
 * Mints an OPNsense API key using OPNSENSE'S OWN MODEL CODE -- the exact path the
 * GUI's "Create and download API key for this user" ticket button invokes
 * (Auth\Api\UserController::addApiKeyAction -> $user->apikeys->add()).
 *
 * WHY THIS AND NOT AN OFFLINE GENERATOR: minting the key ourselves would mean
 * re-implementing OPNsense's $6$ SHA-512 crypt storage format and keeping it
 * byte-compatible forever. A mismatch fails SILENTLY (keys that never
 * authenticate). Calling the vendor's own generator has no format to keep in
 * sync -- same principle as D-113(a2) itself: call the interface, don't
 * re-implement the internals.
 *
 * The secret is written to <output-file> (0600) and NEVER printed: OPNsense
 * stores it as crypt(secret,'$6$'), so creation is the only moment it exists in
 * cleartext. Stdout gets lengths only.
 *
 * Run from /usr/local/opnsense/mvc (load_phalcon.php is relative to it).
 */
require_once('script/load_phalcon.php');

use OPNsense\Core\Config;
use OPNsense\Auth\User;

if ($argc < 3) {
    fwrite(STDERR, "usage: mint-apikey.php <username> <output-file>\n");
    exit(1);
}
$username = $argv[1];
$outfile  = $argv[2];

Config::getInstance()->lock();

$mdl  = new User();
$user = $mdl->getUserByName($username);
if ($user == null) {
    fwrite(STDERR, "FAIL: no such user: {$username}\n");
    exit(1);
}

$new = $user->apikeys->add();
if (empty($new) || empty($new['key']) || empty($new['secret'])) {
    fwrite(STDERR, "FAIL: apikeys->add() returned nothing\n");
    exit(1);
}

$mdl->serializeToConfig();
Config::getInstance()->save();

$payload = "key=" . $new['key'] . "\nsecret=" . $new['secret'] . "\n";
$fh = fopen($outfile, 'w');
if ($fh === false) {
    fwrite(STDERR, "FAIL: cannot write {$outfile}\n");
    exit(1);
}
fwrite($fh, $payload);
fclose($fh);
chmod($outfile, 0600);

printf(
    "OK: minted for %s -- key=%d chars, secret=%d chars, wrote %d bytes to %s (0600, secret NOT printed)\n",
    $username,
    strlen($new['key']),
    strlen($new['secret']),
    strlen($payload),
    $outfile
);
