Passed
Push — master ( c45437...17ee95 )
by Mathieu
01:49
created

LdapAuthIntegration::getUser()   B

Complexity

Conditions 10
Paths 5

Size

Total Lines 46
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 31
dl 0
loc 46
rs 7.6666
c 0
b 0
f 0
cc 10
nc 5
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @package     Mautic
4
 * @copyright   2019 Monogramm. All rights reserved
5
 * @author      Monogramm
6
 * @link        https://www.monogramm.io
7
 * @license     GNU/AGPLv3 http://www.gnu.org/licenses/agpl.html
8
 */
9
10
namespace MauticPlugin\MauticLdapAuthBundle\Integration;
11
12
use Mautic\PluginBundle\Integration\AbstractSsoFormIntegration;
13
use Mautic\UserBundle\Entity\User;
14
15
use Symfony\Component\Ldap\LdapClient;
16
use Symfony\Component\Security\Core\Exception\AuthenticationException;
17
18
/**
19
 * Class LdapAuthIntegration
20
 */
21
class LdapAuthIntegration extends AbstractSsoFormIntegration
22
{
23
    /**
24
     * @return string
25
     */
26
    public function getName()
27
    {
28
        return 'LdapAuth';
29
    }
30
31
    /**
32
     * @return string
33
     */
34
    public function getDisplayName()
35
    {
36
        return 'LDAP Authentication';
37
    }
38
39
    /**
40
     * @return string
41
     */
42
    public function getAuthenticationType()
43
    {
44
        return 'none';
45
    }
46
47
    /**
48
     * {@inheritdoc}
49
     *
50
     * @return array
51
     */
52
    public function getRequiredKeyFields()
53
    {
54
        return [
55
        ];
56
    }
57
58
    /**
59
     * {@inheritdoc}
60
     *
61
     * @return array
62
     */
63
    public function getSecretKeys()
64
    {
65
        return [
66
        ];
67
    }
68
69
    /**
70
     * {@inheritdoc}
71
     *
72
     * @return string
73
     */
74
    public function getAuthTokenKey()
75
    {
76
        return '';
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     *
82
     * @param array $settings
83
     * @param array $parameters
84
     *
85
     * @return bool|array false if no error; otherwise the error string
86
     *
87
     * @throws \Symfony\Component\Security\Core\Exception\AuthenticationException
88
     */
89
    public function authCallback($settings = [], $parameters = [])
90
    {
91
        $hostname = $settings['hostname'];
92
        $port = (int) $settings['port'];
93
        $ssl = (bool) $settings['ssl'];
94
        $startTls = (bool) $settings['starttls'];
95
        $ldapVersion = !empty($settings['version']) ? (int) $settings['version'] : 3;
96
97
        if (substr($hostname, 0, 7) === 'ldap://') {
98
            $hostname = str_replace('ldap://', '', $hostname);
99
        } elseif (substr($hostname, 0, 8) === 'ldaps://') {
100
            $ssl = true;
101
            $startTls = false;
102
            $hostname = str_replace('ldaps://', '', $hostname);
103
        }
104
105
        if (empty($port)) {
106
            if ($ssl) {
107
                $port = 636;
108
            } else {
109
                $port = 389;
110
            }
111
        }
112
113
        if (!empty($hostname) && !empty($parameters['login'])) {
114
            $ldap = new LdapClient($hostname, $port, $ldapVersion, $ssl, $startTls);
115
116
            $response = $this->ldapUserLookup($ldap, $settings, $parameters);
117
118
            return $this->extractAuthKeys($response);
119
        }
120
121
        return false;
122
    }
123
124
    /**
125
     * LDAP authentication and lookup user information.
126
     *
127
     * @param \Symfony\Component\Ldap\LdapClient $ldap
128
     * @param array $settings
129
     * @param array $parameters
130
     *
131
     * @return array array containing the LDAP lookup results or error message(s).
132
     *
133
     * @throws \Symfony\Component\Security\Core\Exception\AuthenticationException
134
     */
135
    private function ldapUserLookup($ldap, $settings = [], $parameters = [])
136
    {
137
        $base_dn = $settings['base_dn'];
138
        $userKey = $settings['user_key'];
139
        $query = $settings['user_query'];
140
        $is_ad = $settings['is_ad'];
141
        $ad_domain = $settings['ad_domain'];
142
143
        $login = $parameters['login'];
144
        $password = $parameters['password'];
145
146
        try {
147
            if ($is_ad) {
148
                $dn = "$login@$ad_domain";
149
            } else {
150
                $dn = "$userKey=$login,$base_dn";
151
            }
152
153
            $userquery = "$userKey=$login";
154
            $query = "(&($userquery)$query)"; // original $query already has brackets!
155
156
            $ldap->bind($dn, $password);
157
            $response = $ldap->find($base_dn, $query);
158
            // If we reach this far, we expect to have found something
159
            // and join the settings to the response to retrieve user fields
160
            if (is_array($response)) {
161
                $response['settings'] = $settings;
162
            }
163
        } catch (\Exception $e) {
164
            $response = array(
165
                'errors' => array(
166
                    $this->factory->getTranslator()->trans(
167
                        'mautic.integration.sso.ldapauth.error.authentication_issue',
168
                        [],
169
                        'flashes'
170
                    ),
171
                    $e->getMessage()
172
                )
173
            );
174
        }
175
176
        return $response;
177
    }
178
179
    /**
180
     * {@inheritdoc}
181
     *
182
     * @param $data
183
     * @param $tokenOverride
184
     *
185
     * @return bool|array false if no error; otherwise the error string
186
     *
187
     * @throws \Symfony\Component\Security\Core\Exception\AuthenticationException
188
     */
189
    public function extractAuthKeys($data, $tokenOverride = null)
190
    {
191
        // Prepare the keys for extraction such as renaming, setting expiry, etc
192
        $data = $this->prepareResponseForExtraction($data);
193
194
        // Parse the response
195
        if (is_array($data) && !empty($data) && isset($data['settings'])) {
196
            return array(
197
                'data' => $data[0],
198
                'settings' => $data['settings']
199
            );
200
        }
201
202
        $error = $this->getErrorsFromResponse($data);
203
        if (empty($error)) {
204
            $error = $this->factory->getTranslator()->trans(
205
                'mautic.integration.error.genericerror',
206
                [],
207
                'flashes'
208
            );
209
        }
210
211
        $fallback = $this->shouldFallbackToLocalAuth();
212
        if (!$fallback) {
213
            throw new AuthenticationException($error);
214
        } else {
215
            $this->getLogger()->addError($error);
216
        }
217
    }
218
219
    /**
220
     * {@inheritdoc}
221
     *
222
     * @param mixed $response
223
     *
224
     * @return mixed
225
     *
226
     * @throws \Doctrine\ORM\ORMException
227
     */
228
    public function getUser($response)
229
    {
230
        if (is_array($response) && isset($response['settings']) && isset($response['data'])) {
231
            $settings = $response['settings'];
232
            $userKey = $settings['user_key'];
233
            $userEmail = $settings['user_email'];
234
            $userFirstname = $settings['user_firstname'];
235
            $userLastname = $settings['user_lastname'];
236
            $userFullname = $settings['user_fullname'];
237
238
            $data = $response['data'];
239
            $login = self::arrayGet($data, $userKey, [null])[0];
240
            $email = self::arrayGet($data, $userEmail, [null])[0];
241
242
            if (empty($login) || empty($email)) {
243
                // Login or email could not be found so bail
244
                return false;
245
            }
246
247
            $firstname = self::arrayGet($data, $userFirstname, [null])[0];
248
            $lastname = self::arrayGet($data, $userLastname, [null])[0];
249
250
            if ((empty($firstname) || empty($lastname)) && isset($data[$userFullname])) {
251
                $names = explode(' ', $data[$userFullname][0]);
252
                if (count($names) > 1) {
253
                    $firstname = $names[0];
254
                    unset($names[0]);
255
                    $lastname = implode(' ', $names);
256
                } else {
257
                    $firstname = $lastname = $names[0];
258
                }
259
            }
260
261
            $user = new User();
262
            $user->setUsername($login)
263
                ->setEmail($email)
264
                ->setFirstName($firstname)
265
                ->setLastName($lastname)
266
                ->setRole(
267
                    $this->getUserRole()
268
                );
269
270
            return $user;
271
        }
272
273
        return false;
274
    }
275
276
    /**
277
     * Get a value from an array or return default value if not set.
278
     *
279
     * @param array $array source array
280
     * @param string $key key to get from array
281
     * @param mixed $default default value if key not set in array
282
     *
283
     * @return mixed a value from array or default value.
284
     */
285
    private function arrayGet($array, $key, $default = null)
286
    {
287
        return isset($array[$key]) ? $array[$key] : $default;
288
    }
289
290
    /**
291
     * Returns if failed LDAP authentication should fallback to local authentication.
292
     *
293
     * @return bool
294
     */
295
    public function shouldFallbackToLocalAuth()
296
    {
297
        $featureSettings = $this->settings->getFeatureSettings();
298
299
        return (isset($featureSettings['auth_fallback'])) ? $featureSettings['auth_fallback'] : true;
300
    }
301
302
    /**
303
     * {@inheritdoc}
304
     *
305
     * @param Form|\Symfony\Component\Form\FormBuilder $builder
306
     * @param array $data
307
     * @param string $formArea
308
     */
309
    public function appendToForm(&$builder, $data, $formArea)
310
    {
311
        if ($formArea == 'features') {
312
            $builder->add(
313
                'auth_fallback',
314
                'yesno_button_group',
315
                [
316
                    'label' => 'mautic.integration.sso.ldapauth.auth_fallback',
317
                    'data' => (isset($data['auth_fallback'])) ? (bool) $data['auth_fallback'] : true,
318
                    'attr' => [
319
                        'tooltip' => 'mautic.integration.sso.ldapauth.auth_fallback.tooltip',
320
                    ],
321
                ]
322
            );
323
324
            $builder->add(
325
                'auto_create_user',
326
                'yesno_button_group',
327
                [
328
                    'label' => 'mautic.integration.sso.auto_create_user',
329
                    'data' => (isset($data['auto_create_user'])) ? (bool) $data['auto_create_user'] : false,
330
                    'attr' => [
331
                        'tooltip' => 'mautic.integration.sso.auto_create_user.tooltip',
332
                    ],
333
                ]
334
            );
335
336
            $builder->add(
337
                'new_user_role',
338
                'role_list',
339
                [
340
                    'label' => 'mautic.integration.sso.new_user_role',
341
                    'label_attr' => ['class' => 'control-label'],
342
                    'attr' => [
343
                        'class' => 'form-control',
344
                        'tooltip' => 'mautic.integration.sso.new_user_role.tooltip',
345
                    ],
346
                ]
347
            );
348
        }
349
    }
350
}
351