ocm:keys:list list known keys with their slot and kid ocm:keys:stage generate a pending key, advertise via JWKS ocm:keys:activate promote pending -> active, demote previous active ocm:keys:retire delete the retiring key (kid stops resolving) Plus the autoloader regen covering the new classes from this branch. Signed-off-by: Micke Nordin <kano@sunet.se>pull/60136/head
parent
3b5107bc96
commit
166bc2c74b
@ -0,0 +1,42 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors |
||||
* SPDX-License-Identifier: AGPL-3.0-or-later |
||||
*/ |
||||
namespace OC\Core\Command\OCM; |
||||
|
||||
use OC\Core\Command\Base; |
||||
use OC\OCM\OCMSignatoryManager; |
||||
use Symfony\Component\Console\Input\InputInterface; |
||||
use Symfony\Component\Console\Output\OutputInterface; |
||||
|
||||
class ActivateKey extends Base { |
||||
public function __construct( |
||||
private readonly OCMSignatoryManager $signatoryManager, |
||||
) { |
||||
parent::__construct(); |
||||
} |
||||
|
||||
#[\Override] |
||||
protected function configure(): void { |
||||
$this |
||||
->setName('ocm:keys:activate') |
||||
->setDescription('promote the staged Ed25519 key to active; the previous active key moves to retiring'); |
||||
} |
||||
|
||||
#[\Override] |
||||
protected function execute(InputInterface $input, OutputInterface $output): int { |
||||
try { |
||||
$this->signatoryManager->activateStagedEd25519Key(); |
||||
} catch (\RuntimeException $e) { |
||||
$output->writeln('<error>' . $e->getMessage() . '</error>'); |
||||
return 1; |
||||
} |
||||
$output->writeln('<info>Staged key promoted to active.</info>'); |
||||
$output->writeln('Run <info>occ ocm:keys:retire</info> once any in-flight signatures using the previous key have been verified.'); |
||||
return 0; |
||||
} |
||||
} |
||||
@ -0,0 +1,54 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors |
||||
* SPDX-License-Identifier: AGPL-3.0-or-later |
||||
*/ |
||||
namespace OC\Core\Command\OCM; |
||||
|
||||
use OC\Core\Command\Base; |
||||
use OC\OCM\OCMSignatoryManager; |
||||
use Symfony\Component\Console\Helper\Table; |
||||
use Symfony\Component\Console\Input\InputInterface; |
||||
use Symfony\Component\Console\Output\OutputInterface; |
||||
|
||||
class ListKeys extends Base { |
||||
public function __construct( |
||||
private readonly OCMSignatoryManager $signatoryManager, |
||||
) { |
||||
parent::__construct(); |
||||
} |
||||
|
||||
#[\Override] |
||||
protected function configure(): void { |
||||
$this |
||||
->setName('ocm:keys:list') |
||||
->setDescription('list Ed25519 keys used by OCM RFC 9421 HTTP Message Signatures'); |
||||
parent::configure(); |
||||
} |
||||
|
||||
#[\Override] |
||||
protected function execute(InputInterface $input, OutputInterface $output): int { |
||||
$keys = $this->signatoryManager->listEd25519Keys(); |
||||
$format = $input->getOption('output'); |
||||
if ($format === self::OUTPUT_FORMAT_JSON || $format === self::OUTPUT_FORMAT_JSON_PRETTY) { |
||||
$output->writeln(json_encode($keys, $format === self::OUTPUT_FORMAT_JSON_PRETTY ? JSON_PRETTY_PRINT : 0)); |
||||
return 0; |
||||
} |
||||
|
||||
if ($keys === []) { |
||||
$output->writeln('<comment>No Ed25519 keys yet; one will be generated on first OCM request.</comment>'); |
||||
return 0; |
||||
} |
||||
|
||||
$table = new Table($output); |
||||
$table->setHeaders(['Pool', 'Slot', 'Key ID']); |
||||
foreach ($keys as $key) { |
||||
$table->addRow([$key['poolId'], $key['slot'] ?? '-', $key['kid']]); |
||||
} |
||||
$table->render(); |
||||
return 0; |
||||
} |
||||
} |
||||
@ -0,0 +1,41 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors |
||||
* SPDX-License-Identifier: AGPL-3.0-or-later |
||||
*/ |
||||
namespace OC\Core\Command\OCM; |
||||
|
||||
use OC\Core\Command\Base; |
||||
use OC\OCM\OCMSignatoryManager; |
||||
use Symfony\Component\Console\Input\InputInterface; |
||||
use Symfony\Component\Console\Output\OutputInterface; |
||||
|
||||
class RetireKey extends Base { |
||||
public function __construct( |
||||
private readonly OCMSignatoryManager $signatoryManager, |
||||
) { |
||||
parent::__construct(); |
||||
} |
||||
|
||||
#[\Override] |
||||
protected function configure(): void { |
||||
$this |
||||
->setName('ocm:keys:retire') |
||||
->setDescription('delete the retiring Ed25519 key; signatures that referenced its kid can no longer be verified'); |
||||
} |
||||
|
||||
#[\Override] |
||||
protected function execute(InputInterface $input, OutputInterface $output): int { |
||||
try { |
||||
$this->signatoryManager->retireEd25519Key(); |
||||
} catch (\RuntimeException $e) { |
||||
$output->writeln('<error>' . $e->getMessage() . '</error>'); |
||||
return 1; |
||||
} |
||||
$output->writeln('<info>Retiring key deleted.</info>'); |
||||
return 0; |
||||
} |
||||
} |
||||
@ -0,0 +1,42 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors |
||||
* SPDX-License-Identifier: AGPL-3.0-or-later |
||||
*/ |
||||
namespace OC\Core\Command\OCM; |
||||
|
||||
use OC\Core\Command\Base; |
||||
use OC\OCM\OCMSignatoryManager; |
||||
use Symfony\Component\Console\Input\InputInterface; |
||||
use Symfony\Component\Console\Output\OutputInterface; |
||||
|
||||
class StageKey extends Base { |
||||
public function __construct( |
||||
private readonly OCMSignatoryManager $signatoryManager, |
||||
) { |
||||
parent::__construct(); |
||||
} |
||||
|
||||
#[\Override] |
||||
protected function configure(): void { |
||||
$this |
||||
->setName('ocm:keys:stage') |
||||
->setDescription('generate a new Ed25519 key and advertise it via JWKS without using it for signing yet'); |
||||
} |
||||
|
||||
#[\Override] |
||||
protected function execute(InputInterface $input, OutputInterface $output): int { |
||||
try { |
||||
$signatory = $this->signatoryManager->stageEd25519Key(); |
||||
} catch (\RuntimeException $e) { |
||||
$output->writeln('<error>' . $e->getMessage() . '</error>'); |
||||
return 1; |
||||
} |
||||
$output->writeln('Staged new Ed25519 key: <info>' . $signatory->getKeyId() . '</info>'); |
||||
$output->writeln('Wait for federated peers to refresh their JWKS cache before activating.'); |
||||
return 0; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue