Completed
Push — master ( 0da2a1...7a48ac )
by Daniel
15:06
created

LDAPMemberExtension::onAfterDelete()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 17
Code Lines 10

Duplication

Lines 17
Ratio 100 %

Importance

Changes 0
Metric Value
dl 17
loc 17
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 10
nc 3
nop 0
1
<?php
2
3
namespace SilverStripe\ActiveDirectory\Extensions;
4
5
use SilverStripe\Core\Injector\Injector;
6
use SilverStripe\Forms\FieldList;
7
use SilverStripe\Forms\LiteralField;
8
use SilverStripe\Forms\ReadonlyField;
9
use SilverStripe\ORM\DataExtension;
10
use SilverStripe\ORM\ValidationResult;
11
use SilverStripe\ORM\ValidationException;
12
13
/**
14
 * Class LDAPMemberExtension.
15
 *
16
 * Adds mappings from AD attributes to SilverStripe {@link Member} fields.
17
 *
18
 * @package activedirectory
19
 */
20
class LDAPMemberExtension extends DataExtension
21
{
22
    /**
23
     * @var array
24
     */
25
    private static $db = [
0 ignored issues
show
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
26
        // Unique user identifier, same field is used by SAMLMemberExtension
27
        'GUID' => 'Varchar(50)',
28
        'Username' => 'Varchar(64)',
29
        'IsExpired' => 'Boolean',
30
        'LastSynced' => 'DBDatetime',
31
    ];
32
33
    /**
34
     * These fields are used by {@link LDAPMemberSync} to map specific AD attributes
35
     * to {@link Member} fields.
36
     *
37
     * @var array
38
     * @config
39
     */
40
    private static $ldap_field_mappings = [
0 ignored issues
show
Unused Code introduced by
The property $ldap_field_mappings is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
41
        'givenname' => 'FirstName',
42
        'samaccountname' => 'Username',
43
        'sn' => 'Surname',
44
        'mail' => 'Email',
45
    ];
46
47
    /**
48
     * The location (relative to /assets) where to save thumbnailphoto data.
49
     *
50
     * @var string
51
     * @config
52
     */
53
    private static $ldap_thumbnail_path = 'Uploads';
0 ignored issues
show
Unused Code introduced by
The property $ldap_thumbnail_path is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
54
55
    /**
56
     * When enabled, LDAP managed Member records (GUID flag)
57
     * have their data written back to LDAP on write, and synchronise
58
     * membership to groups mapped to LDAP.
59
     *
60
     * Keep in mind this will currently NOT trigger if there are no
61
     * field changes due to onAfterWrite in framework not being called
62
     * when there are no changes.
63
     *
64
     * This requires setting write permissions on the user configured in the LDAP
65
     * credentials, which is why this is disabled by default.
66
     *
67
     * @var bool
68
     * @config
69
     */
70
    private static $update_ldap_from_local = false;
0 ignored issues
show
Unused Code introduced by
The property $update_ldap_from_local is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
71
72
    /**
73
     * If enabled, Member records with a Username field have the user created in LDAP
74
     * on write.
75
     *
76
     * This requires setting write permissions on the user configured in the LDAP
77
     * credentials, which is why this is disabled by default.
78
     *
79
     * @var bool
80
     * @config
81
     */
82
    private static $create_users_in_ldap = false;
0 ignored issues
show
Unused Code introduced by
The property $create_users_in_ldap is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
83
84
    /**
85
     * If enabled, deleting Member records mapped to LDAP deletes the LDAP user.
86
     *
87
     * This requires setting write permissions on the user configured in the LDAP
88
     * credentials, which is why this is disabled by default.
89
     *
90
     * @var bool
91
     * @config
92
     */
93
    private static $delete_users_in_ldap = false;
0 ignored issues
show
Unused Code introduced by
The property $delete_users_in_ldap is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
94
95
    /**
96
     * @param FieldList $fields
97
     */
98
    public function updateCMSFields(FieldList $fields)
99
    {
100
        // Redo LDAP metadata fields as read-only and move to LDAP tab.
101
        $ldapMetadata = [];
102
        $fields->replaceField('GUID', $ldapMetadata[] = ReadonlyField::create('GUID'));
103
        $fields->replaceField(
104
            'IsExpired',
105
            $ldapMetadata[] = ReadonlyField::create(
106
                'IsExpired',
107
                _t('LDAPMemberExtension.ISEXPIRED', 'Has user\'s LDAP/AD login expired?')
108
            )
109
        );
110
        $fields->replaceField(
111
            'LastSynced',
112
            $ldapMetadata[] = ReadonlyField::create(
113
                'LastSynced',
114
                _t('LDAPMemberExtension.LASTSYNCED', 'Last synced')
115
            )
116
        );
117
        $fields->addFieldsToTab('Root.LDAP', $ldapMetadata);
118
119
        $message = '';
120
        if ($this->owner->GUID && $this->owner->config()->update_ldap_from_local) {
121
            $message = _t(
122
                'LDAPMemberExtension.CHANGEFIELDSUPDATELDAP',
123
                'Changing fields here will update them in LDAP.'
124
            );
125
        } elseif ($this->owner->GUID && !$this->owner->config()->update_ldap_from_local) {
126
            // Transform the automatically mapped fields into read-only. This doesn't
127
            // apply if updating LDAP from local is enabled, as changing data locally can be written back.
128
            foreach ($this->owner->config()->ldap_field_mappings as $name) {
129
                $field = $fields->dataFieldByName($name);
130
                if (!empty($field)) {
131
                    // Set to readonly, but not disabled so that the data is still sent to the
132
                    // server and doesn't break Member_Validator
133
                    $field->setReadonly(true);
134
                    $field->setTitle($field->Title()._t('LDAPMemberExtension.IMPORTEDFIELD', ' (imported)'));
135
                }
136
            }
137
            $message = _t(
138
                'LDAPMemberExtension.INFOIMPORTED',
139
                'This user is automatically imported from LDAP. '.
140
                    'Manual changes to imported fields will be removed upon sync.'
141
            );
142
        }
143
        if ($message) {
144
            $fields->addFieldToTab(
145
                'Root.Main',
146
                LiteralField::create(
147
                    'Info',
148
                    sprintf('<p class="message warning">%s</p>', $message)
149
                ),
150
                'FirstName'
151
            );
152
        }
153
    }
154
155
    /**
156
     * @param  ValidationResult
157
     * @throws ValidationException
158
     */
159
    public function validate(ValidationResult $validationResult)
160
    {
161
        // We allow empty Username for registration purposes, as we need to
162
        // create Member records with empty Username temporarily. Forms should explicitly
163
        // check for Username not being empty if they require it not to be.
164
        if (empty($this->owner->Username) || !$this->owner->config()->create_users_in_ldap) {
165
            return;
166
        }
167
168
        if (!preg_match('/^[a-z0-9\.]+$/', $this->owner->Username)) {
169
            $validationResult->addError(
170
                'Username must only contain lowercase alphanumeric characters and dots.',
171
                'bad'
172
            );
173
            throw new ValidationException($validationResult);
174
        }
175
    }
176
177
    /**
178
     * Create the user in LDAP, provided this configuration is enabled
179
     * and a username was passed to a new Member record.
180
     */
181 View Code Duplication
    public function onBeforeWrite()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
182
    {
183
        if ($this->owner->LDAPMemberExtension_NoSync) {
184
            return;
185
        }
186
187
        $service = Injector::inst()->get('SilverStripe\\ActiveDirectory\\Services\\LDAPService');
188
        if (!$service->enabled()
189
            || !$this->owner->config()->create_users_in_ldap
190
            || !$this->owner->Username
191
            || $this->owner->GUID
192
        ) {
193
            return;
194
        }
195
196
        $service->createLDAPUser($this->owner);
197
    }
198
199
    public function onAfterWrite()
200
    {
201
        if ($this->owner->LDAPMemberExtension_NoSync) {
202
            return;
203
        }
204
205
        $service = Injector::inst()->get('SilverStripe\\ActiveDirectory\\Services\\LDAPService');
206
        if (
207
            !$service->enabled() ||
208
            !$this->owner->config()->update_ldap_from_local ||
209
            !$this->owner->GUID
210
        ) {
211
            return;
212
        }
213
        $this->sync();
214
    }
215
216 View Code Duplication
    public function onAfterDelete()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
217
    {
218
        if ($this->owner->LDAPMemberExtension_NoSync) {
219
            return;
220
        }
221
222
        $service = Injector::inst()->get('SilverStripe\\ActiveDirectory\\Services\\LDAPService');
223
        if (
224
            !$service->enabled() ||
225
            !$this->owner->config()->delete_users_in_ldap ||
226
            !$this->owner->GUID
227
        ) {
228
            return;
229
        }
230
231
        $service->deleteLDAPMember($this->owner);
232
    }
233
234
    /**
235
     * Write DataObject without triggering this extension's hooks.
236
     *
237
     * @throws Exception
238
     */
239
    public function writeWithoutSync()
240
    {
241
        $this->owner->LDAPMemberExtension_NoSync = true;
242
        try {
243
            $this->owner->write();
244
        } finally {
245
            $this->owner->LDAPMemberExtension_NoSync = false;
246
        }
247
    }
248
249
    /**
250
     * Update the local data with LDAP, and ensure local membership is also set in
251
     * LDAP too. This writes into LDAP, provided that feature is enabled.
252
     */
253
    public function sync()
254
    {
255
        $service = Injector::inst()->get('SilverStripe\\ActiveDirectory\\Services\\LDAPService');
256
        if (
257
            !$service->enabled() ||
258
            !$this->owner->GUID
259
        ) {
260
            return;
261
        }
262
        $service->updateLDAPFromMember($this->owner);
263
        $service->updateLDAPGroupsForMember($this->owner);
264
    }
265
266
    /**
267
     * Triggered by {@link Member::logIn()} when successfully logged in,
268
     * this will update the Member record from AD data.
269
     */
270
    public function memberLoggedIn()
271
    {
272
        if ($this->owner->GUID) {
273
            Injector::inst()
274
                ->get('SilverStripe\\ActiveDirectory\\Services\\LDAPService')
275
                ->updateMemberFromLDAP($this->owner);
276
        }
277
    }
278
}
279