Completed
Pull Request — master (#64)
by Sean
02:43
created

LDAPMemberExtension   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 219
Duplicated Lines 17.81 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 9
Bugs 0 Features 0
Metric Value
wmc 27
c 9
b 0
f 0
lcom 1
cbo 8
dl 39
loc 219
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
B updateCMSFields() 0 54 8
A validate() 0 21 4
B onBeforeWrite() 13 13 5
A onAfterWrite() 14 14 4
A onAfterDelete() 12 12 4
A memberLoggedIn() 0 6 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * Class LDAPMemberExtension.
4
 *
5
 * Adds mappings from AD attributes to SilverStripe {@link Member} fields.
6
 */
7
class LDAPMemberExtension extends DataExtension
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
8
{
9
    /**
10
     * @var array
11
     */
12
    private static $db = array(
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...
13
        // Unique user identifier, same field is used by SAMLMemberExtension
14
        'GUID' => 'Varchar(50)',
15
        'Username' => 'Varchar(64)',
16
        'IsImportedFromLDAP' => 'Boolean',
17
        'IsExpired' => 'Boolean',
18
        'LastSynced' => 'SS_Datetime',
19
    );
20
21
    /**
22
     * These fields are used by {@link LDAPMemberSync} to map specific AD attributes
23
     * to {@link Member} fields.
24
     *
25
     * @var array
26
     * @config
27
     */
28
    private static $ldap_field_mappings = array(
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...
29
        'givenname' => 'FirstName',
30
        'samaccountname' => 'Username',
31
        'sn' => 'Surname',
32
        'mail' => 'Email',
33
    );
34
35
    /**
36
     * The location (relative to /assets) where to save thumbnailphoto data.
37
     *
38
     * @var string
39
     * @config
40
     */
41
    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...
42
43
    /**
44
     * When enabled, LDAP managed Member records (IsImportedFromLDAP flag)
45
     * have their data written back to LDAP on write.
46
     *
47
     * This requires setting write permissions on the user configured in the LDAP
48
     * credentials, which is why this is disabled by default.
49
     *
50
     * Note that some constants must be configured in your environment file:
51
     * LDAP_DOMAIN - the base DN of the directory. e.g. "DN=mydomain,DC=com"
52
     *
53
     * @var bool
54
     * @config
55
     */
56
    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...
57
58
    /**
59
     * If enabled, Member records have a user created in LDAP on write.
60
     *
61
     * This requires setting write permissions on the user configured in the LDAP
62
     * credentials, which is why this is disabled by default.
63
     *
64
     * Note that some constants must be configured in your environment file:
65
     * LDAP_DOMAIN - the base DN of the directory. e.g. "DN=mydomain,DC=com"
66
     * LDAP_NEW_USERS_OBJECT_CATEGORY - the type of object. e.g. "CN=Person,CN=Schema,DC=mydomain,DC=com"
67
     * LDAP_NEW_USERS_DN - where to place users in the directory. e.g. "OU=Users,DC=mydomain,DC=com"
68
     *
69
     * @var bool
70
     * @config
71
     */
72
    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...
73
74
    /**
75
     * If enabled, deleting Member records mapped to LDAP deletes the LDAP user.
76
     *
77
     * This requires setting write permissions on the user configured in the LDAP
78
     * credentials, which is why this is disabled by default.
79
     *
80
     * @var bool
81
     * @config
82
     */
83
    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...
84
85
    /**
86
     * @param FieldList $fields
87
     */
88
    public function updateCMSFields(FieldList $fields)
89
    {
90
        // Redo LDAP metadata fields as read-only and move to LDAP tab.
91
        $ldapMetadata = array();
92
        $fields->replaceField('IsImportedFromLDAP', $ldapMetadata[] = new ReadonlyField(
93
            'IsImportedFromLDAP',
94
            _t('LDAPMemberExtension.ISIMPORTEDFROMLDAP', 'Is user imported from LDAP/AD?')
95
        ));
96
        $fields->replaceField('GUID', $ldapMetadata[] = new ReadonlyField('GUID'));
97
        $fields->replaceField('IsExpired', $ldapMetadata[] = new ReadonlyField(
98
            'IsExpired',
99
            _t('LDAPMemberExtension.ISEXPIRED', 'Has user\'s LDAP/AD login expired?'))
100
        );
101
        $fields->replaceField('LastSynced', $ldapMetadata[] = new ReadonlyField(
102
            'LastSynced',
103
            _t('LDAPMemberExtension.LASTSYNCED', 'Last synced'))
104
        );
105
        $fields->addFieldsToTab('Root.LDAP', $ldapMetadata);
106
107
        $message = null;
108
        if ($this->owner->IsImportedFromLDAP && $this->owner->config()->update_ldap_from_local) {
109
            $message = _t('LDAPMemberExtension.INFOIMPORTEDCANCHANGE',
110
                'This user is automatically imported from LDAP. '.
111
                    'Changing fields here will update them in LDAP.'
112
            );
113
        } elseif ($this->owner->IsImportedFromLDAP && !$this->owner->config()->update_ldap_from_local) {
114
            // Transform the automatically mapped fields into read-only. This doesn't
115
            // apply if updating LDAP from local is enabled, as changing data locally can be written back.
116
            foreach ($this->owner->config()->ldap_field_mappings as $name) {
117
                $field = $fields->dataFieldByName($name);
118
                if (!empty($field)) {
119
                    // Set to readonly, but not disabled so that the data is still sent to the
120
                    // server and doesn't break Member_Validator
121
                    $field->setReadonly(true);
122
                    $field->setTitle($field->Title()._t('LDAPMemberExtension.IMPORTEDFIELD', ' (imported)'));
123
                }
124
            }
125
            $message = _t(
126
                'LDAPMemberExtension.INFOIMPORTED',
127
                'This user is automatically imported from LDAP. '.
128
                    'Manual changes to imported fields will be removed upon sync.'
129
            );
130
        }
131
        if ($message) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $message of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
132
            $fields->addFieldToTab(
133
                'Root.Main',
134
                new LiteralField(
135
                    'Info',
136
                    sprintf('<p class="message warning">%s</p>', $message)
137
                ),
138
                'FirstName'
139
            );
140
        }
141
    }
142
143
    public function validate(ValidationResult $validationResult)
144
    {
145
        if (!$this->owner->config()->create_users_in_ldap) {
146
            return;
147
        }
148
149
        // We allow empty Username for registration purposes, as we need to
150
        // create Member records with empty Username temporarily. Forms should explicitly
151
        // check for Username not being empty if they require it not to be.
152
        if (empty($this->owner->Username)) {
153
            return;
154
        }
155
156
        if (!preg_match('/^[a-z0-9\.]+$/', $this->owner->Username)) {
157
            $validationResult->error(
158
                'Username must only contain lowercase alphanumeric characters and dots.',
159
                'bad'
160
            );
161
            throw new ValidationException($validationResult);
162
        }
163
    }
164
165
    /**
166
     * Create the user in LDAP, provided this configuration is enabled
167
     * and a username was passed to a new Member record.
168
     */
169 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...
170
    {
171
        $service = Injector::inst()->get('LDAPService');
172
        if (!$service->enabled()) {
173
            return;
174
        }
175
        if (!$this->owner->config()->create_users_in_ldap) {
176
            return;
177
        }
178
        if ($this->owner->Username && !$this->owner->IsImportedFromLDAP) {
179
            $service->createLDAPUser($this->owner);
180
        }
181
    }
182
183
    /**
184
     * Update the local data with LDAP, and ensure local membership is also set in
185
     * LDAP too. This writes into LDAP, provided that feature is enabled.
186
     */
187 View Code Duplication
    public function onAfterWrite()
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...
188
    {
189
        $service = Injector::inst()->get('LDAPService');
190
        if (!$service->enabled()) {
191
            return;
192
        }
193
        if (!$this->owner->config()->update_ldap_from_local) {
194
            return;
195
        }
196
        if ($this->owner->IsImportedFromLDAP) {
197
            $service->updateLDAPFromMember($this->owner);
198
            $service->updateLDAPGroupsForMember($this->owner);
199
        }
200
    }
201
202 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...
203
        $service = Injector::inst()->get('LDAPService');
204
        if (!$service->enabled()) {
205
            return;
206
        }
207
        if (!$this->owner->config()->delete_users_in_ldap) {
208
            return;
209
        }
210
        if ($this->owner->IsImportedFromLDAP) {
211
            $service->deleteLDAPMember($this->owner);
212
        }
213
    }
214
215
    /**
216
     * Triggered by {@link Member::logIn()} when successfully logged in,
217
     * this will update the Member record from AD data.
218
     */
219
    public function memberLoggedIn()
220
    {
221
        if ($this->owner->GUID) {
222
            Injector::inst()->get('LDAPService')->updateMemberFromLDAP($this->owner);
223
        }
224
    }
225
}
226