mirror of
https://github.com/nextcloud/server.git
synced 2026-02-21 17:01:34 -05:00
Merge pull request #50674 from nextcloud/backport/50193/stable29
[stable29] fix(oauth2): adjust db schemas when migrating from owncloud
This commit is contained in:
commit
71f2d7a13a
2 changed files with 96 additions and 6 deletions
|
|
@ -34,7 +34,6 @@
|
|||
*/
|
||||
namespace OC;
|
||||
|
||||
use OC\DB\Connection;
|
||||
use OC\DB\ConnectionAdapter;
|
||||
use OC\Repair\AddAppConfigLazyMigration;
|
||||
use OC\Repair\AddBruteForceCleanupJob;
|
||||
|
|
@ -188,7 +187,7 @@ class Repair implements IOutput {
|
|||
\OC::$server->getUserManager(),
|
||||
\OC::$server->getConfig()
|
||||
),
|
||||
new MigrateOauthTables(\OC::$server->get(Connection::class)),
|
||||
\OCP\Server::get(MigrateOauthTables::class),
|
||||
new FixMountStorages(\OC::$server->getDatabaseConnection()),
|
||||
new UpdateLanguageCodes(\OC::$server->getDatabaseConnection(), \OC::$server->getConfig()),
|
||||
new AddLogRotateJob(\OC::$server->getJobList()),
|
||||
|
|
|
|||
|
|
@ -20,11 +20,18 @@
|
|||
*/
|
||||
namespace OC\Repair\Owncloud;
|
||||
|
||||
use OC\Authentication\Token\IProvider as ITokenProvider;
|
||||
use OC\DB\Connection;
|
||||
use OC\DB\SchemaWrapper;
|
||||
use OCA\OAuth2\Db\AccessToken;
|
||||
use OCA\OAuth2\Db\AccessTokenMapper;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Authentication\Token\IToken;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\IRepairStep;
|
||||
use OCP\Security\ICrypto;
|
||||
use OCP\Security\ISecureRandom;
|
||||
|
||||
class MigrateOauthTables implements IRepairStep {
|
||||
/** @var Connection */
|
||||
|
|
@ -33,7 +40,14 @@ class MigrateOauthTables implements IRepairStep {
|
|||
/**
|
||||
* @param Connection $db
|
||||
*/
|
||||
public function __construct(Connection $db) {
|
||||
public function __construct(
|
||||
Connection $db,
|
||||
private AccessTokenMapper $accessTokenMapper,
|
||||
private ITokenProvider $tokenProvider,
|
||||
private ISecureRandom $random,
|
||||
private ITimeFactory $timeFactory,
|
||||
private ICrypto $crypto,
|
||||
) {
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
|
|
@ -51,14 +65,23 @@ class MigrateOauthTables implements IRepairStep {
|
|||
return;
|
||||
}
|
||||
|
||||
$output->info('Update the oauth2_access_tokens table schema.');
|
||||
// Create column and then migrate before handling unique index.
|
||||
// So that we can distinguish between legacy (from oc) and new rows (from nc).
|
||||
$table = $schema->getTable('oauth2_access_tokens');
|
||||
if (!$table->hasColumn('hashed_code')) {
|
||||
$output->info('Prepare the oauth2_access_tokens table schema.');
|
||||
$table->addColumn('hashed_code', 'string', [
|
||||
'notnull' => true,
|
||||
'length' => 128,
|
||||
]);
|
||||
|
||||
// Regenerate schema after migrating to it
|
||||
$this->db->migrateToSchema($schema->getWrappedSchema());
|
||||
$schema = new SchemaWrapper($this->db);
|
||||
}
|
||||
|
||||
$output->info('Update the oauth2_access_tokens table schema.');
|
||||
$table = $schema->getTable('oauth2_access_tokens');
|
||||
if (!$table->hasColumn('encrypted_token')) {
|
||||
$table->addColumn('encrypted_token', 'string', [
|
||||
'notnull' => true,
|
||||
|
|
@ -66,11 +89,31 @@ class MigrateOauthTables implements IRepairStep {
|
|||
]);
|
||||
}
|
||||
if (!$table->hasIndex('oauth2_access_hash_idx')) {
|
||||
// Drop legacy access codes first to prevent integrity constraint violations
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->delete('oauth2_access_tokens')
|
||||
->where($qb->expr()->eq('hashed_code', $qb->createNamedParameter('')));
|
||||
$qb->executeStatement();
|
||||
|
||||
$table->addUniqueIndex(['hashed_code'], 'oauth2_access_hash_idx');
|
||||
}
|
||||
if (!$table->hasIndex('oauth2_access_client_id_idx')) {
|
||||
$table->addIndex(['client_id'], 'oauth2_access_client_id_idx');
|
||||
}
|
||||
if (!$table->hasColumn('token_id')) {
|
||||
$table->addColumn('token_id', 'integer', [
|
||||
'notnull' => true,
|
||||
]);
|
||||
}
|
||||
if ($table->hasColumn('expires')) {
|
||||
$table->dropColumn('expires');
|
||||
}
|
||||
if ($table->hasColumn('user_id')) {
|
||||
$table->dropColumn('user_id');
|
||||
}
|
||||
if ($table->hasColumn('token')) {
|
||||
$table->dropColumn('token');
|
||||
}
|
||||
|
||||
$output->info('Update the oauth2_clients table schema.');
|
||||
$table = $schema->getTable('oauth2_clients');
|
||||
|
|
@ -114,10 +157,10 @@ class MigrateOauthTables implements IRepairStep {
|
|||
$table->addIndex(['client_identifier'], 'oauth2_client_id_idx');
|
||||
}
|
||||
|
||||
$this->db->migrateToSchema($schema->getWrappedSchema());
|
||||
|
||||
// Regenerate schema after migrating to it
|
||||
$this->db->migrateToSchema($schema->getWrappedSchema());
|
||||
$schema = new SchemaWrapper($this->db);
|
||||
|
||||
if ($schema->getTable('oauth2_clients')->hasColumn('identifier')) {
|
||||
$output->info("Move identifier column's data to the new client_identifier column.");
|
||||
// 1. Fetch all [id, identifier] couple.
|
||||
|
|
@ -139,7 +182,10 @@ class MigrateOauthTables implements IRepairStep {
|
|||
$output->info('Drop the identifier column.');
|
||||
$table = $schema->getTable('oauth2_clients');
|
||||
$table->dropColumn('identifier');
|
||||
|
||||
// Regenerate schema after migrating to it
|
||||
$this->db->migrateToSchema($schema->getWrappedSchema());
|
||||
$schema = new SchemaWrapper($this->db);
|
||||
}
|
||||
|
||||
$output->info('Delete clients (and their related access tokens) with the redirect_uri starting with oc:// or ending with *');
|
||||
|
|
@ -172,5 +218,50 @@ class MigrateOauthTables implements IRepairStep {
|
|||
$qbDeleteClients->expr()->iLike('redirect_uri', $qbDeleteClients->createNamedParameter('%*', IQueryBuilder::PARAM_STR))
|
||||
);
|
||||
$qbDeleteClients->executeStatement();
|
||||
|
||||
// Migrate legacy refresh tokens from oc
|
||||
if ($schema->hasTable('oauth2_refresh_tokens')) {
|
||||
$output->info('Migrate legacy oauth2 refresh tokens.');
|
||||
|
||||
$qbSelect = $this->db->getQueryBuilder();
|
||||
$qbSelect->select('*')
|
||||
->from('oauth2_refresh_tokens');
|
||||
$result = $qbSelect->executeQuery();
|
||||
$now = $this->timeFactory->now()->getTimestamp();
|
||||
$index = 0;
|
||||
while ($row = $result->fetch()) {
|
||||
$clientId = $row['client_id'];
|
||||
$refreshToken = $row['token'];
|
||||
|
||||
// Insert expired token so that it can be rotated on the next refresh
|
||||
$accessToken = $this->random->generate(72, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
|
||||
$authToken = $this->tokenProvider->generateToken(
|
||||
$accessToken,
|
||||
$row['user_id'],
|
||||
$row['user_id'],
|
||||
null,
|
||||
"oc_migrated_client${clientId}_t{$now}_i$index",
|
||||
IToken::PERMANENT_TOKEN,
|
||||
IToken::DO_NOT_REMEMBER,
|
||||
);
|
||||
$authToken->setExpires($now - 3600);
|
||||
$this->tokenProvider->updateToken($authToken);
|
||||
|
||||
$accessTokenEntity = new AccessToken();
|
||||
$accessTokenEntity->setTokenId($authToken->getId());
|
||||
$accessTokenEntity->setClientId($clientId);
|
||||
$accessTokenEntity->setHashedCode(hash('sha512', $refreshToken));
|
||||
$accessTokenEntity->setEncryptedToken($this->crypto->encrypt($accessToken, $refreshToken));
|
||||
$accessTokenEntity->setCodeCreatedAt($now);
|
||||
$accessTokenEntity->setTokenCount(1);
|
||||
$this->accessTokenMapper->insert($accessTokenEntity);
|
||||
|
||||
$index++;
|
||||
}
|
||||
$result->closeCursor();
|
||||
|
||||
$schema->dropTable('oauth2_refresh_tokens');
|
||||
$schema->performDropTableCalls();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue