mirror of
https://github.com/flarum/core.git
synced 2025-10-11 23:14:29 +02:00
Auth token and avatarUrl security improvements (#1514)
* Remove AbstractOAuth2Controller There is no reason to provide an implementation for a specific oAuth2 library in core; it's not generic enough (eg. auth-twitter can't use it). This code could be moved into another package which auth extensions depend on, but it's a negligible amount of relatively simple code that I don't think it's worth the trouble. * Introduce login providers Users can have many login providers (a combination of a provider name and an identifier for that user, eg. their Facebook ID). After retrieving user data from a provider (eg. Facebook), you pass the login provider details into the Auth\ResponseFactory. If an associated user is found, a response that logs them in will be returned. If not, a registration token will be created so the user can proceed to sign up. Once the token is fulfilled, the login provider will be associated with the user.
This commit is contained in:
@@ -11,18 +11,17 @@
|
||||
|
||||
namespace Flarum\User\Command;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Foundation\DispatchEventsTrait;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Flarum\User\AssertPermissionTrait;
|
||||
use Flarum\User\AuthToken;
|
||||
use Flarum\User\AvatarUploader;
|
||||
use Flarum\User\Event\RegisteringFromProvider;
|
||||
use Flarum\User\Event\Saving;
|
||||
use Flarum\User\Exception\PermissionDeniedException;
|
||||
use Flarum\User\RegistrationToken;
|
||||
use Flarum\User\User;
|
||||
use Flarum\User\UserValidator;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Contracts\Validation\Factory;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Intervention\Image\ImageManager;
|
||||
|
||||
@@ -46,34 +45,26 @@ class RegisterUserHandler
|
||||
*/
|
||||
protected $avatarUploader;
|
||||
|
||||
/**
|
||||
* @var Factory
|
||||
*/
|
||||
private $validatorFactory;
|
||||
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
* @param SettingsRepositoryInterface $settings
|
||||
* @param UserValidator $validator
|
||||
* @param AvatarUploader $avatarUploader
|
||||
* @param Factory $validatorFactory
|
||||
*/
|
||||
public function __construct(Dispatcher $events, SettingsRepositoryInterface $settings, UserValidator $validator, AvatarUploader $avatarUploader, Factory $validatorFactory)
|
||||
public function __construct(Dispatcher $events, SettingsRepositoryInterface $settings, UserValidator $validator, AvatarUploader $avatarUploader)
|
||||
{
|
||||
$this->events = $events;
|
||||
$this->settings = $settings;
|
||||
$this->validator = $validator;
|
||||
$this->avatarUploader = $avatarUploader;
|
||||
$this->validatorFactory = $validatorFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RegisterUser $command
|
||||
* @return User
|
||||
* @throws PermissionDeniedException if signup is closed and the actor is
|
||||
* not an administrator.
|
||||
* @throws \Flarum\User\Exception\InvalidConfirmationTokenException if an
|
||||
* email confirmation token is provided but is invalid.
|
||||
* @return User
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function handle(RegisterUser $command)
|
||||
{
|
||||
@@ -84,32 +75,24 @@ class RegisterUserHandler
|
||||
$this->assertAdmin($actor);
|
||||
}
|
||||
|
||||
$username = array_get($data, 'attributes.username');
|
||||
$email = array_get($data, 'attributes.email');
|
||||
$password = array_get($data, 'attributes.password');
|
||||
|
||||
// If a valid authentication token was provided as an attribute,
|
||||
// then we won't require the user to choose a password.
|
||||
if (isset($data['attributes']['token'])) {
|
||||
$token = AuthToken::validOrFail($data['attributes']['token']);
|
||||
$token = RegistrationToken::validOrFail($data['attributes']['token']);
|
||||
|
||||
$password = $password ?: str_random(20);
|
||||
}
|
||||
|
||||
$user = User::register($username, $email, $password);
|
||||
$user = User::register(
|
||||
array_get($data, 'attributes.username'),
|
||||
array_get($data, 'attributes.email'),
|
||||
$password
|
||||
);
|
||||
|
||||
// If a valid authentication token was provided, then we will assign
|
||||
// the attributes associated with it to the user's account. If this
|
||||
// includes an email address, then we will activate the user's account
|
||||
// from the get-go.
|
||||
if (isset($token)) {
|
||||
foreach ($token->payload as $k => $v) {
|
||||
$user->$k = $v;
|
||||
}
|
||||
|
||||
if (isset($token->payload['email'])) {
|
||||
$user->activate();
|
||||
}
|
||||
$this->applyToken($user, $token);
|
||||
}
|
||||
|
||||
if ($actor->isAdmin() && array_get($data, 'attributes.isEmailConfirmed')) {
|
||||
@@ -122,30 +105,53 @@ class RegisterUserHandler
|
||||
|
||||
$this->validator->assertValid(array_merge($user->getAttributes(), compact('password')));
|
||||
|
||||
if ($avatarUrl = array_get($data, 'attributes.avatarUrl')) {
|
||||
$validation = $this->validatorFactory->make(compact('avatarUrl'), ['avatarUrl' => 'url']);
|
||||
|
||||
if ($validation->fails()) {
|
||||
throw new ValidationException($validation);
|
||||
}
|
||||
|
||||
try {
|
||||
$image = (new ImageManager)->make($avatarUrl);
|
||||
|
||||
$this->avatarUploader->upload($user, $image);
|
||||
} catch (Exception $e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
$user->save();
|
||||
|
||||
if (isset($token)) {
|
||||
$token->delete();
|
||||
$this->fulfillToken($user, $token);
|
||||
}
|
||||
|
||||
$this->dispatchEventsFor($user, $actor);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
private function applyToken(User $user, RegistrationToken $token)
|
||||
{
|
||||
foreach ($token->user_attributes as $k => $v) {
|
||||
if ($k === 'avatar_url') {
|
||||
$this->uploadAvatarFromUrl($user, $v);
|
||||
continue;
|
||||
}
|
||||
|
||||
$user->$k = $v;
|
||||
|
||||
if ($k === 'email') {
|
||||
$user->activate();
|
||||
}
|
||||
}
|
||||
|
||||
$this->events->dispatch(
|
||||
new RegisteringFromProvider($user, $token->provider, $token->payload)
|
||||
);
|
||||
}
|
||||
|
||||
private function uploadAvatarFromUrl(User $user, string $url)
|
||||
{
|
||||
$image = (new ImageManager)->make($url);
|
||||
|
||||
$this->avatarUploader->upload($user, $image);
|
||||
}
|
||||
|
||||
private function fulfillToken(User $user, RegistrationToken $token)
|
||||
{
|
||||
$token->delete();
|
||||
|
||||
if ($token->provider && $token->identifier) {
|
||||
$user->loginProviders()->create([
|
||||
'provider' => $token->provider,
|
||||
'identifier' => $token->identifier
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user