Issues (70)

src/Tasks/LDAPGroupSyncTask.php (2 issues)

1
<?php
2
3
namespace SilverStripe\LDAP\Tasks;
4
5
use Exception;
6
use SilverStripe\Control\Director;
7
use SilverStripe\Control\HTTPRequest;
8
use SilverStripe\Dev\BuildTask;
9
use SilverStripe\LDAP\Services\LDAPService;
10
use SilverStripe\ORM\DB;
11
use SilverStripe\Security\Group;
12
13
/**
14
 * Class LDAPGroupSyncTask
15
 *
16
 * A task to sync all groups from a specific DN in LDAP to the SilverStripe site in Group models
17
 */
18
class LDAPGroupSyncTask extends BuildTask
19
{
20
    /**
21
     * {@inheritDoc}
22
     * @var string
23
     */
24
    private static $segment = 'LDAPGroupSyncTask';
0 ignored issues
show
The private property $segment is not used, and could be removed.
Loading history...
25
26
    /**
27
     * @var array
28
     */
29
    private static $dependencies = [
0 ignored issues
show
The private property $dependencies is not used, and could be removed.
Loading history...
30
        'LDAPService' => '%$' . LDAPService::class,
31
    ];
32
33
    /**
34
     * Setting this to true causes the sync to delete any local Group
35
     * records that were previously imported, but no longer existing in LDAP.
36
     *
37
     * @config
38
     * @var bool
39
     */
40
    private static $destructive = false;
41
42
    /**
43
     * @var LDAPService
44
     */
45
    protected $ldapService;
46
47
    /**
48
     * @return string
49
     */
50
    public function getTitle()
51
    {
52
        return _t(__CLASS__ . '.SYNCTITLE', 'Sync all groups from LDAP');
53
    }
54
55
    /**
56
     * {@inheritDoc}
57
     * @var HTTPRequest $request
58
     */
59
    public function run($request)
60
    {
61
        ini_set('max_execution_time', 900);
62
63
        // get all groups from LDAP, but only get the attributes we need.
64
        // this is useful to avoid holding onto too much data in memory
65
        // especially in the case where getGroups() would return a lot of groups
66
        $ldapGroups = $this->ldapService->getGroups(
67
            false,
68
            ['objectguid', 'samaccountname', 'dn', 'name', 'description'],
69
            // Change the indexing attribute so we can look up by GUID during the deletion process below.
70
            'objectguid'
71
        );
72
73
        $start = time();
74
75
        $created = 0;
76
        $updated = 0;
77
        $deleted = 0;
78
79
        foreach ($ldapGroups as $data) {
80
            $group = Group::get()->filter('GUID', $data['objectguid'])->limit(1)->first();
81
82
            if (!($group && $group->exists())) {
83
                // create the initial Group with some internal fields
84
                $group = new Group();
85
                $group->GUID = $data['objectguid'];
86
87
                $this->log(sprintf(
88
                    'Creating new Group (GUID: %s, sAMAccountName: %s)',
89
                    $data['objectguid'],
90
                    $data['samaccountname']
91
                ));
92
                $created++;
93
            } else {
94
                $this->log(sprintf(
95
                    'Updating existing Group "%s" (ID: %s, GUID: %s, sAMAccountName: %s)',
96
                    $group->getTitle(),
97
                    $group->ID,
98
                    $data['objectguid'],
99
                    $data['samaccountname']
100
                ));
101
                $updated++;
102
            }
103
104
            try {
105
                $this->ldapService->updateGroupFromLDAP($group, $data);
106
            } catch (Exception $e) {
107
                $this->log($e->getMessage());
108
                continue;
109
            }
110
        }
111
112
        // remove Group records that were previously imported, but no longer exist in the directory
113
        // NOTE: DB::query() here is used for performance and so we don't run out of memory
114
        if ($this->config()->destructive) {
115
            foreach (DB::query('SELECT "ID", "GUID" FROM "Group" WHERE "GUID" IS NOT NULL') as $record) {
116
                if (!isset($ldapGroups[$record['GUID']])) {
117
                    $group = Group::get()->byId($record['ID']);
118
119
                    $this->log(sprintf(
120
                        'Removing Group "%s" (GUID: %s) that no longer exists in LDAP.',
121
                        $group->Title,
122
                        $group->GUID
123
                    ));
124
125
                    try {
126
                        // Cascade into mappings, just to clean up behind ourselves.
127
                        foreach ($group->LDAPGroupMappings() as $mapping) {
128
                            $mapping->delete();
129
                        }
130
                        $group->delete();
131
                    } catch (Exception $e) {
132
                        $this->log($e->getMessage());
133
                        continue;
134
                    }
135
136
                    $deleted++;
137
                }
138
            }
139
        }
140
141
        $this->invokeWithExtensions('onAfterLDAPGroupSyncTask');
142
143
        $end = time() - $start;
144
145
        $this->log(sprintf(
146
            'Done. Created %s records. Updated %s records. Deleted %s records. Duration: %s seconds',
147
            $created,
148
            $updated,
149
            $deleted,
150
            round($end, 0)
151
        ));
152
    }
153
154
    /**
155
     * Sends a message, formatted either for the CLI or browser
156
     *
157
     * @param string $message
158
     */
159
    protected function log($message)
160
    {
161
        $message = sprintf('[%s] ', date('Y-m-d H:i:s')) . $message;
162
        echo Director::is_cli() ? ($message . PHP_EOL) : ($message . '<br>');
163
    }
164
165
    /**
166
     * @param LDAPService $service
167
     * @return $this
168
     */
169
    public function setLDAPService(LDAPService $service)
170
    {
171
        $this->ldapService = $service;
172
        return $this;
173
    }
174
}
175