Skip to content

Commit

Permalink
introduce user:sync [--uid foo|--seenOnly|--showCount]
Browse files Browse the repository at this point in the history
  • Loading branch information
butonic authored and Tom Needham committed Mar 13, 2018
1 parent 1d96a66 commit 0730372
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 71 deletions.
158 changes: 117 additions & 41 deletions core/Command/User/SyncBackend.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php
/**
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @copyright Copyright (c) 2018, ownCloud GmbH
Expand All @@ -23,6 +24,8 @@


use OC\User\AccountMapper;
use OC\User\Sync\AllUsersIterator;
use OC\User\Sync\SeenUsersIterator;
use OC\User\SyncService;
use OCP\IConfig;
use OCP\ILogger;
Expand Down Expand Up @@ -80,6 +83,24 @@ protected function configure() {
InputOption::VALUE_NONE,
'List all known backend classes'
)
->addOption(
'uid',
'u',
InputOption::VALUE_REQUIRED,
'sync only the user with the given user id'
)
->addOption(
'seenOnly',
's',
InputOption::VALUE_NONE,
'sync only seen users'
)
->addOption(
'showCount',
'c',
InputOption::VALUE_NONE,
'calculate user count before syncing'
)
->addOption(
'missing-account-action',
'm',
Expand All @@ -97,12 +118,12 @@ protected function execute(InputInterface $input, OutputInterface $output) {
return 0;
}
$backendClassName = $input->getArgument('backend-class');
if (is_null($backendClassName)) {
$output->writeln("<error>No backend class name given. Please run ./occ help user:sync to understand how this command works.</error>");
if ($backendClassName === null) {
$output->writeln('<error>No backend class name given. Please run ./occ help user:sync to understand how this command works.</error>');
return 1;
}
$backend = $this->getBackend($backendClassName);
if (is_null($backend)) {
if ($backend === null) {
$output->writeln("<error>The backend <$backendClassName> does not exist. Did you miss to enable the app?</error>");
return 1;
}
Expand All @@ -116,7 +137,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
if ($input->getOption('missing-account-action') !== null) {
$missingAccountsAction = $input->getOption('missing-account-action');
if (!in_array($missingAccountsAction, $validActions, true)) {
$output->writeln("<error>Unknown action. Choose between \"disable\" or \"remove\"</error>");
$output->writeln('<error>Unknown action. Choose between "disable" or "remove"</error>');
return 1;
}
} else {
Expand All @@ -132,27 +153,91 @@ protected function execute(InputInterface $input, OutputInterface $output) {

$syncService = new SyncService($this->config, $this->logger, $this->accountMapper);

// analyse unknown users
$this->handleUnknownUsers($input, $output, $backend, $syncService, $missingAccountsAction, $validActions);
$uid = $input->getOption('uid');

if ($uid) {
$this->syncSingleUser($input, $output, $syncService, $backend, $uid, $missingAccountsAction, $validActions);
} else {
$this->syncMultipleUsers($input, $output, $syncService, $backend, $missingAccountsAction, $validActions);
}

return 0;
}

// insert/update known users
$output->writeln("Insert new and update existing users ...");

/**
* @param InputInterface $input
* @param OutputInterface $output
* @param SyncService $syncService
* @param UserInterface $backend
* @param string $missingAccountsAction
* @param array $validActions
*/
private function syncMultipleUsers (
InputInterface $input,
OutputInterface $output,
SyncService $syncService,
UserInterface $backend,
$missingAccountsAction,
array $validActions
) {
$output->writeln('Analyse unknown users ...');
$p = new ProgressBar($output);
$unknownUsers = $syncService->getNoLongerExistingUsers($backend, function () use ($p) {
$p->advance();
});
$p->finish();
$output->writeln('');
$output->writeln('');
$this->handleUnknownUsers($unknownUsers, $input, $output, $missingAccountsAction, $validActions);

$output->writeln('Insert new and update existing users ...');
$p = new ProgressBar($output);
$max = null;
if ($backend->implementsActions(\OC_User_Backend::COUNT_USERS)) {
if ($backend->implementsActions(\OC_User_Backend::COUNT_USERS) && $input->getOption('showCount')) {
$max = $backend->countUsers();
}
$p->start($max);
$syncService->run($backend, function () use ($p) {

if ($input->getOption('seenOnly')) {
$iterator = new SeenUsersIterator($this->accountMapper, get_class($backend));
} else {
$iterator = new AllUsersIterator($backend);
}
$syncService->run($backend, $iterator, function () use ($p) {
$p->advance();
});
$p->finish();
$output->writeln('');
$output->writeln('');

return 0;
}

/**
* @param InputInterface $input
* @param OutputInterface $output
* @param SyncService $syncService
* @param UserInterface $backend
* @param string $uid
* @param string $missingAccountsAction
* @param array $validActions
*/
private function syncSingleUser(
InputInterface $input,
OutputInterface $output,
SyncService $syncService,
UserInterface $backend,
$uid,
$missingAccountsAction,
array $validActions
) {
$output->writeln("Syncing $uid ...");
if (!$backend->userExists($uid)) {
$this->handleUnknownUsers([$uid], $input, $output, $missingAccountsAction, $validActions);
} else {
// sync
$syncService->run($backend, new \ArrayIterator([$uid]), function (){});
}
}
/**
* @param $backend
* @return null|UserInterface
Expand Down Expand Up @@ -187,58 +272,49 @@ private function doActionForAccountUids(array $uids, callable $callbackExists, c
}

/**
* @param string[] $unknownUsers
* @param InputInterface $input
* @param OutputInterface $output
* @param UserInterface $backend
* @param SyncService $syncService
* @param $missingAccountsAction
* @param $validActions
*/
private function handleUnknownUsers(InputInterface $input, OutputInterface $output, UserInterface $backend, SyncService $syncService, $missingAccountsAction, $validActions) {
$output->writeln("Analyse unknown users ...");
$p = new ProgressBar($output);
$toBeDeleted = $syncService->getNoLongerExistingUsers($backend, function () use ($p) {
$p->advance();
});
$p->finish();
$output->writeln('');
$output->writeln('');
private function handleUnknownUsers(array $unknownUsers, InputInterface $input, OutputInterface $output, $missingAccountsAction, $validActions) {

if (empty($toBeDeleted)) {
$output->writeln("No unknown users have been detected.");
if (empty($unknownUsers)) {
$output->writeln('No unknown users have been detected.');
} else {
$output->writeln("Following users are no longer known with the connected backend.");
$output->writeln('Following users are no longer known with the connected backend.');
switch ($missingAccountsAction) {
case 'disable':
$output->writeln("Proceeding to disable the accounts");
$this->doActionForAccountUids($toBeDeleted,
$output->writeln('Proceeding to disable the accounts');
$this->doActionForAccountUids($unknownUsers,
function ($uid, IUser $ac) use ($output) {
$ac->setEnabled(false);
$output->writeln($uid);
},
function ($uid) use ($output) {
$output->writeln($uid . " (unknown account for the user)");
$output->writeln("$uid (unknown account for the user)");
});
break;
case 'remove':
$output->writeln("Proceeding to remove the accounts");
$this->doActionForAccountUids($toBeDeleted,
$output->writeln('Proceeding to remove the accounts');
$this->doActionForAccountUids($unknownUsers,
function ($uid, IUser $ac) use ($output) {
$ac->delete();
$output->writeln($uid);
},
function ($uid) use ($output) {
$output->writeln($uid . " (unknown account for the user)");
$output->writeln("$uid (unknown account for the user)");
});
break;
case 'ask later':
$output->writeln("listing the unknown accounts");
$this->doActionForAccountUids($toBeDeleted,
$output->writeln('listing the unknown accounts');
$this->doActionForAccountUids($unknownUsers,
function ($uid) use ($output) {
$output->writeln($uid);
},
function ($uid) use ($output) {
$output->writeln($uid . " (unknown account for the user)");
$output->writeln("$uid (unknown account for the user)");
});
// overwriting variables!
$helper = $this->getHelper('question');
Expand All @@ -251,23 +327,23 @@ function ($uid) use ($output) {
switch ($missingAccountsAction2) {
// if "nothing" is selected, just ignore and finish
case 'disable':
$output->writeln("Proceeding to disable the accounts");
$this->doActionForAccountUids($toBeDeleted,
$output->writeln('Proceeding to disable the accounts');
$this->doActionForAccountUids($unknownUsers,
function ($uid, IUser $ac) {
$ac->setEnabled(false);
},
function ($uid) use ($output) {
$output->writeln($uid . " (unknown account for the user)");
$output->writeln("$uid (unknown account for the user)");
});
break;
case 'remove':
$output->writeln("Proceeding to remove the accounts");
$this->doActionForAccountUids($toBeDeleted,
$output->writeln('Proceeding to remove the accounts');
$this->doActionForAccountUids($unknownUsers,
function ($uid, IUser $ac) {
$ac->delete();
},
function ($uid) use ($output) {
$output->writeln($uid . " (unknown account for the user)");
$output->writeln("$uid (unknown account for the user)");
});
break;
}
Expand Down
3 changes: 2 additions & 1 deletion core/Migrations/Version20170221114437.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use OC\User\AccountMapper;
use OC\User\AccountTermMapper;
use OC\User\Database;
use OC\User\Sync\AllUsersIterator;
use OC\User\SyncService;
use OCP\Migration\ISimpleMigration;
use OCP\Migration\IOutput;
Expand All @@ -24,7 +25,7 @@ public function run(IOutput $out) {
// insert/update known users
$out->info("Insert new users ...");
$out->startProgress($backend->countUsers());
$syncService->run($backend, function () use ($out) {
$syncService->run($backend, new AllUsersIterator($backend), function () use ($out) {
$out->advance();
});
$out->finishProgress();
Expand Down
32 changes: 32 additions & 0 deletions lib/private/User/AccountMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,36 @@ public function callForAllUsers($callback, $search, $onlySeen) {
$stmt->closeCursor();
}

/**
* @param string $backend
* @param bool $hasLoggedIn
* @param integer $limit
* @param integer $offset
* @return string[]
*/
public function findUserIds($backend = null, $hasLoggedIn = null, $limit = null, $offset = null) {
$qb = $this->db->getQueryBuilder();
$qb->select('user_id')
->from($this->getTableName());
if ($backend !== null) {
$qb->andWhere($qb->expr()->eq('backend', $qb->createNamedParameter($backend)));
}
if ($hasLoggedIn === true) {
$qb->andWhere($qb->expr()->gt('last_login', new Literal(0)));
} else if ($hasLoggedIn === false) {
$qb->andWhere($qb->expr()->eq('last_login', new Literal(0)));
}
if ($limit !== null) {
$qb->setMaxResults($limit);
}
if ($offset !== null) {
$qb->setFirstResult($offset);
}

$stmt = $qb->execute();
$rows = $stmt->fetchAll();
$stmt->closeCursor();
return $rows;
}

}
48 changes: 48 additions & 0 deletions lib/private/User/Sync/AllUsersIterator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
/**
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
*
* @copyright Copyright (c) 2018, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\User\Sync;

use OCP\UserInterface;

class AllUsersIterator extends UsersIterator {
/**
* @var UserInterface
*/
private $backend;

public function __construct(UserInterface $backend) {
$this->backend = $backend;
}

public function rewind() {
parent::rewind();
$this->data = $this->backend->getUsers('', self::LIMIT, 0);
}

public function next() {
$this->position++;
if ($this->currentDataPos() === 0) {
$this->page++;
$offset = $this->page * self::LIMIT;
$this->data = $this->backend->getUsers('', self::LIMIT, $offset);
}
}
}
Loading

0 comments on commit 0730372

Please sign in to comment.