Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.67% covered (success)
97.67%
84 / 86
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
CreateUserEndpoint
97.67% covered (success)
97.67%
84 / 86
0.00% covered (danger)
0.00%
0 / 1
26
0.00% covered (danger)
0.00%
0 / 1
 handle
97.67% covered (success)
97.67%
84 / 86
0.00% covered (danger)
0.00%
0 / 1
26
1<?php
2
3namespace Olz\Users\Endpoints;
4
5use Olz\Api\OlzCreateEntityTypedEndpoint;
6use Olz\Entity\AuthRequest;
7use Olz\Entity\Users\User;
8use PhpTypeScriptApi\HttpError;
9
10/**
11 * @phpstan-import-type OlzUserId from UserEndpointTrait
12 * @phpstan-import-type OlzUserData from UserEndpointTrait
13 *
14 * @extends OlzCreateEntityTypedEndpoint<OlzUserId, OlzUserData, array{
15 *   captchaToken?: ?non-empty-string,
16 * }, array{
17 *   status: 'OK'|'OK_NO_EMAIL_VERIFICATION'|'DENIED'|'ERROR',
18 * }>
19 */
20class CreateUserEndpoint extends OlzCreateEntityTypedEndpoint {
21    use UserEndpointTrait;
22
23    protected function handle(mixed $input): mixed {
24        $current_user = $this->authUtils()->getCurrentUser();
25        $token = $input['custom']['captchaToken'] ?? null;
26        if (!$current_user && !$this->captchaUtils()->validateToken($token)) {
27            return ['custom' => ['status' => 'DENIED'], 'id' => null];
28        }
29
30        $parent_user_id = $input['data']['parentUserId'] ?? null;
31        if ($parent_user_id !== null) {
32            if (!$current_user) {
33                throw new HttpError(403, "Kein Zugriff!");
34            }
35            if ($parent_user_id !== $current_user->getId()) {
36                // Create child of someone else
37                if (!$this->authUtils()->hasPermission('users')) {
38                    throw new HttpError(403, "Kein Zugriff!");
39                }
40            }
41        }
42
43        $first_name = $input['data']['firstName'];
44        $last_name = $input['data']['lastName'];
45        $username = $input['data']['username'];
46        $email = $input['data']['email'] ?? null;
47        $password = $input['data']['password'] ?? null;
48        $this->log()->info("New sign-up (using password): {$first_name} {$last_name} ({$username}@) <{$email}> (Parent: {$parent_user_id})");
49        if (!$parent_user_id && !$email) {
50            throw HttpError::validationError(['email' => ["Feld darf nicht leer sein."]]);
51        }
52        if (!$parent_user_id && !$password) {
53            throw HttpError::validationError(['password' => ["Feld darf nicht leer sein."]]);
54        }
55        if (!$this->authUtils()->isUsernameAllowed($username)) {
56            throw HttpError::validationError(['username' => ["Der Benutzername darf nur Buchstaben, Zahlen, und die Zeichen -_. enthalten."]]);
57        }
58        if (!$parent_user_id && !$this->authUtils()->isPasswordAllowed($password)) {
59            throw HttpError::validationError(['password' => ["Das Passwort muss mindestens 8 Zeichen lang sein."]]);
60        }
61        if ($email && preg_match('/@olzimmerberg\.ch$/i', $email)) {
62            throw HttpError::validationError(['email' => ["Bitte keine @olzimmerberg.ch E-Mail verwenden."]]);
63        }
64        $ip_address = $this->server()['REMOTE_ADDR'];
65        $auth_request_repo = $this->entityManager()->getRepository(AuthRequest::class);
66        $user_repo = $this->entityManager()->getRepository(User::class);
67
68        $same_username_user = $user_repo->findOneBy(['username' => $username]);
69        $same_email_user = $user_repo->findOneBy(['email' => $email]);
70        if ($username && $same_username_user) {
71            if ($same_username_user->getPasswordHash()) {
72                throw HttpError::validationError(['username' => ["Es existiert bereits eine Person mit diesem Benutzernamen. Wolltest du gar kein Konto erstellen, sondern dich nur einloggen?"]]);
73            }
74            // If it's an existing user WITHOUT password, we just update that existing user!
75            $entity = $same_username_user;
76            $this->entityUtils()->updateOlzEntity($entity, ['onOff' => true]);
77        } elseif ($email && $same_email_user) {
78            if ($same_email_user->getPasswordHash()) {
79                throw HttpError::validationError(['email' => ["Es existiert bereits eine Person mit dieser E-Mail Adresse. Wolltest du gar kein Konto erstellen, sondern dich nur einloggen?"]]);
80            }
81            // If it's an existing user WITHOUT password, we just update that existing user!
82            $entity = $same_email_user;
83            $this->entityUtils()->updateOlzEntity($entity, ['onOff' => true]);
84        } elseif (!$this->authUtils()->isUsernameUnique($username, null)) {
85            throw HttpError::validationError(['username' => ["Es existiert bereits eine Person mit diesem Benutzernamen. Wolltest du gar kein Konto erstellen, sondern dich nur einloggen?"]]);
86        } else {
87            $entity = new User();
88            $this->entityUtils()->createOlzEntity($entity, ['onOff' => true]);
89        }
90
91        $entity->setOldUsername(null);
92        $this->updateEntityWithData($entity, $input['data']);
93        $entity->setEmailIsVerified(false);
94        $entity->setEmailVerificationToken(null);
95
96        $password_hash = $password ? $this->authUtils()->hashPassword($password) : null;
97
98        $entity->setPasswordHash($password_hash);
99        $entity->setParentUserId($parent_user_id);
100        $entity->setPermissions('');
101        $entity->setRoot(null);
102        $entity->setMemberType(null);
103        $entity->setMemberLastPaid(null);
104        $entity->setWantsPostalMail(false);
105        $entity->setPostalTitle(null);
106        $entity->setPostalName(null);
107        $entity->setJoinedOn(null);
108        $entity->setJoinedReason(null);
109        $entity->setLeftOn(null);
110        $entity->setLeftReason(null);
111        $entity->setNotes('');
112        $entity->setLastLoginAt(null);
113
114        $this->entityManager()->persist($entity);
115        $this->entityManager()->flush();
116        $this->persistUploads($entity, $input['data']);
117
118        if (!$parent_user_id) {
119            $this->session()->resetConfigure(['timeout' => 3600]);
120
121            $this->authUtils()->setSessionUser($entity);
122            $this->authUtils()->setSessionAuthUser($entity);
123
124            $auth_request_repo->addAuthRequest($ip_address, 'AUTHENTICATED_PASSWORD', $entity->getUsername());
125
126            $this->emailUtils()->setLogger($this->log());
127            try {
128                $this->emailUtils()->sendEmailVerificationEmail($entity);
129            } catch (\Throwable $th) {
130                return [
131                    'custom' => ['status' => 'OK_NO_EMAIL_VERIFICATION'],
132                    'id' => $entity->getId() ?? 0,
133                ];
134            }
135            $this->entityManager()->flush();
136        }
137
138        return [
139            'custom' => ['status' => 'OK'],
140            'id' => $entity->getId() ?? 0,
141        ];
142    }
143}