2014-08-31 09:03:11 +02:00
|
|
|
<?php
|
|
|
|
namespace Szurubooru\Services;
|
|
|
|
|
|
|
|
class UserService
|
|
|
|
{
|
2014-09-03 19:07:53 +02:00
|
|
|
private $config;
|
2014-09-02 09:09:07 +02:00
|
|
|
private $validator;
|
2014-09-14 18:41:14 +02:00
|
|
|
private $transactionManager;
|
2014-08-31 17:42:48 +02:00
|
|
|
private $userDao;
|
|
|
|
private $passwordService;
|
|
|
|
private $emailService;
|
2014-09-07 00:33:46 +02:00
|
|
|
private $fileService;
|
2014-09-09 17:49:19 +02:00
|
|
|
private $thumbnailService;
|
2014-08-31 17:42:48 +02:00
|
|
|
private $timeService;
|
2014-09-08 13:06:32 +02:00
|
|
|
private $tokenService;
|
2014-08-31 09:03:11 +02:00
|
|
|
|
2014-08-31 14:07:46 +02:00
|
|
|
public function __construct(
|
2014-09-03 19:07:53 +02:00
|
|
|
\Szurubooru\Config $config,
|
2014-09-02 09:09:07 +02:00
|
|
|
\Szurubooru\Validator $validator,
|
2014-09-14 18:41:14 +02:00
|
|
|
\Szurubooru\Dao\TransactionManager $transactionManager,
|
2014-08-31 14:07:46 +02:00
|
|
|
\Szurubooru\Dao\UserDao $userDao,
|
2014-08-31 17:42:48 +02:00
|
|
|
\Szurubooru\Services\PasswordService $passwordService,
|
|
|
|
\Szurubooru\Services\EmailService $emailService,
|
2014-09-07 00:33:46 +02:00
|
|
|
\Szurubooru\Services\FileService $fileService,
|
2014-09-09 17:49:19 +02:00
|
|
|
\Szurubooru\Services\ThumbnailService $thumbnailService,
|
2014-09-08 13:06:32 +02:00
|
|
|
\Szurubooru\Services\TimeService $timeService,
|
|
|
|
\Szurubooru\Services\TokenService $tokenService)
|
2014-08-31 09:03:11 +02:00
|
|
|
{
|
2014-09-03 19:07:53 +02:00
|
|
|
$this->config = $config;
|
2014-09-02 09:09:07 +02:00
|
|
|
$this->validator = $validator;
|
2014-09-14 18:41:14 +02:00
|
|
|
$this->transactionManager = $transactionManager;
|
2014-08-31 17:42:48 +02:00
|
|
|
$this->userDao = $userDao;
|
|
|
|
$this->passwordService = $passwordService;
|
|
|
|
$this->emailService = $emailService;
|
2014-09-07 00:33:46 +02:00
|
|
|
$this->fileService = $fileService;
|
2014-09-09 17:49:19 +02:00
|
|
|
$this->thumbnailService = $thumbnailService;
|
2014-08-31 17:42:48 +02:00
|
|
|
$this->timeService = $timeService;
|
2014-09-08 13:06:32 +02:00
|
|
|
$this->tokenService = $tokenService;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getByNameOrEmail($userNameOrEmail, $allowUnconfirmed = false)
|
|
|
|
{
|
2014-09-16 14:04:53 +02:00
|
|
|
$transactionFunc = function() use ($userNameOrEmail, $allowUnconfirmed)
|
2014-09-14 18:41:14 +02:00
|
|
|
{
|
|
|
|
$user = $this->userDao->findByName($userNameOrEmail);
|
|
|
|
if ($user)
|
|
|
|
return $user;
|
2014-09-08 13:06:32 +02:00
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
$user = $this->userDao->findByEmail($userNameOrEmail, $allowUnconfirmed);
|
|
|
|
if ($user)
|
|
|
|
return $user;
|
2014-09-08 13:06:32 +02:00
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
throw new \InvalidArgumentException('User "' . $userNameOrEmail . '" was not found.');
|
2014-09-16 14:04:53 +02:00
|
|
|
};
|
|
|
|
return $this->transactionManager->rollback($transactionFunc);
|
2014-08-31 09:03:11 +02:00
|
|
|
}
|
|
|
|
|
2014-09-08 08:20:31 +02:00
|
|
|
public function getByName($userName)
|
2014-09-04 19:07:57 +02:00
|
|
|
{
|
2014-09-16 14:04:53 +02:00
|
|
|
$transactionFunc = function() use ($userName)
|
2014-09-14 18:41:14 +02:00
|
|
|
{
|
|
|
|
$user = $this->userDao->findByName($userName);
|
|
|
|
if (!$user)
|
|
|
|
throw new \InvalidArgumentException('User with name "' . $userName . '" was not found.');
|
|
|
|
return $user;
|
2014-09-16 14:04:53 +02:00
|
|
|
};
|
|
|
|
return $this->transactionManager->rollback($transactionFunc);
|
2014-09-08 08:20:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getById($userId)
|
|
|
|
{
|
2014-09-16 14:04:53 +02:00
|
|
|
$transactionFunc = function() use ($userId)
|
2014-09-14 18:41:14 +02:00
|
|
|
{
|
|
|
|
$user = $this->userDao->findById($userId);
|
|
|
|
if (!$user)
|
|
|
|
throw new \InvalidArgumentException('User with id "' . $userId . '" was not found.');
|
|
|
|
return $user;
|
2014-09-16 14:04:53 +02:00
|
|
|
};
|
|
|
|
return $this->transactionManager->rollback($transactionFunc);
|
2014-09-04 19:07:57 +02:00
|
|
|
}
|
|
|
|
|
2014-09-26 19:14:34 +02:00
|
|
|
public function getFiltered(\Szurubooru\SearchServices\Filters\UserFilter $filter)
|
2014-09-03 19:07:53 +02:00
|
|
|
{
|
2014-09-26 19:14:34 +02:00
|
|
|
$transactionFunc = function() use ($filter)
|
2014-09-14 18:41:14 +02:00
|
|
|
{
|
2014-09-26 19:14:34 +02:00
|
|
|
return $this->userDao->findFiltered($filter);
|
2014-09-16 14:04:53 +02:00
|
|
|
};
|
|
|
|
return $this->transactionManager->rollback($transactionFunc);
|
2014-09-03 19:07:53 +02:00
|
|
|
}
|
|
|
|
|
2014-09-07 00:33:46 +02:00
|
|
|
public function createUser(\Szurubooru\FormData\RegistrationFormData $formData)
|
2014-08-31 09:03:11 +02:00
|
|
|
{
|
2014-09-16 14:04:53 +02:00
|
|
|
$transactionFunc = function() use ($formData)
|
2014-09-14 18:41:14 +02:00
|
|
|
{
|
|
|
|
$formData->validate($this->validator);
|
2014-08-31 09:03:11 +02:00
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
$user = new \Szurubooru\Entities\User();
|
|
|
|
$user->setRegistrationTime($this->timeService->getCurrentTime());
|
|
|
|
$user->setLastLoginTime(null);
|
|
|
|
$user->setAccessRank($this->userDao->hasAnyUsers()
|
|
|
|
? \Szurubooru\Entities\User::ACCESS_RANK_REGULAR_USER
|
|
|
|
: \Szurubooru\Entities\User::ACCESS_RANK_ADMINISTRATOR);
|
2014-08-31 09:03:11 +02:00
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
$this->updateUserName($user, $formData->userName);
|
|
|
|
$this->updateUserPassword($user, $formData->password);
|
|
|
|
$this->updateUserAvatarStyle($user, \Szurubooru\Entities\User::AVATAR_STYLE_GRAVATAR);
|
|
|
|
$this->updateUserEmail($user, $formData->email);
|
|
|
|
return $this->userDao->save($user);
|
2014-09-16 14:04:53 +02:00
|
|
|
};
|
|
|
|
return $this->transactionManager->commit($transactionFunc);
|
2014-08-31 09:03:11 +02:00
|
|
|
}
|
2014-09-05 13:50:51 +02:00
|
|
|
|
2014-09-08 13:06:32 +02:00
|
|
|
public function updateUser(\Szurubooru\Entities\User $user, \Szurubooru\FormData\UserEditFormData $formData)
|
2014-09-05 13:50:51 +02:00
|
|
|
{
|
2014-09-16 14:04:53 +02:00
|
|
|
$transactionFunc = function() use ($user, $formData)
|
2014-09-14 18:41:14 +02:00
|
|
|
{
|
|
|
|
$this->validator->validate($formData);
|
2014-09-09 19:38:16 +02:00
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
if ($formData->avatarStyle !== null)
|
|
|
|
$this->updateUserAvatarStyle($user, $formData->avatarStyle);
|
2014-09-07 00:33:46 +02:00
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
if ($formData->avatarContent !== null)
|
|
|
|
$this->updateUserAvatarContent($user, $formData->avatarContent);
|
2014-09-07 00:33:46 +02:00
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
if ($formData->userName !== null)
|
|
|
|
$this->updateUserName($user, $formData->userName);
|
2014-09-07 00:33:46 +02:00
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
if ($formData->password !== null)
|
|
|
|
$this->updateUserPassword($user, $formData->password);
|
2014-09-07 00:33:46 +02:00
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
if ($formData->email !== null)
|
|
|
|
$this->updateUserEmail($user, $formData->email);
|
2014-09-07 00:33:46 +02:00
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
if ($formData->accessRank !== null)
|
|
|
|
$this->updateUserAccessRank($user, $formData->accessRank);
|
2014-09-07 00:33:46 +02:00
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
if ($formData->browsingSettings !== null)
|
|
|
|
$this->updateUserBrowsingSettings($user, $formData->browsingSettings);
|
2014-09-10 18:22:15 +02:00
|
|
|
|
2014-09-30 13:22:11 +02:00
|
|
|
if ($formData->banned !== $user->isBanned())
|
|
|
|
$user->setBanned(boolval($formData->banned));
|
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
return $this->userDao->save($user);
|
2014-09-16 14:04:53 +02:00
|
|
|
};
|
|
|
|
return $this->transactionManager->commit($transactionFunc);
|
2014-09-10 18:22:15 +02:00
|
|
|
}
|
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
public function deleteUser(\Szurubooru\Entities\User $user)
|
2014-09-10 18:22:15 +02:00
|
|
|
{
|
2014-09-16 14:04:53 +02:00
|
|
|
$transactionFunc = function() use ($user)
|
2014-09-14 18:41:14 +02:00
|
|
|
{
|
|
|
|
$this->userDao->deleteById($user->getId());
|
2014-09-16 14:04:53 +02:00
|
|
|
};
|
|
|
|
$this->transactionManager->commit($transactionFunc);
|
2014-09-10 18:22:15 +02:00
|
|
|
}
|
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
public function sendPasswordResetEmail(\Szurubooru\Entities\User $user)
|
2014-09-10 18:22:15 +02:00
|
|
|
{
|
2014-09-16 14:04:53 +02:00
|
|
|
$transactionFunc = function() use ($user)
|
2014-09-14 18:41:14 +02:00
|
|
|
{
|
|
|
|
$token = $this->tokenService->createAndSaveToken($user->getName(), \Szurubooru\Entities\Token::PURPOSE_PASSWORD_RESET);
|
|
|
|
$this->emailService->sendPasswordResetEmail($user, $token);
|
2014-09-16 14:04:53 +02:00
|
|
|
};
|
|
|
|
$this->transactionManager->commit($transactionFunc);
|
2014-09-10 18:22:15 +02:00
|
|
|
}
|
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
public function finishPasswordReset(\Szurubooru\Entities\Token $token)
|
2014-09-10 18:22:15 +02:00
|
|
|
{
|
2014-09-16 14:04:53 +02:00
|
|
|
$transactionFunc = function() use ($token)
|
2014-09-14 18:41:14 +02:00
|
|
|
{
|
|
|
|
if ($token->getPurpose() !== \Szurubooru\Entities\Token::PURPOSE_PASSWORD_RESET)
|
|
|
|
throw new \Exception('This token is not a password reset token.');
|
|
|
|
|
|
|
|
$user = $this->getByName($token->getAdditionalData());
|
|
|
|
$newPassword = $this->passwordService->getRandomPassword();
|
|
|
|
$user->setPasswordHash($this->passwordService->getHash($newPassword));
|
|
|
|
$this->userDao->save($user);
|
|
|
|
$this->tokenService->invalidateByName($token->getName());
|
|
|
|
return $newPassword;
|
2014-09-16 14:04:53 +02:00
|
|
|
};
|
|
|
|
return $this->transactionManager->commit($transactionFunc);
|
2014-09-10 18:22:15 +02:00
|
|
|
}
|
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
public function sendActivationEmail(\Szurubooru\Entities\User $user)
|
2014-09-10 18:22:15 +02:00
|
|
|
{
|
2014-09-16 14:04:53 +02:00
|
|
|
$transactionFunc = function() use ($user)
|
2014-09-14 18:41:14 +02:00
|
|
|
{
|
|
|
|
$token = $this->tokenService->createAndSaveToken($user->getName(), \Szurubooru\Entities\Token::PURPOSE_ACTIVATE);
|
|
|
|
$this->emailService->sendActivationEmail($user, $token);
|
2014-09-16 14:04:53 +02:00
|
|
|
};
|
|
|
|
$this->transactionManager->commit($transactionFunc);
|
2014-09-10 18:22:15 +02:00
|
|
|
}
|
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
public function finishActivation(\Szurubooru\Entities\Token $token)
|
2014-09-10 18:22:15 +02:00
|
|
|
{
|
2014-09-16 14:04:53 +02:00
|
|
|
$transactionFunc = function() use ($token)
|
2014-09-10 18:22:15 +02:00
|
|
|
{
|
2014-09-14 18:41:14 +02:00
|
|
|
if ($token->getPurpose() !== \Szurubooru\Entities\Token::PURPOSE_ACTIVATE)
|
|
|
|
throw new \Exception('This token is not an activation token.');
|
|
|
|
|
|
|
|
$user = $this->getByName($token->getAdditionalData());
|
|
|
|
$user = $this->confirmUserEmail($user);
|
|
|
|
$this->userDao->save($user);
|
|
|
|
$this->tokenService->invalidateByName($token->getName());
|
2014-09-16 14:04:53 +02:00
|
|
|
};
|
|
|
|
$this->transactionManager->commit($transactionFunc);
|
2014-09-10 18:22:15 +02:00
|
|
|
}
|
2014-09-07 14:50:16 +02:00
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
private function updateUserAvatarStyle(\Szurubooru\Entities\User $user, $newAvatarStyle)
|
2014-09-09 19:38:16 +02:00
|
|
|
{
|
2014-09-14 18:41:14 +02:00
|
|
|
$user->setAvatarStyle($newAvatarStyle);
|
2014-09-09 19:38:16 +02:00
|
|
|
}
|
|
|
|
|
2014-09-15 17:09:42 +02:00
|
|
|
private function updateUserAvatarContent(\Szurubooru\Entities\User $user, $newAvatarContent)
|
2014-09-07 00:33:46 +02:00
|
|
|
{
|
2014-09-25 19:11:41 +02:00
|
|
|
$mime = \Szurubooru\Helpers\MimeHelper::getMimeTypeFromBuffer($newAvatarContent);
|
|
|
|
if (!\Szurubooru\Helpers\MimeHelper::isImage($mime))
|
|
|
|
throw new \DomainException('Avatar must be an image.');
|
|
|
|
|
|
|
|
if (strlen($newAvatarContent) > $this->config->database->maxCustomThumbnailSize)
|
|
|
|
throw new \DomainException('Upload is too big.');
|
|
|
|
|
2014-09-20 12:45:56 +02:00
|
|
|
$user->setCustomAvatarSourceContent($newAvatarContent);
|
2014-09-05 13:50:51 +02:00
|
|
|
}
|
2014-09-07 00:33:46 +02:00
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
private function updateUserName(\Szurubooru\Entities\User $user, $newName)
|
2014-09-08 13:06:32 +02:00
|
|
|
{
|
2014-09-14 18:41:14 +02:00
|
|
|
$this->assertNoUserWithThisName($user, $newName);
|
|
|
|
$user->setName($newName);
|
2014-09-08 13:06:32 +02:00
|
|
|
}
|
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
private function updateUserPassword(\Szurubooru\Entities\User $user, $newPassword)
|
2014-09-08 13:06:32 +02:00
|
|
|
{
|
2014-09-13 23:58:13 +02:00
|
|
|
$user->setPasswordHash($this->passwordService->getHash($newPassword));
|
2014-09-08 13:06:32 +02:00
|
|
|
}
|
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
private function updateUserEmail(\Szurubooru\Entities\User $user, $newEmail)
|
2014-09-08 13:06:32 +02:00
|
|
|
{
|
2014-09-14 18:41:14 +02:00
|
|
|
if ($user->getEmail() === $newEmail)
|
|
|
|
{
|
|
|
|
$user->setEmailUnconfirmed(null);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$this->assertNoUserWithThisEmail($user, $newEmail);
|
|
|
|
$user->setEmailUnconfirmed($newEmail);
|
|
|
|
$user = $this->sendActivationEmailIfNeeded($user);
|
|
|
|
}
|
2014-09-08 13:06:32 +02:00
|
|
|
}
|
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
private function updateUserAccessRank(\Szurubooru\Entities\User $user, $newAccessRank)
|
2014-09-08 13:06:32 +02:00
|
|
|
{
|
2014-09-14 18:41:14 +02:00
|
|
|
$user->setAccessRank($newAccessRank);
|
2014-09-08 13:06:32 +02:00
|
|
|
}
|
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
private function updateUserBrowsingSettings(\Szurubooru\Entities\User $user, $newBrowsingSettings)
|
2014-09-09 19:38:16 +02:00
|
|
|
{
|
2014-09-14 18:41:14 +02:00
|
|
|
$user->setBrowsingSettings($newBrowsingSettings);
|
2014-09-09 19:38:16 +02:00
|
|
|
}
|
|
|
|
|
2014-09-14 18:41:14 +02:00
|
|
|
public function updateUserLastLoginTime(\Szurubooru\Entities\User $user)
|
2014-09-09 19:38:16 +02:00
|
|
|
{
|
2014-09-16 14:04:53 +02:00
|
|
|
$transactionFunc = function() use ($user)
|
2014-09-14 18:41:14 +02:00
|
|
|
{
|
|
|
|
$user->setLastLoginTime($this->timeService->getCurrentTime());
|
|
|
|
$this->userDao->save($user);
|
2014-09-16 14:04:53 +02:00
|
|
|
};
|
|
|
|
$this->transactionManager->commit($transactionFunc);
|
2014-09-09 19:38:16 +02:00
|
|
|
}
|
|
|
|
|
2014-09-09 12:34:57 +02:00
|
|
|
private function sendActivationEmailIfNeeded(\Szurubooru\Entities\User $user)
|
2014-09-07 00:33:46 +02:00
|
|
|
{
|
2014-09-14 18:41:14 +02:00
|
|
|
if ($user->getAccessRank() === \Szurubooru\Entities\User::ACCESS_RANK_ADMINISTRATOR
|
|
|
|
or !$this->config->security->needEmailActivationToRegister)
|
2014-09-08 13:06:32 +02:00
|
|
|
{
|
2014-09-09 19:38:16 +02:00
|
|
|
$user = $this->confirmUserEmail($user);
|
2014-09-08 13:06:32 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$this->sendActivationEmail($user);
|
|
|
|
}
|
2014-09-09 12:34:57 +02:00
|
|
|
return $user;
|
2014-09-08 13:06:32 +02:00
|
|
|
}
|
|
|
|
|
2014-09-09 19:38:16 +02:00
|
|
|
private function confirmUserEmail(\Szurubooru\Entities\User $user)
|
2014-09-08 13:06:32 +02:00
|
|
|
{
|
|
|
|
//security issue:
|
|
|
|
//1. two users set their unconfirmed mail to godzilla@empire.gov
|
|
|
|
//2. activation mail is sent to both of them
|
|
|
|
//3. first user confirms, ok
|
|
|
|
//4. second user confirms, ok
|
|
|
|
//5. two users share the same mail --> problem.
|
|
|
|
//by checking here again for users with such mail, this problem is solved with first-come first-serve approach:
|
|
|
|
//whoever confirms e-mail first, wins.
|
2014-09-13 23:58:13 +02:00
|
|
|
$this->assertNoUserWithThisEmail($user, $user->getEmailUnconfirmed());
|
2014-09-08 13:06:32 +02:00
|
|
|
|
2014-09-14 16:44:57 +02:00
|
|
|
$user->setAccountConfirmed(true);
|
2014-09-17 11:18:54 +02:00
|
|
|
$user->setEmail($user->getEmailUnconfirmed());
|
|
|
|
$user->setEmailUnconfirmed(null);
|
2014-09-09 12:34:57 +02:00
|
|
|
return $user;
|
2014-09-07 00:33:46 +02:00
|
|
|
}
|
2014-09-10 18:22:15 +02:00
|
|
|
|
|
|
|
private function assertNoUserWithThisName(\Szurubooru\Entities\User $owner, $nameToCheck)
|
|
|
|
{
|
2014-09-13 23:58:13 +02:00
|
|
|
$userWithThisName = $this->userDao->findByName($nameToCheck);
|
|
|
|
if ($userWithThisName and $userWithThisName->getId() !== $owner->getId())
|
2014-09-10 18:22:15 +02:00
|
|
|
throw new \DomainException('User with this name already exists.');
|
|
|
|
}
|
|
|
|
|
|
|
|
private function assertNoUserWithThisEmail(\Szurubooru\Entities\User $owner, $emailToCheck)
|
|
|
|
{
|
2014-09-24 23:29:47 +02:00
|
|
|
if (!$emailToCheck)
|
|
|
|
return;
|
2014-09-13 23:58:13 +02:00
|
|
|
$userWithThisEmail = $this->userDao->findByEmail($emailToCheck);
|
|
|
|
if ($userWithThisEmail and $userWithThisEmail->getId() !== $owner->getId())
|
2014-09-10 18:22:15 +02:00
|
|
|
throw new \DomainException('User with this e-mail already exists.');
|
|
|
|
}
|
2014-08-31 09:03:11 +02:00
|
|
|
}
|