aboutsummaryrefslogtreecommitdiff
path: root/cli/Commands
diff options
context:
space:
mode:
authorMarcus Eibrink-Lunzenauer <lunzenauer@elan-ev.de>2022-07-15 11:47:35 +0000
committerMarcus Eibrink-Lunzenauer <lunzenauer@elan-ev.de>2022-07-15 11:47:35 +0000
commit55852ef4819e5eafce9ae53dc4de2d84cdad1778 (patch)
tree9aedcdf89f416a7936f7df80da339a537082b5d5 /cli/Commands
parenta9585dad3547a4ebbadd00f44065f95017d18684 (diff)
StEP-366: Add OAuth2 support to Stud.IP
Closes #1035 and #1198 Merge request studip/studip!635
Diffstat (limited to 'cli/Commands')
-rw-r--r--cli/Commands/OAuth2/Keys.php68
-rw-r--r--cli/Commands/OAuth2/Purge.php58
2 files changed, 126 insertions, 0 deletions
diff --git a/cli/Commands/OAuth2/Keys.php b/cli/Commands/OAuth2/Keys.php
new file mode 100644
index 0000000..c9c0738
--- /dev/null
+++ b/cli/Commands/OAuth2/Keys.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Studip\Cli\Commands\OAuth2;
+
+use phpseclib\Crypt\RSA;
+use Studip\OAuth2\Container;
+use Studip\OAuth2\KeyInformation;
+use Studip\OAuth2\SetupInformation;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+class Keys extends Command
+{
+ protected static $defaultName = 'oauth2:keys';
+
+ protected function configure(): void
+ {
+ $this->setDescription(
+ 'Erstelle alle kryptografischen Schlüssel, um Stud.IP als OAuth2-Authorization-Server zu verwenden.'
+ );
+ $this->addOption('force', null, InputOption::VALUE_NONE, 'Überschreibe ggf. vorhandene Schlüssel');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $io = new SymfonyStyle($input, $output);
+
+ $container = new Container();
+ $setup = $container->get(SetupInformation::class);
+
+ $encryptionKey = $setup->encryptionKey();
+ $publicKey = $setup->publicKey();
+ $privateKey = $setup->privateKey();
+
+ $force = $input->getOption('force');
+
+ if (($encryptionKey->exists() || $publicKey->exists() || $privateKey->exists()) && !$force) {
+ $io->error(
+ 'Schlüsseldateien liegen bereits vor. Verwenden Sie die Option --force, um diese zu überschreiben.'
+ );
+ return Command::FAILURE;
+ }
+
+ $this->storeKeyContentsToFile($encryptionKey, $this->generateEncryptionKey());
+
+ $keys = (new RSA())->createKey(4096);
+ $this->storeKeyContentsToFile($publicKey, $keys['publickey']);
+ $this->storeKeyContentsToFile($privateKey, $keys['privatekey']);
+
+ $io->info('Schlüsseldateien erfolgreich angelegt.');
+
+ return Command::SUCCESS;
+ }
+
+ private function storeKeyContentsToFile(KeyInformation $key, string $contents)
+ {
+ file_put_contents($key->filename(), $contents);
+ chmod($key->filename(), 0660);
+ }
+
+ private function generateEncryptionKey(): string
+ {
+ return "<?php return '" . randomString(48) . "';";
+ }
+}
diff --git a/cli/Commands/OAuth2/Purge.php b/cli/Commands/OAuth2/Purge.php
new file mode 100644
index 0000000..3f7561f
--- /dev/null
+++ b/cli/Commands/OAuth2/Purge.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Studip\Cli\Commands\OAuth2;
+
+use Studip\OAuth2\Models\AccessToken;
+use Studip\OAuth2\Models\AuthCode;
+use Studip\OAuth2\Models\RefreshToken;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+class Purge extends Command
+{
+ protected static $defaultName = 'oauth2:purge';
+
+ protected function configure(): void
+ {
+ $this->setDescription('Bereinige die OAuth2-Datenbanktabellen von widerrufenen und/oder abgelaufenen Token');
+ $this->addOption('revoked', null, InputOption::VALUE_NONE, 'Entferne widerrufene Token');
+ $this->addOption('expired', null, InputOption::VALUE_NONE, 'Entferne abgelaufene Token');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $io = new SymfonyStyle($input, $output);
+
+ $expiryDate = strtotime('-7days midnight');
+
+ $revoked = $input->getOption('revoked');
+ $expired = $input->getOption('expired');
+
+ if (($revoked && $expired) || (!$revoked && !$expired)) {
+ AccessToken::deleteBySQL('revoked = ? AND expires_at < ?', [1, $expiryDate]);
+ AuthCode::deleteBySQL('revoked = ? AND expires_at < ?', [1, $expiryDate]);
+ RefreshToken::deleteBySQL('revoked = ? AND expires_at < ?', [1, $expiryDate]);
+
+ $io->info(
+ 'Alle Token, die widerrufen wurden oder vor mindestens 7 Tagen abgelaufen sind, wurden entfernt.'
+ );
+ } elseif ($revoked) {
+ AccessToken::deleteBySQL('revoked = ?', [1]);
+ AuthCode::deleteBySQL('revoked = ?', [1]);
+ RefreshToken::deleteBySQL('revoked = ?', [1]);
+
+ $io->info('Alle widerrufenen Token wurden entfernt.');
+ } elseif ($expired) {
+ AccessToken::deleteBySQL('expires_at < ?', [$expiryDate]);
+ AuthCode::deleteBySQL('expires_at < ?', [$expiryDate]);
+ RefreshToken::deleteBySQL('expires_at < ?', [$expiryDate]);
+
+ $io->info('Alle Token, die vor mindestens 7 Tagen abgelaufen sind, wurden entfernt.');
+ }
+
+ return Command::SUCCESS;
+ }
+}