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

LDAPMemberExtension::updateCMSFields()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 47
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
c 6
b 0
f 0
dl 0
loc 47
rs 8.5125
cc 5
eloc 29
nc 4
nop 1
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.
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, new Member records written are also created as users in LDAP.
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_new_users_in_ldap = false;
0 ignored issues
show
Unused Code introduced by
The property $create_new_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
     * @var array
76
     */
77
    private static $dependencies = array(
0 ignored issues
show
Unused Code introduced by
The property $dependencies 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...
78
        'ldapService' => '%$LDAPService',
79
    );
80
81
    /**
82
     * @param FieldList $fields
83
     */
84
    public function updateCMSFields(FieldList $fields)
85
    {
86
        // Redo LDAP metadata fields as read-only and move to LDAP tab.
87
        $ldapMetadata = array();
88
        $fields->replaceField('IsImportedFromLDAP', $ldapMetadata[] = new ReadonlyField(
89
            'IsImportedFromLDAP',
90
            _t('LDAPMemberExtension.ISIMPORTEDFROMLDAP', 'Is user imported from LDAP/AD?')
91
        ));
92
        $fields->replaceField('GUID', $ldapMetadata[] = new ReadonlyField('GUID'));
93
        $fields->replaceField('IsExpired', $ldapMetadata[] = new ReadonlyField(
94
            'IsExpired',
95
            _t('LDAPMemberExtension.ISEXPIRED', 'Has user\'s LDAP/AD login expired?'))
96
        );
97
        $fields->replaceField('LastSynced', $ldapMetadata[] = new ReadonlyField(
98
            'LastSynced',
99
            _t('LDAPMemberExtension.LASTSYNCED', 'Last synced'))
100
        );
101
        $fields->addFieldsToTab('Root.LDAP', $ldapMetadata);
102
103
        if ($this->owner->IsImportedFromLDAP && !$this->owner->config()->update_ldap_from_local) {
104
            // Transform the automatically mapped fields into read-only. This doesn't
105
            // apply if updating LDAP from local is enabled, as changing data locally can be written back.
106
            foreach ($this->owner->config()->ldap_field_mappings as $name) {
107
                $field = $fields->dataFieldByName($name);
108
                if (!empty($field)) {
109
                    // Set to readonly, but not disabled so that the data is still sent to the
110
                    // server and doesn't break Member_Validator
111
                    $field->setReadonly(true);
112
                    $field->setTitle($field->Title()._t('LDAPMemberExtension.IMPORTEDFIELD', ' (imported)'));
113
                }
114
            }
115
116
            $message = _t(
117
                'LDAPMemberExtension.INFOIMPORTED',
118
                'This user is automatically imported from LDAP. '.
119
                    'Manual changes to imported fields will be removed upon sync.'
120
            );
121
            $fields->addFieldToTab(
122
                'Root.Main',
123
                new LiteralField(
124
                    'Info',
125
                    sprintf('<p class="message warning">%s</p>', $message)
126
                ),
127
                'FirstName'
128
            );
129
        }
130
    }
131
132
    public function validate(ValidationResult $validationResult)
133
    {
134
        if (!$this->owner->config()->create_new_users_in_ldap) {
135
            return;
136
        }
137
138
        // We allow empty Username for registration purposes, as we need to
139
        // create Member records with empty Username temporarily. Forms should explicitly
140
        // check for Username not being empty if they require it not to be.
141
        if (empty($this->owner->Username)) {
142
            return;
143
        }
144
145
        if (!preg_match('/^[a-z0-9\.]+$/', $this->owner->Username)) {
146
            $validationResult->error(
147
                'Username must only contain lowercase alphanumeric characters and dots.',
148
                'bad'
149
            );
150
            throw new ValidationException($validationResult);
151
        }
152
    }
153
154
    /**
155
     * Create the user in LDAP, provided this configuration is enabled
156
     * and a username was passed to a new Member record.
157
     *
158
     * Set a flag "Creating" so other extensions using on*() events can
159
     * detect whether it's in a state of being created, such as for
160
     * synchronising with other services when a user is being created
161
     * in LDAP for the first time.
162
     */
163
    public function onBeforeWrite()
164
    {
165
        $service = $this->ldapService;
0 ignored issues
show
Bug introduced by
The property ldapService does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
166
        if (!$service->enabled()) {
167
            return;
168
        }
169
        if (!$this->owner->config()->create_new_users_in_ldap) {
170
            return;
171
        }
172
        if ($this->owner->Username && !$this->owner->IsImportedFromLDAP) {
173
            $service->createLDAPUser($this->owner);
174
            $this->owner->Creating = true;
175
        }
176
    }
177
178
    /**
179
     * Update the local data with LDAP, and ensure local membership is also set in
180
     * LDAP too. This writes into LDAP, provided that feature is enabled.
181
     */
182
    public function onAfterWrite()
183
    {
184
        $service = $this->ldapService;
185
        if (!$service->enabled()) {
186
            return;
187
        }
188
        if (!$this->owner->config()->update_ldap_from_local) {
189
            return;
190
        }
191
        if ($this->owner->IsImportedFromLDAP) {
192
            $service->updateLDAPFromMember($this->owner);
193
            $service->updateLDAPGroupsForMember($this->owner);
194
        }
195
    }
196
197
    /**
198
     * Triggered by {@link Member::logIn()} when successfully logged in,
199
     * this will update the Member record from AD data.
200
     */
201
    public function memberLoggedIn()
202
    {
203
        if ($this->owner->GUID) {
204
            $this->ldapService->updateMemberFromLDAP($this->owner);
205
        }
206
    }
207
}
208