Completed
Pull Request — master (#4165)
by Craig
06:23
created

CreateUsersApi::isValidUserData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 8
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Zikula package.
7
 *
8
 * Copyright Zikula Foundation - 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\Api;
15
16
use Doctrine\Persistence\ManagerRegistry;
17
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
18
use Symfony\Component\Validator\Constraints;
19
use Symfony\Component\Validator\ConstraintViolationList;
20
use Symfony\Component\Validator\Validator\ValidatorInterface;
21
use Zikula\ExtensionsModule\Api\ApiInterface\VariableApiInterface;
22
use Zikula\GroupsModule\Constant as GroupsConstant;
23
use Zikula\GroupsModule\Entity\GroupEntity;
24
use Zikula\GroupsModule\Entity\RepositoryInterface\GroupRepositoryInterface;
25
use Zikula\UsersModule\Api\ApiInterface\CurrentUserApiInterface;
26
use Zikula\UsersModule\Constant as UsersConstant;
27
use Zikula\UsersModule\Entity\UserEntity;
28
use Zikula\UsersModule\Validator\Constraints\ValidEmail;
29
use Zikula\UsersModule\Validator\Constraints\ValidUname;
30
use Zikula\ZAuthModule\Api\ApiInterface\CreateUsersApiInterface;
31
use Zikula\ZAuthModule\Entity\AuthenticationMappingEntity;
32
use Zikula\ZAuthModule\Validator\Constraints\ValidPassword;
33
use Zikula\ZAuthModule\ZAuthConstant;
34
35
class CreateUsersApi implements CreateUsersApiInterface
36
{
37
    /**
38
     * array of created users
39
     *
40
     * @var UserEntity[]
41
     */
42
    private $users = [];
43
44
    /**
45
     * array of created ZAuth mappings
46
     *
47
     * @var AuthenticationMappingEntity[]
48
     */
49
    private $mappings = [];
50
51
    /**
52
     * @var Constraints\Collection
53
     */
54
    private $constraint;
55
56
    /**
57
     * @var ValidatorInterface
58
     */
59
    private $validator;
60
61
    /**
62
     * @var CurrentUserApiInterface
63
     */
64
    private $currentUserApi;
65
66
    /**
67
     * @var EncoderFactoryInterface
68
     */
69
    private $encoderFactory;
70
71
    /**
72
     * @var ManagerRegistry
73
     */
74
    private $managerRegistry;
75
76
    /**
77
     * @var VariableApiInterface
78
     */
79
    private $variableApi;
80
81
    /**
82
     * @var GroupEntity[]
83
     */
84
    private $groups;
85
86
    public function __construct(
87
        ValidatorInterface $validator,
88
        CurrentUserApiInterface $currentUserApi,
89
        EncoderFactoryInterface $encoderFactory,
90
        ManagerRegistry $managerRegistry,
91
        VariableApiInterface $variableApi,
92
        GroupRepositoryInterface $groupRepository
93
    ) {
94
        $this->validator = $validator;
95
        $this->currentUserApi = $currentUserApi;
96
        $this->encoderFactory = $encoderFactory;
97
        $this->managerRegistry = $managerRegistry;
98
        $this->variableApi = $variableApi;
99
        $this->groups = $groupRepository->findAllAndIndexBy('gid');
100
        $this->constraint = new Constraints\Collection(['fields' => [
101
            'uname' => new ValidUname(),
102
            'pass' => new ValidPassword(),
103
            'email' => new ValidEmail(),
104
            'activated' => new Constraints\Optional([new Constraints\Type('numeric'), new Constraints\Choice([0, 1, '0', '1'])]),
105
            'sendmail' => new Constraints\Optional([new Constraints\Type('numeric'), new Constraints\Choice([0, 1, '0', '1'])]),
106
            'groups' => new Constraints\Optional([new Constraints\Type('string'), new Constraints\Regex(['pattern' => '%^[0-9\|]+$%'])])
107
        ]]);
108
    }
109
110
    /**
111
     * @param array $userArray
112
     *
113
     * @return bool|string
114
     */
115
    public function isValidUserData(array $userArray)
116
    {
117
        $errors = $this->validator->validate($userArray, $this->constraint);
118
        if (0 !== count($errors)) {
119
            return $errors[0]->getMessage();
120
        }
121
122
        return true;
123
    }
124
125
    public function isValidUserDataArray(array $userArrays)
126
    {
127
        $violations = new ConstraintViolationList();
128
        foreach ($userArrays as $userArray) {
129
            $errors = $this->validator->validate($userArray, $this->constraint);
130
            if (0 !== count($errors)) {
131
                $violations->addAll($errors);
132
            }
133
        }
134
135
        return 0 === $violations->count() ? true : $violations;
136
    }
137
138
    public function createUser(array $userArray): void
139
    {
140
        $groups = !empty($userArray['groups']) ? explode('|', $userArray['groups']) : [GroupsConstant::GROUP_ID_USERS];
141
        $password = $userArray['pass'];
142
        unset($userArray['pass'], $userArray['sendmail'], $userArray['groups']);
143
        $userArray['activated'] = isset($userArray['activated']) ? empty($userArray['activated']) ? UsersConstant::ACTIVATED_PENDING_REG : 1 : 1;
144
145
        $userEntity = new UserEntity();
146
        $userEntity->merge($userArray);
147
        $nowUTC = new \DateTime('now', new \DateTimeZone('UTC'));
148
        $userEntity->setRegistrationDate($nowUTC);
149
        if (1 === $userEntity->getActivated()) {
150
            $userEntity->setApprovedDate($nowUTC);
151
            $currentUser = $this->currentUserApi->get('uid') ?? UsersConstant::USER_ID_ADMIN;
152
            $userEntity->setApprovedBy($currentUser);
153
        }
154
        foreach ($groups as $group) {
155
            if (isset($this->groups[$group])) {
156
                $userEntity->addGroup($this->groups[$group]);
157
                $this->groups[$group]->addUser($userEntity);
158
            }
159
        }
160
        $hash = hash('crc32', $userEntity->getUname() . $userEntity->getEmail());
161
        $this->users[$hash] = $userEntity;
162
163
        $this->createMapping($userEntity, $password, $hash);
164
    }
165
166
    private function createMapping(UserEntity $userEntity, string $pass, string $hash): void
167
    {
168
        $mapping = new AuthenticationMappingEntity();
169
        $mapping->setUname($userEntity->getUname());
170
        $mapping->setEmail($userEntity->getEmail());
171
        $mapping->setPass($this->encoderFactory->getEncoder($mapping)->encodePassword($pass, null));
172
        $mapping->setMethod(ZAuthConstant::AUTHENTICATION_METHOD_EITHER);
173
        $userMustVerify = $this->variableApi->get('ZikulaZAuthModule', ZAuthConstant::MODVAR_EMAIL_VERIFICATION_REQUIRED, ZAuthConstant::DEFAULT_EMAIL_VERIFICATION_REQUIRED);
174
        $mapping->setVerifiedEmail(!$userMustVerify);
175
        $this->mappings[$hash] = $mapping;
176
    }
177
178
    public function createUsers(array $users): array
179
    {
180
        $errors = [];
181
        foreach ($users as $row => $user) {
182
            if (true === $this->isValidUserData($user)) {
183
                $this->createUser($user);
184
            } else {
185
                $errors[] = sprintf('Row %d with email `%s` and uname `%s` is invalid and was rejected.', $row, $user['email'] ?? 'unset', $user['uname'] ?? 'unset');
186
            }
187
        }
188
189
        return $errors;
190
    }
191
192
    public function persist(): void
193
    {
194
        foreach ($this->users as $userEntity) {
195
            $this->managerRegistry->getManager()->persist($userEntity);
196
        }
197
        foreach ($this->mappings as $hash => $mapping) {
198
            $mapping->setUid($this->users[$hash]->getUid());
199
            $this->managerRegistry->getManager()->persist($mapping);
200
        }
201
202
        $this->managerRegistry->getManager()->flush();
203
    }
204
205
    public function getCreatedUsers(): array
206
    {
207
        return $this->users;
208
    }
209
210
    public function getCreatedMappings(): array
211
    {
212
        return $this->mappings;
213
    }
214
215
    public function clearCreated(): void
216
    {
217
        $this->users = [];
218
        $this->mappings = [];
219
    }
220
}
221