Skip to content

Commit 0e48800

Browse files
committed
feat(GroupMigration): Add dry run mode to command
And fix detection of old group. Signed-off-by: Carl Schwan <carlschwan@kde.org>
1 parent 8ade297 commit 0e48800

2 files changed

Lines changed: 48 additions & 10 deletions

File tree

lib/Command/GroupMigrationCopyIncomplete.php

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use OCA\User_SAML\Service\GroupMigration;
1212
use Psr\Log\LoggerInterface;
1313
use Symfony\Component\Console\Input\InputInterface;
14+
use Symfony\Component\Console\Input\InputOption;
1415
use Symfony\Component\Console\Output\OutputInterface;
1516
use Throwable;
1617

@@ -23,20 +24,30 @@ public function __construct(
2324
}
2425
#[\Override]
2526
protected function configure(): void {
26-
$this->setName('saml:group-migration:copy-incomplete-members');
27-
$this->setDescription('Transfers remaining group members from old local to current SAML groups');
27+
$this->setName('saml:group-migration:copy-incomplete-members')
28+
->setDescription('Transfers remaining group members from old local to current SAML groups')
29+
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Output the SQL queries instead of running them.');
2830
}
2931

3032
protected function execute(InputInterface $input, OutputInterface $output): int {
33+
$dryRun = $input->getOption('dry-run');
34+
3135
$groupsToTreat = $this->groupMigration->findGroupsWithLocalMembers();
3236
if (empty($groupsToTreat)) {
33-
if ($output->isVerbose()) {
37+
if ($output->isVerbose() || $dryRun) {
3438
$output->writeln('<info>No pending group member transfer</info>');
3539
}
3640
return 0;
3741
}
3842

39-
if (!$this->doMemberTransfer($groupsToTreat, $output)) {
43+
if ($dryRun) {
44+
$output->writeln('<info>Found the following SAML group with a corresponding local group:</info>');
45+
foreach ($groupsToTreat as $group) {
46+
$output->writeln('<info>- ' . $group . '</info>');
47+
}
48+
}
49+
50+
if (!$this->doMemberTransfer($groupsToTreat, $output, $dryRun)) {
4051
if (!$output->isQuiet()) {
4152
$output->writeln('<comment>Not all group members could be transferred completely. Rerun this command or check the Nextcloud log.</comment>');
4253
}
@@ -54,16 +65,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int
5465
* @param OutputInterface $output
5566
* @return bool
5667
*/
57-
protected function doMemberTransfer(array $groups, OutputInterface $output): bool {
68+
protected function doMemberTransfer(array $groups, OutputInterface $output, bool $dryRun): bool {
5869
$errorOccurred = false;
5970
for ($i = 0; $i < 2; $i++) {
6071
$retry = [];
6172
foreach ($groups as $gid) {
6273
try {
63-
$isComplete = $this->groupMigration->migrateGroupUsers($gid);
74+
$isComplete = $this->groupMigration->migrateGroupUsers($gid, $output, $dryRun);
6475
if (!$isComplete) {
6576
$retry[] = $gid;
66-
} else {
77+
} elseif (!$dryRun) {
6778
$this->groupMigration->cleanUpOldGroupUsers($gid);
6879
if ($output->isVerbose()) {
6980
$output->writeln(sprintf('<info>Members transferred successfully for group %s</info>', $gid));

lib/Service/GroupMigration.php

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
namespace OCA\User_SAML\Service;
99

1010
use OCA\User_SAML\GroupBackend;
11+
use OCA\User_SAML\SAMLSettings;
1112
use OCP\AppFramework\Db\TTransactional;
1213
use OCP\DB\Exception;
1314
use OCP\DB\QueryBuilder\IQueryBuilder;
1415
use OCP\IDBConnection;
1516
use OCP\IGroupManager;
1617
use OCP\IUser;
1718
use Psr\Log\LoggerInterface;
19+
use Symfony\Component\Console\Output\OutputInterface;
1820
use Throwable;
1921

2022
class GroupMigration {
@@ -42,6 +44,15 @@ public function findGroupsWithLocalMembers(): array {
4244
->where($qb->expr()->in('gid', $qb->createParameter('gidList')));
4345

4446
$allOwnedGroups = $this->ownGroupBackend->getGroups();
47+
48+
// Remove prefix from group names
49+
$allOwnedGroups = array_merge($allOwnedGroups, array_map(function (string $groupName): string {
50+
if (substr($groupName, 0, strlen(SAMLSettings::DEFAULT_GROUP_PREFIX)) == SAMLSettings::DEFAULT_GROUP_PREFIX) {
51+
$groupName = substr($groupName, strlen(SAMLSettings::DEFAULT_GROUP_PREFIX));
52+
}
53+
return $groupName;
54+
}, $allOwnedGroups));
55+
4556
foreach (array_chunk($allOwnedGroups, self::CHUNK_SIZE) as $groupsChunk) {
4657
$qb->setParameter('gidList', $groupsChunk, IQueryBuilder::PARAM_STR_ARRAY);
4758
$result = $qb->executeQuery();
@@ -59,19 +70,35 @@ public function findGroupsWithLocalMembers(): array {
5970
* @throws Exception
6071
* @throws Throwable
6172
*/
62-
public function migrateGroupUsers(string $gid): bool {
73+
public function migrateGroupUsers(string $gid, ?OutputInterface $output = null, bool $dryRun = false): bool {
6374
$originalGroup = $this->groupManager->get($gid);
6475
$members = $originalGroup?->getUsers();
6576

77+
$newGid = $gid;
78+
if (!$this->ownGroupBackend->groupExists($gid)) {
79+
if ($this->ownGroupBackend->groupExists(SAMLSettings::DEFAULT_GROUP_PREFIX . $gid)) {
80+
$newGid = SAMLSettings::DEFAULT_GROUP_PREFIX . $gid;
81+
} else {
82+
$output->writeln("SAML group corresponding to the local $gid group does not exist");
83+
return true;
84+
}
85+
}
86+
87+
if ($dryRun) {
88+
assert($output instanceof OutputInterface);
89+
$output->writeln('Found ' . count($members) . ' members in old local group ' . $gid . ' and migrating them to ' . $newGid);
90+
return true;
91+
}
92+
6693
$areAllInserted = true;
6794
foreach (array_chunk($members ?? [], (int)floor(self::CHUNK_SIZE / 2)) as $userBatch) {
68-
$areAllInserted = ($this->atomic(function () use ($userBatch, $gid) {
95+
$areAllInserted = ($this->atomic(function () use ($userBatch, $newGid) {
6996
/** @var IUser $user */
7097
foreach ($userBatch as $user) {
7198
$this->dbc->insertIgnoreConflict(
7299
GroupBackend::TABLE_MEMBERS,
73100
[
74-
'gid' => $gid,
101+
'gid' => $newGid,
75102
'uid' => $user->getUID(),
76103
]
77104
);

0 commit comments

Comments
 (0)