Skip to content

Commit

Permalink
API Update API to reflect changes to CLI interaction
Browse files Browse the repository at this point in the history
  • Loading branch information
GuySartorelli committed Sep 16, 2024
1 parent 9750358 commit 93bab52
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 157 deletions.
28 changes: 14 additions & 14 deletions docs/en/developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,8 @@ See the [module docs](https://github.com/symbiote/silverstripe-queuedjobs) on ho

If you don't want to run a queued job, you can set a cronjob yourself by calling:

```
env php vendor/silverstripe/framework/cli-script.php dev/tasks/LDAPMemberSyncTask
```sh
vendor/bin/sake tasks:LDAPMemberSyncTask
```

### Syncing AD groups and users on a schedule
Expand All @@ -365,8 +365,8 @@ SilverStripe\LDAP\Jobs\LDAPAllSyncJob:
If you don't want to run a queued job, you can set a cronjob yourself by calling the two sync tasks (order is important, otherwise your group memberships might not get updated):

```sh
env php vendor/silverstripe/framework/cli-script.php dev/tasks/LDAPGroupSyncTask
env php vendor/silverstripe/framework/cli-script.php dev/tasks/LDAPMemberSyncTask
vendor/bin/sake tasks:LDAPGroupSyncTask
vendor/bin/sake tasks:LDAPMemberSyncTask
```

### Migrating existing users
Expand Down Expand Up @@ -438,9 +438,9 @@ The fallback authenticator will be used in the following conditions:

### Extending the member and group sync tasks with custom functionality

Both `LDAPMemberSyncTask` and `LDAPGroupSyncTask` provide extension points (`onAfterLDAPMemberSyncTask` and
`onAfterLDAPGroupSyncTask` respectively) after all members/groups have been synced and before the task exits. This is a
perfect time to set values that are dependent on a full sync - for example linking a user to their manager based on DNs.
Both `LDAPMemberSyncTask` and `LDAPGroupSyncTask` provide extension points (`onAfterLDAPMemberSyncTask` and
`onAfterLDAPGroupSyncTask` respectively) after all members/groups have been synced and before the task exits. This is a
perfect time to set values that are dependent on a full sync - for example linking a user to their manager based on DNs.
For example:

```yaml
Expand All @@ -462,7 +462,7 @@ use SilverStripe\Security\Member;
class LDAPMemberSyncExtension extends Extension
{
/**
* Assuming the `DN` and `ManagerDN` values are set by LDAP, this code will link a member with their manager and
* Assuming the `DN` and `ManagerDN` values are set by LDAP, this code will link a member with their manager and
* store the link in the `Manager` has_one.
*/
protected function onAfterLDAPMemberSyncTask()
Expand Down Expand Up @@ -505,19 +505,19 @@ This will allow users to change their AD password via the regular CMS "forgot pa
### Allow SilverStripe attributes to be reset (removed) by AD
By default if attributes are present, and then missing in subsequent requests, they are ignored (non-destructive) by
By default if attributes are present, and then missing in subsequent requests, they are ignored (non-destructive) by
this module. This can cause attributes to persist when they've been deliberately removed (attribute is no longer present)
in the LDAP source data.
in the LDAP source data.
If you wish a full two way sync to occur, then set the attribute on `LDAPService` for `reset_missing_attributes` to
enable a full sync.
If you wish a full two way sync to occur, then set the attribute on `LDAPService` for `reset_missing_attributes` to
enable a full sync.

*Note*: This will mean syncs are destructive, and data or attributes will be reset if missing from the master LDAP source
data.
data.

```yaml
SilverStripe\LDAP\Services\LDAPService:
reset_missing_attributes: true
reset_missing_attributes: true
```

### Writing LDAP data from SilverStripe
Expand Down
10 changes: 8 additions & 2 deletions src/Jobs/LDAPAllSyncJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
use SilverStripe\LDAP\Tasks\LDAPGroupSyncTask;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\PolyExecution\PolyOutput;
use SilverStripe\LDAP\Tasks\LDAPMemberSyncTask;
use Symbiote\QueuedJobs\Services\AbstractQueuedJob;
use Symbiote\QueuedJobs\Services\QueuedJob;
use Symbiote\QueuedJobs\Services\QueuedJobService;
use Symfony\Component\Console\Input\ArrayInput;

/**
* Class LDAPAllSyncJob
Expand Down Expand Up @@ -84,11 +86,15 @@ public function process()
singleton(QueuedJobService::class)->queueJob($nextJob, date('Y-m-d H:i:s', time() + $regenerateTime));
}

$output = PolyOutput::create(PolyOutput::FORMAT_ANSI);
$input = new ArrayInput([]);
$input->setInteractive(false);

$task = Injector::inst()->create(LDAPGroupSyncTask::class);
$task->run(null);
$task->run($input, $output);

$task = Injector::inst()->create(LDAPMemberSyncTask::class);
$task->run(null);
$task->run($input, $output);

$this->isComplete = true;
}
Expand Down
7 changes: 6 additions & 1 deletion src/Jobs/LDAPMemberSyncJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
use SilverStripe\LDAP\Tasks\LDAPMemberSyncTask;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\PolyExecution\PolyOutput;
use Symbiote\QueuedJobs\Services\AbstractQueuedJob;
use Symbiote\QueuedJobs\Services\QueuedJob;
use Symbiote\QueuedJobs\Services\QueuedJobService;
use Symfony\Component\Console\Input\ArrayInput;

/**
* Class LDAPMemberSyncJob
Expand Down Expand Up @@ -89,7 +91,10 @@ public function process()
}

$task = Injector::inst()->create(LDAPMemberSyncTask::class);
$task->run(null);
$output = PolyOutput::create(PolyOutput::FORMAT_ANSI);
$input = new ArrayInput([]);
$input->setInteractive(false);
$task->run($input, $output);

$this->isComplete = true;
}
Expand Down
56 changes: 16 additions & 40 deletions src/Tasks/LDAPGroupSyncTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

use Exception;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Dev\BuildTask;
use SilverStripe\PolyExecution\PolyOutput;
use SilverStripe\LDAP\Services\LDAPService;
use SilverStripe\ORM\DB;
use SilverStripe\Security\Group;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;

/**
* Class LDAPGroupSyncTask
Expand All @@ -17,11 +19,7 @@
*/
class LDAPGroupSyncTask extends BuildTask
{
/**
* {@inheritDoc}
* @var string
*/
private static $segment = 'LDAPGroupSyncTask';
protected static string $commandName = 'LDAPGroupSyncTask';

/**
* @var array
Expand All @@ -44,19 +42,12 @@ class LDAPGroupSyncTask extends BuildTask
*/
protected $ldapService;

/**
* @return string
*/
public function getTitle()
public function getTitle(): string
{
return _t(__CLASS__ . '.SYNCTITLE', 'Sync all groups from LDAP');
}

/**
* {@inheritDoc}
* @var HTTPRequest $request
*/
public function run($request)
protected function execute(InputInterface $input, PolyOutput $output): int
{
ini_set('max_execution_time', 900);

Expand All @@ -70,8 +61,6 @@ public function run($request)
'objectguid'
);

$start = time();

$created = 0;
$updated = 0;
$deleted = 0;
Expand All @@ -84,14 +73,14 @@ public function run($request)
$group = new Group();
$group->GUID = $data['objectguid'];

$this->log(sprintf(
$output->writeln(sprintf(
'Creating new Group (GUID: %s, sAMAccountName: %s)',
$data['objectguid'],
$data['samaccountname']
));
$created++;
} else {
$this->log(sprintf(
$output->writeln(sprintf(
'Updating existing Group "%s" (ID: %s, GUID: %s, sAMAccountName: %s)',
$group->getTitle(),
$group->ID,
Expand All @@ -104,7 +93,7 @@ public function run($request)
try {
$this->ldapService->updateGroupFromLDAP($group, $data);
} catch (Exception $e) {
$this->log($e->getMessage());
$output->writeln('<error>' . $e->getMessage() . '</>');
continue;
}
}
Expand All @@ -116,7 +105,7 @@ public function run($request)
if (!isset($ldapGroups[$record['GUID']])) {
$group = Group::get()->byId($record['ID']);

$this->log(sprintf(
$output->writeln(sprintf(
'Removing Group "%s" (GUID: %s) that no longer exists in LDAP.',
$group->Title,
$group->GUID
Expand All @@ -129,7 +118,7 @@ public function run($request)
}
$group->delete();
} catch (Exception $e) {
$this->log($e->getMessage());
$output->writeln('<error>' . $e->getMessage() . '</>');
continue;
}

Expand All @@ -138,28 +127,15 @@ public function run($request)
}
}

$this->invokeWithExtensions('onAfterLDAPGroupSyncTask');
$this->invokeWithExtensions('onAfterLDAPGroupSyncTask', $output);

$end = time() - $start;

$this->log(sprintf(
'Done. Created %s records. Updated %s records. Deleted %s records. Duration: %s seconds',
$output->writeln(sprintf(
'Done. Created %s records. Updated %s records. Deleted %s records.',
$created,
$updated,
$deleted,
round($end ?? 0.0, 0)
$deleted
));
}

/**
* Sends a message, formatted either for the CLI or browser
*
* @param string $message
*/
protected function log($message)
{
$message = sprintf('[%s] ', date('Y-m-d H:i:s')) . $message;
echo Director::is_cli() ? ($message . PHP_EOL) : ($message . '<br>');
return Command::SUCCESS;
}

/**
Expand Down
56 changes: 28 additions & 28 deletions src/Tasks/LDAPMemberSyncOneTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,23 @@
namespace SilverStripe\LDAP\Tasks;

use Exception;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\PolyExecution\PolyOutput;
use SilverStripe\LDAP\Services\LDAPService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;

/**
* Class LDAPMemberSyncOneTask
* @package SilverStripe\LDAP\Tasks
*
* Debug build task that can be used to sync a single member by providing their email address registered in LDAP.
*
* Usage: /dev/tasks/LDAPMemberSyncOneTask?mail[email protected]
* Usage: sake tasks:LDAPMemberSyncOneTask --email[email protected]
*/
class LDAPMemberSyncOneTask extends LDAPMemberSyncTask
{
/**
* {@inheritDoc}
* @var string
*/
private static $segment = 'LDAPMemberSyncOneTask';
protected static string $commandName = 'LDAPMemberSyncOneTask';

/**
* @var array
Expand All @@ -34,64 +33,58 @@ class LDAPMemberSyncOneTask extends LDAPMemberSyncTask
*/
protected $ldapService;

/**
* @return string
*/
public function getTitle()
public function getTitle(): string
{
return _t(__CLASS__ . '.SYNCONETITLE', 'Sync single user from LDAP');
}

/**
* Syncs a single user based on the email address passed in the URL
*
* @param HTTPRequest $request
*/
public function run($request)
protected function execute(InputInterface $input, PolyOutput $output): int
{
$email = $request->getVar('email');
$email = $input->getOption('email');

if (!$email) {
echo 'You must supply an email parameter to this method.', PHP_EOL;
exit;
$output->writeln('<error>You must supply an email address.</>');
return Command::INVALID;
}

$user = $this->ldapService->getUserByEmail($email);

if (!$user) {
echo sprintf('No user found in LDAP for email %s', $email), PHP_EOL;
exit;
$output->writeln(sprintf('<error>No user found in LDAP for email %s</>', $email));
return Command::FAILURE;
}

$member = $this->findOrCreateMember($user);

// If member exists already, we're updating - otherwise we're creating
if ($member->exists()) {
$this->log(sprintf(
$output->writeln(sprintf(
'Updating existing Member %s: "%s" (ID: %s, SAM Account Name: %s)',
$user['objectguid'],
$member->getName(),
$member->ID,
$user['samaccountname']
));
} else {
$this->log(sprintf(
$output->writeln(sprintf(
'Creating new Member %s: "%s" (SAM Account Name: %s)',
$user['objectguid'],
$user['cn'],
$user['samaccountname']
));
}

$this->log('User data returned from LDAP follows:');
$this->log(var_export($user));
$output->writeln('User data returned from LDAP follows:');
$output->writeln(var_export($user, true));

try {
$this->ldapService->updateMemberFromLDAP($member, $user);
$this->log('Done!');
} catch (Exception $e) {
$this->log($e->getMessage());
$output->writeln('<error>' . $e->getMessage() . '</>');
return Command::FAILURE;
}

return Command::SUCCESS;
}

/**
Expand All @@ -103,4 +96,11 @@ public function setLDAPService(LDAPService $service)
$this->ldapService = $service;
return $this;
}

public function getOptions(): array
{
return [
new InputOption('email', null, InputOption::VALUE_REQUIRED, 'Email address of the member to sync (required)'),
];
}
}
Loading

0 comments on commit 93bab52

Please sign in to comment.