Passed
Pull Request — master (#6735)
by Angel Fernando Quiroz
08:16
created

LdapAuthenticator::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 21
nc 1
nop 3
dl 0
loc 34
rs 9.584
c 1
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
declare(strict_types=1);
6
7
namespace Chamilo\CoreBundle\Security\Authenticator\Ldap;
8
9
use Chamilo\CoreBundle\Helpers\AccessUrlHelper;
10
use Chamilo\CoreBundle\Helpers\AuthenticationConfigHelper;
11
use Chamilo\CoreBundle\Repository\Node\UserRepository;
12
use stdClass;
13
use Symfony\Component\HttpFoundation\Request;
14
use Symfony\Component\HttpFoundation\Response;
15
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
16
use Symfony\Component\Ldap\Adapter\ExtLdap\Adapter;
17
use Symfony\Component\Ldap\Ldap;
18
use Symfony\Component\Ldap\Security\LdapBadge;
19
use Symfony\Component\Ldap\Security\LdapUserProvider;
20
use Symfony\Component\PropertyAccess\Exception\AccessException;
21
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
22
use Symfony\Component\Security\Core\Exception\AuthenticationException;
23
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
24
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
25
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
26
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
27
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
28
29
use function is_string;
30
use function sprintf;
31
32
final class LdapAuthenticator extends AbstractAuthenticator
33
{
34
35
    private Ldap $ldap;
36
    private LdapUserProvider $userProvider;
37
    private string $baseDn = '';
38
    private ?string $searchDn = '';
39
    private ?string $searchPassword = '';
40
    private ?string $queryString = '';
41
42
    public function __construct(
43
        private readonly UserRepository $userRepository,
44
        private readonly AuthenticationConfigHelper $authenticationConfigHelper,
45
        AccessUrlHelper $accessUrlHelper,
46
    ) {
47
        $providerParams = $this->authenticationConfigHelper->getLdapConfig($accessUrlHelper->getCurrent());
48
49
        $this->baseDn = $providerParams['base_dn'];
50
        $this->searchDn = $providerParams['search_dn'];
51
        $this->searchPassword = $providerParams['search_password'];
52
        $this->queryString = $providerParams['query_string'];
53
54
        $adapter = new Adapter(
55
            [
56
                'connection_string' => $providerParams['connection_string'],
57
                'options'           => [
58
                    'protocol_version' => $providerParams['protocol_version'],
59
                    'referrals'        => $providerParams['referrals'],
60
                ],
61
            ]
62
        );
63
64
        $this->ldap = new Ldap($adapter);
65
66
        $this->userProvider = new LdapUserProvider(
67
            $this->ldap,
68
            $this->baseDn,
69
            $this->searchDn,
70
            $this->searchPassword,
71
            $providerParams['default_roles'],
72
            $providerParams['uid_key'],
73
            $providerParams['filter'],
74
            $providerParams['password_attribute'],
75
            $providerParams['extra_fields']
76
        );
77
    }
78
79
    public function supports(Request $request): ?bool
80
    {
81
        return 'login_ldap_check' === $request->attributes->get('_route')
82
            && $request->isMethod('POST');
83
    }
84
85
    public function authenticate(Request $request): Passport
86
    {
87
        try {
88
            $data = json_decode($request->getContent());
89
            if (!$data instanceof stdClass) {
90
                throw new BadRequestHttpException('Invalid JSON.');
91
            }
92
93
            $credentials = $this->getCredentials($data);
94
        } catch (BadRequestHttpException $e) {
95
            $request->setRequestFormat('json');
96
97
            throw $e;
98
        }
99
100
        $userBadge = new UserBadge(
101
            $credentials['username'],
102
            $this->userProvider->loadUserByIdentifier(...)
103
        );
104
        $passport = new Passport(
105
            $userBadge,
106
            new PasswordCredentials($credentials['password']),
107
            [new RememberMeBadge((array) $data)]
108
        );
109
110
        $passport->addBadge(
111
            new LdapBadge(
112
                Ldap::class,
113
                $this->baseDn,
114
                $this->searchDn,
115
                $this->searchPassword,
116
                $this->queryString
117
            )
118
        );
119
120
        return $passport;
121
    }
122
123
    private function getCredentials(stdClass $data): array
124
    {
125
        $credentials = [];
126
        try {
127
            $credentials['username'] = $data->username;
128
129
            if (!is_string($credentials['username'])) {
130
                throw new BadRequestHttpException(sprintf('The key "%s" must be a string.', 'username_path'));
131
            }
132
        } catch (AccessException $e) {
133
            throw new BadRequestHttpException(sprintf('The key "%s" must be provided.', 'username_path'), $e);
134
        }
135
136
        try {
137
            $credentials['password'] = $data->password;
138
            $data->password_path = null;
139
140
            if (!is_string($credentials['password'])) {
141
                throw new BadRequestHttpException(sprintf('The key "%s" must be a string.', 'password_path'));
142
            }
143
        } catch (AccessException $e) {
144
            throw new BadRequestHttpException(sprintf('The key "%s" must be provided.', 'password_path'), $e);
145
        }
146
147
        if ('' === $credentials['username'] || '' === $credentials['password']) {
148
            trigger_deprecation('symfony/security', '6.2',
0 ignored issues
show
Bug introduced by
The function trigger_deprecation was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

148
            /** @scrutinizer ignore-call */ 
149
            trigger_deprecation('symfony/security', '6.2',
Loading history...
149
                'Passing an empty string as username or password parameter is deprecated.');
150
        }
151
152
        return $credentials;
153
    }
154
155
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
156
    {
157
        return null;
158
    }
159
160
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
161
    {
162
        $message = strtr($exception->getMessage(), $exception->getMessageData());
163
164
        return new Response($message, Response::HTTP_FORBIDDEN);
165
    }
166
}