Skip to content

Commit

Permalink
Telekom bearer secret changes
Browse files Browse the repository at this point in the history
  • Loading branch information
memurats committed Nov 12, 2024
1 parent 42fde60 commit ce754c1
Show file tree
Hide file tree
Showing 8 changed files with 577 additions and 5 deletions.
12 changes: 10 additions & 2 deletions lib/Command/UpsertProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ protected function configure() {
->addOption('clientsecret', 's', InputOption::VALUE_REQUIRED, 'OpenID client secret')
->addOption('discoveryuri', 'd', InputOption::VALUE_REQUIRED, 'OpenID discovery endpoint uri')
->addOption('endsessionendpointuri', 'e', InputOption::VALUE_OPTIONAL, 'OpenID end session endpoint uri')
->addOption('bearersecret', 'bs', InputOption::VALUE_OPTIONAL, 'Telekom bearer token requires a different client secret for bearer tokens')
->addOption('scope', 'o', InputOption::VALUE_OPTIONAL, 'OpenID requested value scopes, if not set defaults to "openid email profile"');
foreach (self::EXTRA_OPTIONS as $name => $option) {
$this->addOption($name, $option['shortcut'], $option['mode'], $option['description'], $option['default']);
Expand All @@ -170,10 +171,17 @@ protected function execute(InputInterface $input, OutputInterface $output) {
return $this->listProviders($input, $output);
}

// bearersecret is usually base64 encoded, but SAM delivers it non-encoded by default
// so always encode/decode for this field
$bearersecret = $input->getOption('bearersecret');
if ($bearersecret !== null) {
$bearersecret = $this->crypto->encrypt(\Base64Url\Base64Url::encode($bearersecret));

Check failure on line 178 in lib/Command/UpsertProvider.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedClass

lib/Command/UpsertProvider.php:178:43: UndefinedClass: Class, interface or enum named Base64Url\Base64Url does not exist (see https://psalm.dev/019)
}

// check if any option for updating is provided
$updateOptions = array_filter($input->getOptions(), static function ($value, $option) {
return in_array($option, [
'identifier', 'clientid', 'clientsecret', 'discoveryuri', 'scope',
'identifier', 'clientid', 'clientsecret', 'discoveryuri', 'scope', 'bearersecret',
...array_keys(self::EXTRA_OPTIONS),
]) && $value !== null;
}, ARRAY_FILTER_USE_BOTH);
Expand Down Expand Up @@ -213,7 +221,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
$scope = $scope ?? 'openid email profile';
}
try {
$provider = $this->providerMapper->createOrUpdateProvider($identifier, $clientid, $clientsecret, $discoveryuri, $scope, $endsessionendpointuri);
$provider = $this->providerMapper->createOrUpdateProvider($identifier, $clientid, $clientsecret, $discoveryuri, $scope, $endsessionendpointuri, $bearersecret);
// invalidate JWKS cache (even if it was just created)
$this->providerService->setSetting($provider->getId(), ProviderService::SETTING_JWKS_CACHE, '');
$this->providerService->setSetting($provider->getId(), ProviderService::SETTING_JWKS_CACHE_TIMESTAMP, '');
Expand Down
6 changes: 4 additions & 2 deletions lib/Controller/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public function isDiscoveryEndpointValid($url) {
return $result;
}

public function createProvider(string $identifier, string $clientId, string $clientSecret, string $discoveryEndpoint,
public function createProvider(string $identifier, string $clientId, string $clientSecret, string $discoveryEndpoint, string $bearerSecret,
array $settings = [], string $scope = 'openid email profile', ?string $endSessionEndpoint = null): JSONResponse {
if ($this->providerService->getProviderByIdentifier($identifier) !== null) {
return new JSONResponse(['message' => 'Provider with the given identifier already exists'], Http::STATUS_CONFLICT);
Expand All @@ -96,14 +96,16 @@ public function createProvider(string $identifier, string $clientId, string $cli
$provider->setDiscoveryEndpoint($discoveryEndpoint);
$provider->setEndSessionEndpoint($endSessionEndpoint ?: null);
$provider->setScope($scope);
$encryptedBearerSecret = $this->crypto->encrypt(\Base64Url\Base64Url::encode($bearerSecret));

Check failure on line 99 in lib/Controller/SettingsController.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

UndefinedClass

lib/Controller/SettingsController.php:99:51: UndefinedClass: Class, interface or enum named Base64Url\Base64Url does not exist (see https://psalm.dev/019)
$provider->setBearerSecret($encryptedBearerSecret);
$provider = $this->providerMapper->insert($provider);

$providerSettings = $this->providerService->setSettings($provider->getId(), $settings);

return new JSONResponse(array_merge($provider->jsonSerialize(), ['settings' => $providerSettings]));
}

public function updateProvider(int $providerId, string $identifier, string $clientId, string $discoveryEndpoint, ?string $clientSecret = null,
public function updateProvider(int $providerId, string $identifier, string $clientId, string $discoveryEndpoint, ?string $clientSecret = null, string $bearerSecret = null,
array $settings = [], string $scope = 'openid email profile', ?string $endSessionEndpoint = null): JSONResponse {
$provider = $this->providerMapper->getProvider($providerId);

Expand Down
6 changes: 6 additions & 0 deletions lib/Db/Provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
* @method void setClientId(string $clientId)
* @method string getClientSecret()
* @method void setClientSecret(string $clientSecret)
* @method string getBearerSecret()
* @method void setBearerSecret(string $bearerSecret)
* @method string getDiscoveryEndpoint()
* @method void setDiscoveryEndpoint(string $discoveryEndpoint)
* @method string getEndSessionEndpoint()
* @method void setEndSessionEndpoint(string $endSessionEndpoint)
* @method string getScope()
* @method void setScope(string $scope)
*/
class Provider extends Entity implements \JsonSerializable {
Expand All @@ -34,6 +37,9 @@ class Provider extends Entity implements \JsonSerializable {
/** @var string */
protected $clientSecret;

/** @var string */
protected $bearerSecret;

/** @var string */
protected $discoveryEndpoint;

Expand Down
7 changes: 6 additions & 1 deletion lib/Db/ProviderMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public function getProviders() {
* @param string $identifier
* @param string|null $clientid
* @param string|null $clientsecret
* @param string|null $bearersecret
* @param string|null $discoveryuri
* @param string $scope
* @param string|null $endsessionendpointuri
Expand All @@ -90,7 +91,7 @@ public function getProviders() {
*/
public function createOrUpdateProvider(string $identifier, ?string $clientid = null,
?string $clientsecret = null, ?string $discoveryuri = null, string $scope = 'openid email profile',
?string $endsessionendpointuri = null) {
?string $endsessionendpointuri = null, string $bearersecret = null) {
try {
$provider = $this->findProviderByIdentifier($identifier);
} catch (DoesNotExistException $eNotExist) {
Expand All @@ -105,6 +106,7 @@ public function createOrUpdateProvider(string $identifier, ?string $clientid = n
$provider->setIdentifier($identifier);
$provider->setClientId($clientid);
$provider->setClientSecret($clientsecret);
$provider->setBearerSecret($bearersecret ?? '');
$provider->setDiscoveryEndpoint($discoveryuri);
$provider->setEndSessionEndpoint($endsessionendpointuri);
$provider->setScope($scope);
Expand All @@ -116,6 +118,9 @@ public function createOrUpdateProvider(string $identifier, ?string $clientid = n
if ($clientsecret !== null) {
$provider->setClientSecret($clientsecret);
}
if ($bearersecret !== null) {
$provider->setBearerSecret($bearersecret);
}
if ($discoveryuri !== null) {
$provider->setDiscoveryEndpoint($discoveryuri);
}
Expand Down
25 changes: 25 additions & 0 deletions lib/Migration/Version00008Date20211114183344.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace OCA\UserOIDC\Migration;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

class Version00008Date20211114183344 extends SimpleMigrationStep {
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();

$table = $schema->getTable('user_oidc_providers');
$table->addColumn('bearer_secret', 'string', [
'notnull' => true,
'length' => 64,
]);

return $schema;
}
}
97 changes: 97 additions & 0 deletions lib/Migration/Version010304Date20230902125945.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

declare(strict_types=1);

/**
* @copyright Copyright 2023, Julien Veyssier <[email protected]>
*
* @author B. Rederlechner <[email protected]>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\UserOIDC\Migration;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use OCP\Security\ICrypto;

class Version010304Date20230902125945 extends SimpleMigrationStep {

/**
* @var IDBConnection
*/
private $connection;
/**
* @var ICrypto
*/
private $crypto;

public function __construct(
IDBConnection $connection,
ICrypto $crypto
) {
$this->connection = $connection;
$this->crypto = $crypto;
}

public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$tableName = 'user_oidc_providers';

if ($schema->hasTable($tableName)) {
$table = $schema->getTable($tableName);
if ($table->hasColumn('bearer_secret')) {
$column = $table->getColumn('bearer_secret');
$column->setLength(512);
return $schema;
}
}

return null;
}

public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) {
$tableName = 'user_oidc_providers';

// update secrets in user_oidc_providers and user_oidc_id4me
$qbUpdate = $this->connection->getQueryBuilder();
$qbUpdate->update($tableName)
->set('bearer_secret', $qbUpdate->createParameter('updateSecret'))
->where(
$qbUpdate->expr()->eq('id', $qbUpdate->createParameter('updateId'))
);

$qbSelect = $this->connection->getQueryBuilder();
$qbSelect->select('id', 'bearer_secret')
->from($tableName);
$req = $qbSelect->executeQuery();
while ($row = $req->fetch()) {
$id = $row['id'];
$secret = $row['bearer_secret'];
$encryptedSecret = $this->crypto->encrypt($secret);
$qbUpdate->setParameter('updateSecret', $encryptedSecret, IQueryBuilder::PARAM_STR);
$qbUpdate->setParameter('updateId', $id, IQueryBuilder::PARAM_INT);
$qbUpdate->executeStatement();
}
$req->closeCursor();
}
}
9 changes: 9 additions & 0 deletions src/components/SettingsForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@
:required="!update"
autocomplete="off">
</p>
<p>
<label for="oidc-bearer-secret">{{ t('user_oidc', 'Bearer shared secret') }}</label>
<input id="oidc-bearer-secret"
v-model="localProvider.bearerSecret"
:placeholder="update ? t('user_oidc', 'Leave empty to keep existing') : null"
type="text"
:required="!update"
autocomplete="off">
</p>
<p class="settings-hint warning-hint">
<AlertOutlineIcon :size="20" class="icon" />
{{ t('user_oidc', 'Warning, if the protocol of the URLs in the discovery content is HTTP, the ID token will be delivered through an insecure connection.') }}
Expand Down
Loading

0 comments on commit ce754c1

Please sign in to comment.