szurubooru/src/Services/UserService.php

334 lines
9.8 KiB
PHP
Raw Normal View History

2014-08-31 09:03:11 +02:00
<?php
namespace Szurubooru\Services;
use Szurubooru\Config;
use Szurubooru\Dao\TransactionManager;
use Szurubooru\Dao\UserDao;
use Szurubooru\Entities\Token;
use Szurubooru\Entities\User;
use Szurubooru\FormData\RegistrationFormData;
use Szurubooru\FormData\UserEditFormData;
use Szurubooru\Helpers\MimeHelper;
use Szurubooru\Helpers\EnumHelper;
use Szurubooru\Search\Filters\UserFilter;
use Szurubooru\Services\EmailService;
use Szurubooru\Services\PasswordService;
use Szurubooru\Services\TimeService;
use Szurubooru\Services\TokenService;
use Szurubooru\Validator;
2014-08-31 09:03:11 +02:00
class UserService
{
private $config;
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;
private $timeService;
private $tokenService;
2014-08-31 09:03:11 +02:00
2014-08-31 14:07:46 +02:00
public function __construct(
Config $config,
Validator $validator,
TransactionManager $transactionManager,
UserDao $userDao,
PasswordService $passwordService,
EmailService $emailService,
TimeService $timeService,
TokenService $tokenService)
2014-08-31 09:03:11 +02:00
{
$this->config = $config;
$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;
$this->timeService = $timeService;
$this->tokenService = $tokenService;
}
public function getByNameOrEmail($userNameOrEmail, $allowUnconfirmed = false)
{
$transactionFunc = function() use ($userNameOrEmail, $allowUnconfirmed)
2014-09-14 18:41:14 +02:00
{
$user = $this->userDao->findByName($userNameOrEmail);
if ($user)
return $user;
2014-09-14 18:41:14 +02:00
$user = $this->userDao->findByEmail($userNameOrEmail, $allowUnconfirmed);
if ($user)
return $user;
2014-09-14 18:41:14 +02:00
throw new \InvalidArgumentException('User "' . $userNameOrEmail . '" was not found.');
};
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
{
$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;
};
return $this->transactionManager->rollback($transactionFunc);
2014-09-08 08:20:31 +02:00
}
public function getById($userId)
{
$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;
};
return $this->transactionManager->rollback($transactionFunc);
2014-09-04 19:07:57 +02:00
}
public function getFiltered(UserFilter $filter)
{
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);
};
return $this->transactionManager->rollback($transactionFunc);
}
public function createUser(RegistrationFormData $formData)
2014-08-31 09:03:11 +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
$user = new User();
2014-09-14 18:41:14 +02:00
$user->setRegistrationTime($this->timeService->getCurrentTime());
$user->setLastLoginTime(null);
$user->setAccessRank($this->userDao->hasAnyUsers()
? $this->getDefaultAccessRank()
: User::ACCESS_RANK_ADMINISTRATOR);
2014-10-05 22:26:56 +02:00
$user->setPasswordSalt($this->passwordService->getRandomPassword());
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, User::AVATAR_STYLE_GRAVATAR);
2014-09-14 18:41:14 +02:00
$this->updateUserEmail($user, $formData->email);
return $this->userDao->save($user);
};
return $this->transactionManager->commit($transactionFunc);
2014-08-31 09:03:11 +02:00
}
2014-09-05 13:50:51 +02:00
public function updateUser(User $user, UserEditFormData $formData)
2014-09-05 13:50:51 +02:00
{
$transactionFunc = function() use ($user, $formData)
2014-09-14 18:41:14 +02:00
{
$this->validator->validate($formData);
2014-09-14 18:41:14 +02:00
if ($formData->avatarStyle !== null)
$this->updateUserAvatarStyle($user, $formData->avatarStyle);
2014-09-14 18:41:14 +02:00
if ($formData->avatarContent !== null)
$this->updateUserAvatarContent($user, $formData->avatarContent);
2014-09-14 18:41:14 +02:00
if ($formData->userName !== null)
$this->updateUserName($user, $formData->userName);
2014-09-14 18:41:14 +02:00
if ($formData->password !== null)
$this->updateUserPassword($user, $formData->password);
2014-09-14 18:41:14 +02:00
if ($formData->email !== null)
$this->updateUserEmail($user, $formData->email);
2014-09-14 18:41:14 +02:00
if ($formData->accessRank !== null)
$this->updateUserAccessRank($user, $formData->accessRank);
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);
};
return $this->transactionManager->commit($transactionFunc);
2014-09-10 18:22:15 +02:00
}
public function deleteUser(User $user)
2014-09-10 18:22:15 +02:00
{
$transactionFunc = function() use ($user)
2014-09-14 18:41:14 +02:00
{
$this->userDao->deleteById($user->getId());
};
$this->transactionManager->commit($transactionFunc);
2014-09-10 18:22:15 +02:00
}
public function sendPasswordResetEmail(User $user)
2014-09-10 18:22:15 +02:00
{
$transactionFunc = function() use ($user)
2014-09-14 18:41:14 +02:00
{
$token = $this->tokenService->createAndSaveToken($user->getName(), Token::PURPOSE_PASSWORD_RESET);
2014-09-14 18:41:14 +02:00
$this->emailService->sendPasswordResetEmail($user, $token);
};
$this->transactionManager->commit($transactionFunc);
2014-09-10 18:22:15 +02:00
}
public function finishPasswordReset(Token $token)
2014-09-10 18:22:15 +02:00
{
$transactionFunc = function() use ($token)
2014-09-14 18:41:14 +02:00
{
if ($token->getPurpose() !== Token::PURPOSE_PASSWORD_RESET)
2014-09-14 18:41:14 +02:00
throw new \Exception('This token is not a password reset token.');
$user = $this->getByName($token->getAdditionalData());
$newPassword = $this->passwordService->getRandomPassword();
2014-10-05 22:26:56 +02:00
$this->updateUserPassword($user, $newPassword);
2014-09-14 18:41:14 +02:00
$this->userDao->save($user);
$this->tokenService->invalidateByName($token->getName());
return $newPassword;
};
return $this->transactionManager->commit($transactionFunc);
2014-09-10 18:22:15 +02:00
}
public function sendActivationEmail(User $user)
2014-09-10 18:22:15 +02:00
{
$transactionFunc = function() use ($user)
2014-09-14 18:41:14 +02:00
{
$token = $this->tokenService->createAndSaveToken($user->getName(), Token::PURPOSE_ACTIVATE);
2014-09-14 18:41:14 +02:00
$this->emailService->sendActivationEmail($user, $token);
};
$this->transactionManager->commit($transactionFunc);
2014-09-10 18:22:15 +02:00
}
public function finishActivation(Token $token)
2014-09-10 18:22:15 +02:00
{
$transactionFunc = function() use ($token)
2014-09-10 18:22:15 +02:00
{
if ($token->getPurpose() !== Token::PURPOSE_ACTIVATE)
2014-09-14 18:41:14 +02:00
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());
};
$this->transactionManager->commit($transactionFunc);
2014-09-10 18:22:15 +02:00
}
2014-09-07 14:50:16 +02:00
private function updateUserAvatarStyle(User $user, $newAvatarStyle)
{
2014-09-14 18:41:14 +02:00
$user->setAvatarStyle($newAvatarStyle);
}
private function updateUserAvatarContent(User $user, $newAvatarContent)
{
$mime = MimeHelper::getMimeTypeFromBuffer($newAvatarContent);
if (!MimeHelper::isImage($mime))
2014-09-25 19:11:41 +02:00
throw new \DomainException('Avatar must be an image.');
if (strlen($newAvatarContent) > $this->config->database->maxCustomThumbnailSize)
throw new \DomainException('Upload is too big.');
$user->setCustomAvatarSourceContent($newAvatarContent);
2014-09-05 13:50:51 +02:00
}
private function updateUserName(User $user, $newName)
{
2014-09-14 18:41:14 +02:00
$this->assertNoUserWithThisName($user, $newName);
$user->setName($newName);
}
private function updateUserPassword(User $user, $newPassword)
{
2014-10-05 22:26:56 +02:00
$user->setPasswordHash($this->passwordService->getHash($newPassword, $user->getPasswordSalt()));
}
private function updateUserEmail(User $user, $newEmail)
{
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);
}
}
private function updateUserAccessRank(User $user, $newAccessRank)
{
2014-09-14 18:41:14 +02:00
$user->setAccessRank($newAccessRank);
}
private function updateUserBrowsingSettings(User $user, $newBrowsingSettings)
{
2014-09-14 18:41:14 +02:00
$user->setBrowsingSettings($newBrowsingSettings);
}
public function updateUserLastLoginTime(User $user)
{
$transactionFunc = function() use ($user)
2014-09-14 18:41:14 +02:00
{
$user->setLastLoginTime($this->timeService->getCurrentTime());
$this->userDao->save($user);
};
$this->transactionManager->commit($transactionFunc);
}
private function sendActivationEmailIfNeeded(User $user)
{
if ($user->getAccessRank() === User::ACCESS_RANK_ADMINISTRATOR
2014-09-14 18:41:14 +02:00
or !$this->config->security->needEmailActivationToRegister)
{
$user = $this->confirmUserEmail($user);
}
else
{
$this->sendActivationEmail($user);
}
2014-09-09 12:34:57 +02:00
return $user;
}
private function confirmUserEmail(User $user)
{
//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.
$this->assertNoUserWithThisEmail($user, $user->getEmailUnconfirmed());
$user->setAccountConfirmed(true);
$user->setEmail($user->getEmailUnconfirmed());
$user->setEmailUnconfirmed(null);
2014-09-09 12:34:57 +02:00
return $user;
}
2014-09-10 18:22:15 +02:00
private function assertNoUserWithThisName(User $owner, $nameToCheck)
2014-09-10 18:22:15 +02:00
{
$userWithThisName = $this->userDao->findByName($nameToCheck);
2014-11-10 19:14:58 +01:00
if ($userWithThisName && $userWithThisName->getId() !== $owner->getId())
2014-09-10 18:22:15 +02:00
throw new \DomainException('User with this name already exists.');
}
private function assertNoUserWithThisEmail(User $owner, $emailToCheck)
2014-09-10 18:22:15 +02:00
{
if (!$emailToCheck)
return;
$userWithThisEmail = $this->userDao->findByEmail($emailToCheck);
2014-11-10 19:14:58 +01:00
if ($userWithThisEmail && $userWithThisEmail->getId() !== $owner->getId())
2014-09-10 18:22:15 +02:00
throw new \DomainException('User with this e-mail already exists.');
}
private function getDefaultAccessRank()
{
return EnumHelper::accessRankFromString($this->config->security->defaultAccessRank);
}
2014-08-31 09:03:11 +02:00
}