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