Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ protected function registerAuthConfiguration()
*/
protected function registerSingletons()
{
$this->app->singleton('user.actions', \RainLab\User\Classes\ActionManager::class);
$this->app->singleton('user.twofactor', \RainLab\User\Classes\TwoFactorManager::class);

// Laravel services
Expand Down
130 changes: 130 additions & 0 deletions classes/ActionManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<?php namespace RainLab\User\Classes;

use App;
use Auth;
use Event;
use Request;
use RainLab\User\Models\User;
use RainLab\User\Models\UserLog;
use RainLab\User\Helpers\User as UserHelper;
use Illuminate\Contracts\Auth\PasswordBroker;

/**
* ActionManager implements user workflows shared by CMS components and
* headless integrations, such as REST or GraphQL endpoints.
*
* @package rainlab\user
* @author Alexey Bobkov, Samuel Georges
*/
class ActionManager
{
use \RainLab\User\Classes\ActionManager\ActionLogin;
use \RainLab\User\Classes\ActionManager\ActionLogout;
use \RainLab\User\Classes\ActionManager\ActionRegisterUser;
use \RainLab\User\Classes\ActionManager\ActionTwoFactorLogin;
use \RainLab\User\Classes\ActionManager\ActionRecoverPassword;
use \RainLab\User\Classes\ActionManager\ActionResetPassword;
use \RainLab\User\Classes\ActionManager\ActionChangePassword;
use \RainLab\User\Classes\ActionManager\ActionUpdateProfile;
use \RainLab\User\Classes\ActionManager\ActionVerifyEmail;
use \RainLab\User\Classes\ActionManager\ActionDeleteUser;
use \RainLab\User\Classes\ActionManager\ActionTwoFactor;
use \RainLab\User\Classes\ActionManager\ActionBrowserSessions;

/**
* @var string TWO_FACTOR_CHALLENGE result when a login must complete a two factor challenge
*/
const TWO_FACTOR_CHALLENGE = 'two-factor-challenge';

/**
* @var object|null context is an optional host object used to emit events, typically a CMS component
*/
protected $context;

/**
* instance of the action manager
*/
public static function instance(): static
{
return App::make('user.actions');
}

/**
* withContext returns a copy of this manager that fires events through the given host object
*/
public function withContext($context): static
{
$manager = clone $this;
$manager->context = $context;

return $manager;
}

/**
* user returns the currently authenticated user
*/
protected function user(): ?User
{
return Auth::user();
}

/**
* isUserPasswordValid checks a supplied password against the current user
*/
protected function isUserPasswordValid(string $password): bool
{
$user = $this->user();
$username = UserHelper::username();

if (!$user || !$password) {
return false;
}

return Auth::validate([
$username => $user->{$username},
'password' => $password
]);
}

/**
* makePasswordBroker to be used during password reset
*/
protected function makePasswordBroker(): PasswordBroker
{
return App::make('auth.password')->broker('users');
}

/**
* prepareAuthenticatedSession protects against session fixation
*/
protected function prepareAuthenticatedSession()
{
if (Request::hasSession()) {
Request::session()->regenerate();
}
}

/**
* recordUserLogAuthenticated
*/
protected function recordUserLogAuthenticated($user, $twoFactor = false)
{
UserLog::createRecord($user->getKey(), UserLog::TYPE_SELF_LOGIN, [
'user_full_name' => $user->full_name,
'is_two_factor' => $twoFactor
]);
}

/**
* fireSystemEvent fires through the context host when one is set, otherwise the
* global event fires with this manager as the emitting object
*/
protected function fireSystemEvent(string $event, array $params = [], bool $halt = true)
{
if ($this->context) {
return $this->context->fireSystemEvent($event, $params, $halt);
}

return Event::fire($event, array_merge([$this], $params), $halt);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php namespace RainLab\User\Components\Account;
<?php namespace RainLab\User\Classes\ActionManager;

use Db;
use Auth;
Expand All @@ -17,9 +17,10 @@
trait ActionBrowserSessions
{
/**
* fetchSessions
* getBrowserSessions returns the browser sessions of the authenticated user,
* only available when using the database session driver
*/
protected function fetchSessions()
public function getBrowserSessions(): array
{
if (Config::get('session.driver') !== 'database') {
return [];
Expand Down Expand Up @@ -48,11 +49,12 @@ protected function fetchSessions()
}

/**
* actionDeleteOtherSessions
* deleteOtherSessions logs out other browser sessions, requiring the user
* password for confirmation
*/
protected function actionDeleteOtherSessions()
public function deleteOtherSessions(array $input): void
{
$password = (string) post('password');
$password = (string) array_get($input, 'password');

if (!$this->isUserPasswordValid($password)) {
throw new ValidationException([
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<?php namespace RainLab\User\Components\ResetPassword;
<?php namespace RainLab\User\Classes\ActionManager;

use Auth;
use Request;
use Validator;
use ForbiddenException;
use RainLab\User\Models\User;
use RainLab\User\Models\UserLog;
use RainLab\User\Helpers\User as UserHelper;
use ForbiddenException;

/**
* ActionChangePassword
Expand All @@ -17,16 +17,17 @@
trait ActionChangePassword
{
/**
* actionChangePassword
* changePassword updates the password of the authenticated user, requiring
* the current password for confirmation
*/
protected function actionChangePassword()
public function changePassword(array $input): void
{
$user = Auth::user();
$user = $this->user();
if (!$user) {
throw new ForbiddenException;
}

$this->updateUserPassword($user, post());
$this->updateUserPassword($user, $input);

UserLog::createRecord($user->getKey(), UserLog::TYPE_SELF_PASSWORD_CHANGE);

Expand Down
55 changes: 55 additions & 0 deletions classes/actionmanager/ActionDeleteUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php namespace RainLab\User\Classes\ActionManager;

use Auth;
use Request;
use RainLab\User\Models\User;
use RainLab\User\Models\UserLog;
use ValidationException;
use ForbiddenException;

/**
* ActionDeleteUser
*
* @package rainlab\user
* @author Alexey Bobkov, Samuel Georges
*/
trait ActionDeleteUser
{
/**
* deleteUser removes the authenticated user from the system, requiring
* their password for confirmation
*/
public function deleteUser(array $input): void
{
if (!$this->user()) {
throw new ForbiddenException;
}

if (!$this->isUserPasswordValid((string) array_get($input, 'password'))) {
throw new ValidationException([
'password' => __('This password does not match our records.'),
]);
}

$this->deleteUserRecord($this->user()->fresh());

Auth::logout();

if (Request::hasSession()) {
Request::session()->invalidate();
Request::session()->regenerateToken();
}
}

/**
* deleteUserRecord
*/
protected function deleteUserRecord(User $user)
{
UserLog::createRecord($user->getKey(), UserLog::TYPE_SELF_DELETE, [
'user_full_name' => $user->full_name,
]);

$user->smartDelete();
}
}
Loading
Loading