*/ class UserController extends Controller { public ?string $groupIds = null; public ?string $organization = null; public ?string $password = null; public ?string $welcomeFile = null; public ?string $siteSubdomain = null; public ?string $consultationPath = null; public bool $forcePasswordChange = false; public bool $forceTwoFactor = false; public bool $preventPasswordChange = false; public bool $disablePrivateComments = false; public bool $fixedName = false; public bool $fixedOrganization = false; public function options($actionID): array { return match ($actionID) { 'create', 'create-or-update' => ['siteSubdomain', 'consultationPath', 'groupIds', 'organization', 'welcomeFile', 'forcePasswordChange', 'forceTwoFactor', 'preventPasswordChange', 'fixedName', 'fixedOrganization', 'disablePrivateComments'], 'update' => ['siteSubdomain', 'consultationPath', 'groupIds', 'organization', 'password', 'welcomeFile'], default => [], }; } private function findUserByAuth(string $auth): ?User { if (!str_contains($auth, ':') && str_contains($auth, '@')) { $auth = 'email:' . $auth; } return User::findOne(['auth' => $auth]); } private function getAndSetConsultation(): Consultation { $consultation = null; if ($this->siteSubdomain) { $site = Site::findOne(['subdomain' => $this->siteSubdomain]); } else { $site = Site::findOne(['subdomain' => AntragsgruenApp::getInstance()->siteSubdomain]); } if ($this->consultationPath) { foreach ($site->consultations as $siteConsultation) { if ($siteConsultation->urlPath === $this->consultationPath) { $consultation = $siteConsultation; } } } else { $consultation = $site->currentConsultation; } if (!$consultation) { throw new \RuntimeException('Consultation not found'); } UrlHelper::setCurrentSite($site); UrlHelper::setCurrentConsultation($consultation); return $consultation; } /** * @return ConsultationUserGroup[] */ private function getToSetUserGroups(): array { if (!$this->groupIds) { return []; } $orgaIds = array_map('intval', explode(',', $this->groupIds)); $toUserGroups = []; foreach ($orgaIds as $orgaId) { $group = ConsultationUserGroup::findOne(['id' => $orgaId]); if ($group) { $toUserGroups[] = $group; } else { throw new \Exception('User group not found: ' . $orgaId); } } return $toUserGroups; } private function sendWelcomeEmail(Consultation $consultation, User $user, string $welcomeTemplate, ?string $password): void { \Yii::$app->urlManager->setBaseUrl("/"); \Yii::$app->language = substr($consultation->wordingBase, 0, 2); $methods = new UserGroupAdminMethods(); $methods->setRequestData($consultation, null, null); $methods->sendWelcomeEmail($user, $welcomeTemplate, $password); } /** * Resets the password for a given user */ public function actionSetUserPassword(string $auth, string $password): int { /** @var User|null $user */ $user = $this->findUserByAuth($auth); if (!$user) { $this->stderr('User not found: ' . $auth . "\n"); return 1; } $user->changePassword($password); $this->stdout('The password has been changed.' . "\n"); return 0; } /** * Creates a user or updates their data if already existing * * Example: * ./yii user/create-or-update email:test@example.org test@example.org "Given Name" "Family Name" TestPassword --groupIds 1,2 --organization Antragsgrün --welcome-file welcome-email.txt * * "groupIds" refer to the primary IDs in "consultationUserGroup" * Optional flags: * --forcePasswordChange * --forceTwoFactor * --preventPasswordChange * --fixedName * --fixedOrganization * --disablePrivateComments */ public function actionCreateOrUpdate(string $auth, string $email, string $givenName, string $familyName, string $password): int { /** @var User|null $user */ $user = $this->findUserByAuth($auth); if ($user) { $this->updateUser($user, $this->organization, $password); } else { $this->actionCreate($auth, $email, $givenName, $familyName, $password); } return 0; } /** * Creates a user * * Example: * ./yii user/create email:test@example.org test@example.org "Given Name" "Family Name" TestPassword --groupIds 1,2 --organization Antragsgrün --welcome-file welcome-email.txt * * "groupIds" refer to the primary IDs in "consultationUserGroup" * Optional flags: * --forcePasswordChange * --forceTwoFactor * --preventPasswordChange * --fixedName * --fixedOrganization * --disablePrivateComments */ public function actionCreate(string $auth, string $email, string $givenName, string $familyName, string $password): int { $welcomeTemplate = null; if ($this->welcomeFile) { if (!file_exists($this->welcomeFile)) { throw new \RuntimeException('welcome template not found'); } $welcomeTemplate = file_get_contents($this->welcomeFile); } $consultation = $this->getAndSetConsultation(); $toUserGroups = $this->getToSetUserGroups(); $user = new User(); $user->auth = $auth; $user->email = $email; $user->nameGiven = $givenName; $user->nameFamily = $familyName; $user->name = $givenName . ' ' . $familyName; $user->emailConfirmed = 1; $user->pwdEnc = password_hash($password, PASSWORD_DEFAULT); $user->status = User::STATUS_CONFIRMED; $user->organizationIds = ''; $user->organization = $this->organization; $user->fixedData = 0; if ($this->fixedName) { $user->fixedData |= User::FIXED_NAME; } if ($this->fixedOrganization) { $user->fixedData |= User::FIXED_ORGA; } $userSettings = $user->getSettingsObj(); if ($this->forcePasswordChange) { $userSettings->forcePasswordChange = true; } if ($this->disablePrivateComments) { $userSettings->allowPrivateComments = false; } if ($this->preventPasswordChange) { $userSettings->preventPasswordChange = true; } if ($this->forceTwoFactor) { $userSettings->enforceTwoFactorAuthentication = true; } $user->setSettingsObj($userSettings); $user->save(); foreach ($toUserGroups as $toUserGroup) { $user->link('userGroups', $toUserGroup); } $this->stdout('Created the user: ' . $user->auth . "\n"); if ($welcomeTemplate) { $this->sendWelcomeEmail($consultation, $user, $welcomeTemplate, $password); } return 0; } /** * Updates a user * * Example: * ./yii user/update email:test@example.org --password TestPassword --groupIds 1,2 --organization Antragsgrün * * "groupIds" refer to the primary IDs in "consultationUserGroup" */ public function actionUpdate(string $auth): int { /** @var User|null $user */ $user = $this->findUserByAuth($auth); if (!$user) { $this->stderr('User not found: ' . $auth . "\n"); return 1; } $this->updateUser($user, $this->organization, $this->password); return 0; } private function updateUser(?User $user, ?string $organization, ?string $password): void { $welcomeTemplate = null; if ($this->welcomeFile) { if (!file_exists($this->welcomeFile)) { throw new \RuntimeException('welcome template not found'); } $welcomeTemplate = file_get_contents($this->welcomeFile); } $consultation = $this->getAndSetConsultation(); $toUserGroups = $this->getToSetUserGroups(); if ($organization !== null) { $user->organization = $organization; $user->save(); } if ($password) { $user->changePassword($password); } $existingGroups = []; foreach ($user->userGroups as $userGroup) { $existingGroups[] = $userGroup->id; } foreach ($toUserGroups as $toUserGroup) { if (!in_array($toUserGroup->id, $existingGroups)) { $user->link('userGroups', $toUserGroup); } } $this->stdout('Updated the user: ' . $user->auth); if ($welcomeTemplate) { $this->sendWelcomeEmail($consultation, $user, $welcomeTemplate, $password); } } /** * Deletes a user * * Example: * ./yii user/update email:test@example.org */ public function actionDelete(string $auth): int { /** @var User|null $user */ $user = $this->findUserByAuth($auth); if (!$user) { $this->stderr('User not found: ' . $auth . "\n"); return 1; } $auth = $user->auth; $user->deleteAccount(); $this->stdout('User deleted: ' . $auth . "\n"); return 0; } }