Test Setup Failed
Push — dependabot/composer/thomaspark... ( aabc3d...0d7c4f )
by
unknown
09:36 queued 04:31
created

AbstractNativeAuthenticationMethod::getMapping()   B

Complexity

Conditions 9
Paths 4

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 18
nc 4
nop 2
dl 0
loc 30
rs 8.0555
c 0
b 0
f 0
1
<!-- // <?php -->
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected '>' on line 1 at column 16
Loading history...
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Zikula package.
7
 *
8
 * Copyright Zikula - https://ziku.la/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Zikula\ZAuthModule\AuthenticationMethod;
15
16
use Exception;
17
use Symfony\Component\HttpFoundation\RequestStack;
18
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
19
use Symfony\Component\Validator\Validator\ValidatorInterface;
20
use Symfony\Contracts\Translation\TranslatorInterface;
21
use Zikula\ExtensionsModule\Api\ApiInterface\VariableApiInterface;
22
use Zikula\UsersModule\AuthenticationMethodInterface\NonReEntrantAuthenticationMethodInterface;
23
use Zikula\ZAuthModule\Entity\AuthenticationMappingEntity;
24
use Zikula\ZAuthModule\Entity\RepositoryInterface\AuthenticationMappingRepositoryInterface;
25
use Zikula\ZAuthModule\Form\Type\RegistrationType;
26
use Zikula\ZAuthModule\ZAuthConstant;
27
28
abstract class AbstractNativeAuthenticationMethod implements NonReEntrantAuthenticationMethodInterface
29
{
30
    /**
31
     * @var AuthenticationMappingRepositoryInterface
32
     */
33
    private $mappingRepository;
34
35
    /**
36
     * @var RequestStack
37
     */
38
    private $requestStack;
39
40
    /**
41
     * @var TranslatorInterface
42
     */
43
    protected $translator;
44
45
    /**
46
     * @var VariableApiInterface
47
     */
48
    private $variableApi;
49
50
    /**
51
     * @var ValidatorInterface
52
     */
53
    private $validator;
54
55
    /**
56
     * @var EncoderFactoryInterface
57
     */
58
    private $encoderFactory;
59
60
    public function __construct(
61
        AuthenticationMappingRepositoryInterface $mappingRepository,
62
        RequestStack $requestStack,
63
        TranslatorInterface $translator,
64
        VariableApiInterface $variableApi,
65
        ValidatorInterface $validator,
66
        EncoderFactoryInterface $encoderFactory
67
    ) {
68
        $this->mappingRepository = $mappingRepository;
69
        $this->requestStack = $requestStack;
70
        $this->translator = $translator;
71
        $this->variableApi = $variableApi;
72
        $this->validator = $validator;
73
        $this->encoderFactory = $encoderFactory;
74
    }
75
76
    public function getRegistrationFormClassName(): string
77
    {
78
        return RegistrationType::class;
79
    }
80
81
    public function getRegistrationTemplateName(): string
82
    {
83
        return '@ZikulaZAuthModule/Authentication/register.html.twig';
84
    }
85
86
    protected function authenticateByField(array $data, string $field = 'uname'): ?int
87
    {
88
        if (!isset($data[$field])) {
89
            return null;
90
        }
91
92
        $mapping = $this->getMapping($field, $data[$field]);
93
        if (!isset($mapping) || !$mapping->getPass()) {
94
            return null;
95
        }
96
        $passwordEncoder = $this->encoderFactory->getEncoder($mapping);
97
98
        if ($passwordEncoder->isPasswordValid($mapping->getPass(), $data['pass'], null)) {
99
            if ($passwordEncoder->needsRehash($mapping->getPass())) { // check to update hash to newer algo
100
                $this->updatePassword($mapping, $data['pass']);
101
            }
102
103
            return $mapping->getUid();
104
        }
105
106
        return null;
107
    }
108
109
    private function updatePassword(AuthenticationMappingEntity $mapping, string $unHashedPassword)
110
    {
111
        $mapping->setPass($this->encoderFactory->getEncoder($mapping)->encodePassword($unHashedPassword, null));
112
        $this->mappingRepository->persistAndFlush($mapping);
113
    }
114
115
    /**
116
     * Get a AuthenticationMappingEntity if it exists. If not, check for existing UserEntity and
117
     * migrate data from UserEntity to AuthenticationMappingEntity and return that.
118
     *
119
     * @throws Exception
120
     */
121
    private function getMapping(string $field, string $value): ?AuthenticationMappingEntity
122
    {
123
        /** @var AuthenticationMappingEntity $mapping */
124
        $mapping = $this->mappingRepository->findOneBy([$field => $value]);
125
        if (
126
            isset($mapping)
127
            && (
128
                ('email' === $field && ZAuthConstant::AUTHENTICATION_METHOD_UNAME === $mapping->getMethod())
129
                || ('uname' === $field && ZAuthConstant::AUTHENTICATION_METHOD_EMAIL === $mapping->getMethod())
130
            )
131
        ) {
132
            // mapping exists but method is set to opposite. allow either if possible.
133
            $mapping->setMethod(ZAuthConstant::AUTHENTICATION_METHOD_EITHER);
134
            $errors = $this->validator->validate($mapping);
135
            if (count($errors) > 0) {
136
                // the error is probably only because of duplicate email... so....
137
                $request = $this->requestStack->getCurrentRequest();
138
                if ($request->hasSession() && ($session = $request->getSession())) {
139
                    $session->getFlashBag()->add(
140
                        'error',
141
                        'The email you are trying to authenticate with is in use by another user. You can only login by username.'
142
                    );
143
                }
144
                $mapping = null;
145
            } else {
146
                $this->mappingRepository->persistAndFlush($mapping);
147
            }
148
        }
149
150
        return $mapping;
151
    }
152
153
    public function register(array $data = []): bool
154
    {
155
        $mapping = new AuthenticationMappingEntity();
156
        $mapping->setUid($data['uid']);
157
        $mapping->setUname($data['uname']);
158
        $mapping->setEmail($data['email']);
159
160
        if (empty($data['pass'])) {
161
            $mapping->setPass('');
162
        } else {
163
            $mapping->setPass($this->encoderFactory->getEncoder($mapping)->encodePassword($data['pass'], null));
164
        }
165
166
        $mapping->setMethod($this->getAlias());
167
168
        $userMustVerify = $this->variableApi->get('ZikulaZAuthModule', ZAuthConstant::MODVAR_EMAIL_VERIFICATION_REQUIRED, ZAuthConstant::DEFAULT_EMAIL_VERIFICATION_REQUIRED);
169
        $request = $this->requestStack->getCurrentRequest();
170
        if ($request->hasSession() && ($session = $request->getSession())) {
171
            $userMustVerify = $session->has(ZAuthConstant::MODVAR_EMAIL_VERIFICATION_REQUIRED)
172
                ? 'Y' === $session->get(ZAuthConstant::MODVAR_EMAIL_VERIFICATION_REQUIRED)
173
                : $userMustVerify;
174
        }
175
176
        $mapping->setVerifiedEmail(!$userMustVerify);
177
        $errors = $this->validator->validate($mapping);
178
        if ($request->hasSession() && ($session = $request->getSession()) && 0 < count($errors)) {
179
            foreach ($errors as $error) {
180
                $session->getFlashBag()->add('error', $error->getMessage());
181
            }
182
183
            return false;
184
        }
185
        $this->mappingRepository->persistAndFlush($mapping);
186
187
        return true;
188
    }
189
}
190