ILIAS  trunk Revision v12.0_alpha-16-g3e876e53c80
DualOptInServiceImpl.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
31use ILIAS\User\Settings\NewAccountMail\Repository as NewAccountMailRepository;
33
34final readonly class DualOptInServiceImpl implements DualOptInService
35{
36 public const string ID = 'reg_hash_service';
37
38 public function __construct(
39 private \ilRegistrationSettings $settings,
40 private PendingRegistrationRepository $pending_reg_repository,
41 private \ilDBInterface $db,
42 private \ilComponentLogger $logger,
43 private ClockFactory $clock_factory
44 ) {
45 }
46
48 {
49 $pending_reg = $this->findConfirmableRegistration($hash);
50
52 $user = \ilObjectFactory::getInstanceByObjId($pending_reg->userId()->toInt());
53 $this->activateUser($user);
54
55 $this->pending_reg_repository->delete(
56 $pending_reg->withConfirmed()
57 );
58
59 return $user;
60 }
61
68 {
69 $pending_reg = $this->pending_reg_repository->findByHashValue($hash->toString());
70 if ($pending_reg === null) {
72 }
73
74 $lifetime = $this->settings->getRegistrationHashLifetime();
75 $pending_reg = $pending_reg->withEvaluatedState(
76 $this->clock_factory->utc()->now(),
77 $lifetime > 0 ? $lifetime : null
78 );
79
80 if ($pending_reg->isConfirmed()) {
82 }
83
84 if ($pending_reg->isExpired()) {
85 $this->triggerExpiredUserCleanup($pending_reg);
87 }
88
89 return $pending_reg;
90 }
91
92 public function distributeMailsOnRegistration(\ilObjUser $user): void
93 {
94 $pending_reg = $this->createPendingRegistration($user->getId());
95
96 $mail = new DualOptInMail(
97 $user,
98 $pending_reg,
99 $this->settings->getRegistrationHashLifetime()
100 );
101 $mail->setRecipients([$user]);
102 $mail->send();
103 }
104
105 private function createPendingRegistration(int $usr_id): PendingRegistration
106 {
107 $pending_reg = new PendingRegistration(
108 $this->pending_reg_repository->nextIdentity(),
109 new ObjectId($usr_id),
110 $this->pending_reg_repository->findNewHash(),
111 $this->clock_factory->utc()->now()
112 );
113 $this->pending_reg_repository->store($pending_reg);
114
115 return $pending_reg;
116 }
117
118 public function deleteExpiredUserObjects(int $usr_id): void
119 {
120 $this->logger->debug(
121 'Started deletion of inactive user objects with expired confirmation hash values (dual opt in) ...'
122 );
123
124 $lifetime = $this->settings->getRegistrationHashLifetime();
125 if ($lifetime <= 0) {
126 $this->logger->debug('Registration hash lifetime is <= 0, skipping deletion.');
127 return;
128 }
129
130 $now = $this->clock_factory->utc()->now();
131 $interval = new \DateInterval("PT{$lifetime}S");
132 $cutoff = $now->sub($interval);
133
134 $expired_regs = array_filter(
135 array_map(
136 static fn(PendingRegistration $reg): PendingRegistration => $reg->withEvaluatedState($now, $lifetime),
137 $this->pending_reg_repository->findExpired($cutoff->getTimestamp(), $usr_id)
138 ),
139 static fn(PendingRegistration $reg): bool => $reg->isExpired()
140 );
141
142 $this->logger->info(
143 \sprintf(
144 '%d inactive user objects eligible for deletion found and deleted (cutoff: %s, lifetime: %d s).',
145 \count($expired_regs),
146 $cutoff->format(\DateTimeInterface::ATOM),
147 $lifetime
148 )
149 );
150
151 $this->pending_reg_repository->delete(...$expired_regs);
152
153 $num_deleted_users = 0;
154 foreach ($expired_regs as $expired_reg) {
155 $user = \ilObjectFactory::getInstanceByObjId($expired_reg->userId()->toInt(), false);
156 if (!($user instanceof \ilObjUser)) {
157 continue;
158 }
159
160 $this->logger->info(
161 \sprintf(
162 'Deleting user (login: %s | id: %d) – expired dual opt-in (created: %s, cutoff: %s, lifetime: %d s)',
163 $user->getLogin(),
164 $user->getId(),
165 $expired_reg->createdAt()->format(\DateTimeInterface::ATOM),
166 $cutoff->format(\DateTimeInterface::ATOM),
167 $lifetime
168 )
169 );
170
171 $user->delete();
172 ++$num_deleted_users;
173 }
174
175 $this->logger->info(
176 \sprintf(
177 '%d inactive user objects with expired confirmation hash values (dual opt-in) deleted.',
178 $num_deleted_users
179 )
180 );
181 }
182
183 private function activateUser(\ilObjUser $user): void
184 {
185 $user->setActive(true);
186
187 $password = '';
188 if ($this->settings->passwordGenerationEnabled()) {
190 $user->setPasswd($password, \ilObjUser::PASSWD_PLAIN);
191 $user->setLastPasswordChangeTS($this->clock_factory->utc()->now()->getTimestamp());
192 }
193
194 $user->update();
195
196 $this->sendRegistrationMail($user, $password);
197 }
198
199 private function triggerExpiredUserCleanup(PendingRegistration $expired_reg): void
200 {
201 $soap_client = new \ilSoapClient();
202 $soap_client->setResponseTimeout(1);
203 $soap_client->enableWSDL(true);
204 $soap_client->init();
205
206 $this->logger->info(
207 'Triggered soap call (background process) for deletion of inactive ' .
208 'user objects with expired confirmation hash values (dual opt in) ...'
209 );
210
211 $sid = session_id() . '::' . CLIENT_ID;
212 $soap_client->call('deleteExpiredDualOptInUserObjects', [$sid, $expired_reg->userId()->toInt()]);
213 }
214
215 private function sendRegistrationMail(\ilObjUser $user, string $password): void
216 {
217 $account_mail = (new \ilAccountRegistrationMail(
218 $this->settings,
220 new NewAccountMailRepository($this->db)
221 ))->withEmailConfirmationRegistrationMode();
222
223 if ($user->getPref('reg_target') ?? '') {
224 $account_mail = $account_mail->withPermanentLinkTarget($user->getPref('reg_target'));
225 }
226
227 $account_mail->send($user, $password);
228 }
229}
withEvaluatedState(\DateTimeImmutable $now, ?int $validity_in_seconds)
__construct(private \ilRegistrationSettings $settings, private PendingRegistrationRepository $pending_reg_repository, private \ilDBInterface $db, private \ilComponentLogger $logger, private ClockFactory $clock_factory)
Component logger with individual log levels by component id.
static getLogger(string $a_component_id)
Get component logger.
User class.
setLastPasswordChangeTS(int $a_last_password_change_ts)
setPasswd(string $a_str, string $a_type=ilObjUser::PASSWD_PLAIN)
setActive(bool $active, int $owner=0)
set user active state and updates system fields appropriately
const PASSWD_PLAIN
getPref(string $keyword)
static getInstanceByObjId(?int $obj_id, bool $stop_on_error=true)
get an instance of an Ilias object by object id
Class ilObjAuthSettingsGUI.
static generatePasswords(int $a_number)
Generate a number of passwords.
const CLIENT_ID
Definition: constants.php:41
verifyHashAndActivateUser(PendingRegistrationHash $hash)
Interface ilDBInterface.