2020-05-02 23:45:29 +00:00
< ? php
namespace App\Http\Controllers ;
2020-05-13 21:47:51 +00:00
use App\Http\Requests\ChangeEmailRequest ;
use App\Http\Requests\ChangePasswordRequest ;
use App\Http\Requests\FlushSessionsRequest ;
2020-06-26 23:32:33 +00:00
use App\Http\Requests\DeleteUserRequest ;
use App\Http\Requests\SearchPlayerRequest ;
use App\Http\Requests\UpdateUserRequest ;
2020-07-17 21:44:10 +00:00
use App\Http\Requests\Add2FASecretRequest ;
use App\Http\Requests\Remove2FASecretRequest ;
2020-06-26 23:32:33 +00:00
2020-05-13 21:47:51 +00:00
use App\User ;
2020-06-26 23:32:33 +00:00
use App\Ban ;
2020-05-02 23:45:29 +00:00
use Illuminate\Http\Request ;
2020-05-13 21:47:51 +00:00
use Illuminate\Support\Facades\Auth ;
use Illuminate\Support\Facades\Hash ;
use Illuminate\Support\Facades\Log ;
2020-06-26 23:32:33 +00:00
use App\Facades\UUID ;
use App\Notifications\EmailChanged ;
use App\Notifications\ChangedPassword ;
use Spatie\Permission\Models\Role ;
2020-05-02 23:45:29 +00:00
2020-10-08 18:19:10 +00:00
use App\Traits\ReceivesAccountTokens ;
2020-07-17 21:44:10 +00:00
use Google2FA ;
2020-05-02 23:45:29 +00:00
class UserController extends Controller
{
2020-10-08 18:19:10 +00:00
use ReceivesAccountTokens ;
2020-06-27 18:15:33 +00:00
2020-05-02 23:45:29 +00:00
public function showStaffMembers ()
{
2020-06-27 18:15:33 +00:00
$this -> authorize ( 'viewStaff' , User :: class );
2020-06-26 23:32:33 +00:00
$staffRoles = [
'reviewer' ,
'hiringManager' ,
'admin'
]; // TODO: Un-hardcode this, move to config/roles.php
2020-06-27 18:15:33 +00:00
$users = User :: with ( 'roles' ) -> get ();
$staffMembers = collect ([]);
2020-06-26 23:32:33 +00:00
2020-06-27 18:15:33 +00:00
foreach ( $users as $user )
2020-06-26 23:32:33 +00:00
{
2020-06-27 18:15:33 +00:00
if ( empty ( $user -> roles ))
2020-06-26 23:32:33 +00:00
{
2020-06-27 18:15:33 +00:00
Log :: debug ( $user -> role -> name );
Log :: debug ( 'Staff list: User without role detected; Ignoring' );
continue ;
}
2020-06-26 23:32:33 +00:00
2020-06-27 18:15:33 +00:00
foreach ( $user -> roles as $role )
{
if ( in_array ( $role -> name , $staffRoles ))
2020-06-26 23:32:33 +00:00
{
2020-06-27 18:15:33 +00:00
$staffMembers -> push ( $user );
continue 2 ; // Skip directly to the next user instead of comparing more roles for the current user
2020-06-26 23:32:33 +00:00
}
}
}
2020-06-27 18:15:33 +00:00
return view ( 'dashboard.administration.staff-members' )
-> with ([
'users' => $staffMembers
]);
2020-05-02 23:45:29 +00:00
}
public function showPlayers ()
{
2020-06-27 18:15:33 +00:00
$this -> authorize ( 'viewPlayers' , User :: class );
2020-06-26 23:32:33 +00:00
$users = User :: with ( 'roles' ) -> get ();
$players = collect ([]);
foreach ( $users as $user )
{
// TODO: Might be problematic if we don't check if the role is user
if ( count ( $user -> roles ) == 1 )
{
$players -> push ( $user );
}
}
2020-06-27 18:15:33 +00:00
return view ( 'dashboard.administration.players' )
-> with ([
'users' => $players ,
'bannedUserCount' => Ban :: all () -> count ()
]);
2020-06-26 23:32:33 +00:00
}
public function showPlayersLike ( SearchPlayerRequest $request )
{
2020-06-27 18:15:33 +00:00
$this -> authorize ( 'viewPlayers' , User :: class );
2020-06-26 23:32:33 +00:00
2020-06-27 18:15:33 +00:00
$searchTerm = $request -> searchTerm ;
2020-06-26 23:32:33 +00:00
$matchingUsers = User :: query ()
-> where ( 'name' , 'LIKE' , " % { $searchTerm } % " )
-> orWhere ( 'email' , 'LIKE' , " % { $searchTerm } % " )
-> get ();
if ( ! $matchingUsers -> isEmpty ())
{ $request -> session () -> flash ( 'success' , 'There were ' . $matchingUsers -> count () . ' user(s) matching your search.' );
return view ( 'dashboard.administration.players' )
-> with ([
'users' => $matchingUsers ,
'bannedUserCount' => Ban :: all () -> count ()
]);
}
else
{
$request -> session () -> flash ( 'error' , 'Your search term did not return any results.' );
return redirect ( route ( 'registeredPlayerList' ));
}
2020-05-02 23:45:29 +00:00
}
2020-05-13 21:47:51 +00:00
2020-07-17 21:44:10 +00:00
public function showAccount ( Request $request )
2020-05-13 21:47:51 +00:00
{
2020-07-17 21:44:10 +00:00
$QRCode = null ;
if ( ! $request -> user () -> has2FA ())
{
if ( $request -> session () -> has ( 'twofaAttemptFailed' ))
{
$twoFactorSecret = $request -> session () -> get ( 'current2FA' );
}
else
{
$twoFactorSecret = Google2FA :: generateSecretKey ( 32 , '' );
$request -> session () -> put ( 'current2FA' , $twoFactorSecret );
}
$QRCode = Google2FA :: getQRCodeInline (
config ( 'app.name' ),
$request -> user () -> email ,
$twoFactorSecret
);
}
2020-05-13 21:47:51 +00:00
return view ( 'dashboard.user.profile.useraccount' )
2020-07-17 21:44:10 +00:00
-> with ( 'ip' , request () -> ip ())
-> with ( 'twofaQRCode' , $QRCode );
2020-05-13 21:47:51 +00:00
}
public function flushSessions ( FlushSessionsRequest $request )
{
// TODO: Move all log calls to a listener, which binds to an event fired by each significant event, such as this one
// This will allow for other actions to be performed on certain events (like login failed event)
Auth :: logoutOtherDevices ( $request -> currentPasswordFlush );
Log :: notice ( 'User ' . Auth :: user () -> name . ' has logged out other devices in their account' ,
[
'originIPAddress' => $request -> ip (),
'userID' => Auth :: user () -> id ,
'timestamp' => now ()
]);
$request -> session () -> flash ( 'success' , 'Successfully logged out other devices. Remember to change your password if you think you\'ve been compromised.' );
return redirect () -> back ();
}
public function changePassword ( ChangePasswordRequest $request )
{
$user = User :: find ( Auth :: user () -> id );
if ( ! is_null ( $user ))
{
$user -> password = Hash :: make ( $request -> newPassword );
$user -> save ();
Log :: info ( 'User ' . $user -> name . ' has changed their password' , [
'originIPAddress' => $request -> ip (),
'userID' => $user -> id ,
'timestamp' => now ()
]);
2020-06-26 23:32:33 +00:00
$user -> notify ( new ChangedPassword ());
2020-05-13 21:47:51 +00:00
2020-06-26 23:32:33 +00:00
Auth :: logout ();
2020-05-13 21:47:51 +00:00
return redirect () -> back ();
}
}
public function changeEmail ( ChangeEmailRequest $request )
{
$user = User :: find ( Auth :: user () -> id );
if ( ! is_null ( $user ))
{
$user -> email = $request -> newEmail ;
$user -> save ();
Log :: notice ( 'User ' . $user -> name . ' has just changed their contact email address' , [
'originIPAddress' => $request -> ip (),
'userID' => $user -> id ,
'timestamp' => now ()
]);
2020-06-26 23:32:33 +00:00
$user -> notify ( new EmailChanged ());
2020-05-13 21:47:51 +00:00
$request -> session () -> flash ( 'success' , 'Your email address has been changed!' );
}
else
{
$request -> session () -> flash ( 'error' , 'There has been an error whilst trying to update your account. Please contact administrators.' );
}
return redirect () -> back ();
}
2020-06-26 23:32:33 +00:00
public function delete ( DeleteUserRequest $request , User $user )
{
2020-07-16 20:21:28 +00:00
$this -> authorize ( 'delete' , $user );
2020-06-26 23:32:33 +00:00
if ( $request -> confirmPrompt == 'DELETE ACCOUNT' )
{
2020-10-08 18:19:10 +00:00
$user -> forceDelete ();
2020-06-26 23:32:33 +00:00
$request -> session () -> flash ( 'success' , 'User deleted successfully. PII has been erased.' );
}
else
{
$request -> session () -> flash ( 'error' , 'Wrong confirmation text! Try again.' );
}
return redirect () -> route ( 'registeredPlayerList' );
}
2020-10-08 18:19:10 +00:00
2020-06-26 23:32:33 +00:00
public function update ( UpdateUserRequest $request , User $user )
{
2020-07-16 20:21:28 +00:00
$this -> authorize ( 'adminEdit' , $user );
2020-06-26 23:32:33 +00:00
// Mass update would not be possible here without extra code, making route model binding useless
$user -> email = $request -> email ;
$user -> name = $request -> name ;
$user -> uuid = $request -> uuid ;
$existingRoles = Role :: all ()
-> pluck ( 'name' )
-> all ();
$roleDiff = array_diff ( $existingRoles , $request -> roles );
// Adds roles that were selected. Removes roles that aren't selected if the user has them.
foreach ( $roleDiff as $deselectedRole )
{
if ( $user -> hasRole ( $deselectedRole ) && $deselectedRole !== 'user' )
{
$user -> removeRole ( $deselectedRole );
}
}
foreach ( $request -> roles as $role )
{
if ( ! $user -> hasRole ( $role ))
{
$user -> assignRole ( $role );
}
}
$user -> save ();
$request -> session () -> flash ( 'success' , 'User updated successfully!' );
return redirect () -> back ();
}
2020-07-17 21:44:10 +00:00
public function add2FASecret ( Add2FASecretRequest $request )
{
$currentSecret = $request -> session () -> get ( 'current2FA' );
$isValid = Google2FA :: verifyKey ( $currentSecret , $request -> otp );
if ( $isValid )
{
$request -> user () -> twofa_secret = $currentSecret ;
$request -> user () -> save ();
Log :: warning ( 'SECURITY: User activated two-factor authentication' , [
'initiator' => $request -> user () -> email ,
'ip' => $request -> ip ()
]);
Google2FA :: login ();
Log :: warning ( 'SECURITY: Started two factor session automatically' , [
'initiator' => $request -> user () -> email ,
'ip' => $request -> ip ()
]);
$request -> session () -> forget ( 'current2FA' );
if ( $request -> session () -> has ( 'twofaAttemptFailed' ))
$request -> session () -> forget ( 'twofaAttemptFailed' );
$request -> session () -> flash ( 'success' , '2FA succesfully enabled! You\'ll now be prompted for an OTP each time you log in.' );
}
else
{
$request -> session () -> flash ( 'error' , 'Incorrect code. Please reopen the 2FA settings panel and try again.' );
$request -> session () -> put ( 'twofaAttemptFailed' , true );
}
return redirect () -> back ();
}
public function remove2FASecret ( Remove2FASecretRequest $request )
{
Log :: warning ( 'SECURITY: Disabling two factor authentication (user initiated)' , [
'initiator' => $request -> user () -> email ,
'ip' => $request -> ip ()
]);
$request -> user () -> twofa_secret = null ;
$request -> user () -> save ();
$request -> session () -> flash ( 'success' , 'Two-factor authentication disabled.' );
return redirect () -> back ();
}
2020-06-26 23:32:33 +00:00
public function terminate ( Request $request , User $user )
{
2020-06-27 18:15:33 +00:00
$this -> authorize ( 'terminate' , User :: class );
2020-06-26 23:32:33 +00:00
2020-07-17 21:44:10 +00:00
// TODO: move logic to policy
2020-06-26 23:32:33 +00:00
if ( ! $user -> isStaffMember () || $user -> is ( Auth :: user ()))
{
$request -> session () -> flash ( 'error' , 'You cannot terminate this user.' );
return redirect () -> back ();
}
foreach ( $user -> roles as $role )
{
if ( $role -> name == 'user' )
{
continue ;
}
$user -> removeRole ( $role -> name );
}
Log :: info ( 'User ' . $user -> name . ' has just been demoted.' );
$request -> session () -> flash ( 'success' , 'User terminated successfully.' );
//TODO: Dispatch event
return redirect () -> back ();
}
2020-10-08 18:19:10 +00:00
2020-05-02 23:45:29 +00:00
}