Skip to content

Commit

Permalink
Switching accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
kreut committed Oct 18, 2024
1 parent e427780 commit 77589f3
Show file tree
Hide file tree
Showing 28 changed files with 1,029 additions and 13 deletions.
58 changes: 58 additions & 0 deletions app/Console/Commands/Cleanup/removeOldValidationCodes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace App\Console\Commands\Cleanup;

use App\Exceptions\Handler;
use App\InstructorAccessCode;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

class removeOldValidationCodes extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'remove:oldValidationCodes';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Removes codes older than 10 minutes';

/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}

/**
* Execute the console command.
*
* @return int
* @throws Exception
*/
public function handle(): int
{
try {
DB::table('link_to_account_validation_codes')
->where('created_at', '<=', Carbon::now()->subMinutes(10)->toDateTimeString())
->delete();

} catch (Exception $e) {
$h = new Handler(app());
$h->report($e);
echo $e->getMessage();
return 1;
}
}
}
1 change: 1 addition & 0 deletions app/Console/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ protected function schedule(Schedule $schedule)
/* end grader notifications */
$schedule->command('check:AssignTos')->twiceDaily();
$schedule->command('remove:oldAccessCodes')->daily();
$schedule->command('remove:oldValidationCodes')->everyMinute();
$schedule->command('find:accents')->daily();

$schedule->command('find:richTextError')->twiceDaily();
Expand Down
39 changes: 39 additions & 0 deletions app/Helpers/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,45 @@ static function getCompletionScoringMode($scoring_type, $completion_scoring_mode
} else return null;
}

public static function getLinkedAccounts(int $user_id)
{
$user = User::find($user_id);
$linked_accounts = [];

$linked_from_user = DB::table('linked_accounts')
->join('users', 'linked_accounts.linked_to_user_id', '=', 'users.id')
->select('linked_accounts.user_id')
->where('linked_to_user_id', $user->id)
->first();
if ($linked_from_user) {
$linked_from_user_id = $linked_from_user->user_id;
} else {
$linked_from_user = DB::table('linked_accounts')->where('user_id', $user_id)->first();
if (!$linked_from_user) {
return json_encode([]);
} else {
$linked_from_user_id = $linked_from_user->user_id;
}
}

$linked_from_user = User::find($linked_from_user_id);
$linked_to_users = DB::table('linked_accounts')
->join('users', 'linked_accounts.linked_to_user_id', '=', 'users.id')
->where('user_id', $linked_from_user_id)
->select('users.*')
->get();
$linked_accounts[] = [
'id' => $linked_from_user->id,
'email' => $linked_from_user->email,
'main_account' => true];
foreach ($linked_to_users as $linked_to_user) {
$linked_accounts[] = ['id' => $linked_to_user->id,
'email' => $linked_to_user->email,
'main_account' => false];
}
return json_encode($linked_accounts);
}

public
static function createAccessCode($length = 12)
{
Expand Down
4 changes: 4 additions & 0 deletions app/Http/Controllers/Auth/LoginController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Exceptions\Handler;
use App\Exceptions\VerifyEmailException;
use App\FCMToken;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Question;
use App\User;
Expand Down Expand Up @@ -58,10 +59,13 @@ protected function attemptLogin(Request $request): bool
}

$user = $this->guard()->user();
$linked_accounts = Helper::getLinkedAccounts($user->id);
session()->put('linked_accounts', $linked_accounts);
session()->forget('original_user_id');
session()->forget('admin_user_id');
session()->put('original_role', $user->role);
session()->put('original_email', $user->email);
$user->linked_accounts = $linked_accounts;
$user->instructor_user_id = null;
DB::table('users')->where('instructor_user_id', $user->id)->update(['instructor_user_id' => null]);

Expand Down
2 changes: 2 additions & 0 deletions app/Http/Controllers/Auth/OAuthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Http\Controllers\Auth;

use App\Exceptions\EmailTakenException;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\OAuthProvider;
use App\User;
Expand Down Expand Up @@ -54,6 +55,7 @@ public function handleProviderCallback($provider)
$user = Socialite::driver($provider)->stateless()->user();
$user = $this->findOrCreateUser($provider, $user);

session()->put('linked_accounts', Helper::getLinkedAccounts($user->id));
$this->guard()->setToken(
$token = $this->guard()->login($user)
);
Expand Down
14 changes: 12 additions & 2 deletions app/Http/Controllers/Auth/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,17 @@
use MiladRahimi\Jwt\Generator;
use MiladRahimi\Jwt\Parser;
use MiladRahimi\Jwt\Cryptography\Algorithms\Hmac\HS256;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;

class UserController extends Controller
{

/**
* @param Request $request
* @return JsonResponse
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function current(Request $request): JsonResponse
{
Expand All @@ -41,6 +45,7 @@ public function current(Request $request): JsonResponse
if ($request->user()->is_tester_student) {
$request->user()->email = '';
}
$request->user()->linked_accounts = session()->get('linked_accounts');
$request->user()->is_developer = $request->user()->isDeveloper();
$request->user()->logged_in_as_user = session()->get('original_user_id');
$request->user()->is_instructor_logged_in_as_student = is_numeric(session()->get('original_user_id')) || $request->user()->fake_student;
Expand Down Expand Up @@ -341,6 +346,9 @@ public function loginAs(LoginAsRequest $request, User $user): array
session()->put(['admin_user_id' => $request->user()->id]);
}
session()->put(['original_user_id' => $request->user()->id]);
$linked_accounts = Helper::getLinkedAccounts($new_user->id);
session()->put('linked_accounts', $linked_accounts);
$new_user->linked_accounts = $linked_accounts;
$response['type'] = 'success';
$response['token'] = \JWTAuth::fromUser($new_user);
} catch (Exception $e) {
Expand Down Expand Up @@ -373,6 +381,9 @@ public function exitLoginAs()
$new_user = User::find($original_user_id);
session()->forget('original_user_id');
session()->forget('admin_user_id');
$linked_accounts = Helper::getLinkedAccounts($new_user->id);
session()->put('linked_accounts', $linked_accounts);
$new_user->linked_accounts = $linked_accounts;
DB::beginTransaction();
$response['type'] = 'success';
$response['token'] = \JWTAuth::fromUser($new_user);
Expand Down Expand Up @@ -455,8 +466,7 @@ public function getAuthenticatedUser(Request $request)
return response()->json(['token_absent'], $e->getStatusCode());

}
Log::info(\JWTAuth::parseToken()->getPayload() . "\r\n");
Log::info($request->all());

// the token is valid and we have found the user via the sub claim
return [\JWTAuth::parseToken()->getPayload(), $request->all()];
}
Expand Down
1 change: 1 addition & 0 deletions app/Http/Controllers/BreadcrumbController.php
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ public function index(Request $request)
case('settings.password'):
case('settings.notifications'):
case('settings.account_customizations'):
case('settings.linked_accounts'):
$breadcrumbs[] = ['text' => 'Settings',
'href' => "#",
'active' => true];
Expand Down
163 changes: 163 additions & 0 deletions app/Http/Controllers/LinkedAccountController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php

namespace App\Http\Controllers;

use App\Exceptions\Handler;
use App\Helpers\Helper;
use App\Http\Requests\AccountValidationCodeRequest;
use App\Http\Requests\EmailLinkToAccountRequest;
use App\LinkedAccount;
use App\LinkToAccountValidationCode;
use App\User;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Snowfire\Beautymail\Beautymail;

class LinkedAccountController extends Controller
{
/**
* @param Request $request
* @param User $account_to_switch_to
* @return array
*/
public function switch(Request $request, User $account_to_switch_to): array
{

$response['type'] = 'error';
$linked_accounts = json_decode(Helper::getLinkedAccounts($request->user()->id), 1);
$can_switch = false;
foreach ($linked_accounts as $linked_account) {
if ($linked_account['id'] === $account_to_switch_to->id) {
$can_switch = true;
}
}
if (!$can_switch) {
$response['message'] = "You cannot switch to that account.";
return $response;
}

$response['type'] = 'success';
$response['token'] = \JWTAuth::fromUser($account_to_switch_to);
return $response;

}

/**
* @param Request $request
* @param LinkedAccount $linkedAccount
* @param User $account_to_unlink
* @return array
* @throws Exception
*/
public function unlink(Request $request, LinkedAccount $linkedAccount, User $account_to_unlink): array
{

try {
$response['type'] = 'error';
$authorized = Gate::inspect('unlink', [$linkedAccount, $account_to_unlink]);
if (!$authorized->allowed()) {
$response['message'] = $authorized->message();
return $response;
}
$linkedAccount->where('user_id', $request->user()->id)
->where('linked_to_user_id', $account_to_unlink->id)
->delete();
Helper::getLinkedAccounts($request->user()->id);
session()->put('linked_accounts', Helper::getLinkedAccounts($request->user()->id));
$response['type'] = 'info';
$response['message'] = "$account_to_unlink->email is no longer linked.";
} catch (Exception $e) {
DB::rollback();
$h = new Handler(app());
$h->report($e);
$response['message'] = "We were unable to unlink your account. Please try again or contact us for assistance.";
}
return $response;
}


/**
* @param AccountValidationCodeRequest $request
* @param LinkToAccountValidationCode $linkToAccountValidationCode
* @return array
* @throws Exception
*/
public function validateCodeToLinkToAccount(AccountValidationCodeRequest $request,
LinkToAccountValidationCode $linkToAccountValidationCode): array
{
try {
$response['type'] = 'error';
DB::beginTransaction();
$data = $request->validated();
$linked_account_validation_code = $linkToAccountValidationCode
->where('validation_code', $data['validation_code'])
->first();
$linked_account = User::where('email', $linked_account_validation_code->email)->first();
$linkToAccountValidationCode->where('validation_code', $data['validation_code'])->delete();
$linkedAccount = new LinkedAccount();
$linkedAccount->user_id = $request->user()->id;
$linkedAccount->linked_to_user_id = $linked_account->id;
$linkedAccount->save();
session()->put('linked_accounts', Helper::getLinkedAccounts($request->user()->id));
DB::commit();
$response['type'] = 'success';
$response['message'] = "The accounts have been linked.";

} catch (Exception $e) {
DB::rollback();
$h = new Handler(app());
$h->report($e);
$response['message'] = "We were unable to validate the code. Please try again or contact us for assistance.";
}
return $response;
}


/**
* @param EmailLinkToAccountRequest $request
* @return array
* @throws Exception
*/
public function emailLinkToAccountValidationCode(EmailLinkToAccountRequest $request): array
{

try {
$response['type'] = 'error';
$email = trim($request->email);
$account_to_link_to = User::where('email', $email)->first();
$data = $request->validated();
$email = $data['email'];
if ($account_to_link_to) {
if ($account_to_link_to->id === $request->user()->id) {
$response['message'] = "You are trying to link to your current account.";
return $response;
}
$validation_code = Helper::createAccessCode(15);

LinkToAccountValidationCode::updateOrCreate(['email' => $email], ['validation_code' => $validation_code]);

$mail_info = [
'first_name' => $request->user()->first_name,
'validation_code' => $validation_code
];
$beautymail = app()->make(Beautymail::class);
$beautymail->send('emails.link_to_account_validation_code', $mail_info, function ($message)
use ($account_to_link_to) {
$message->from('adapt@noreply.libretexts.org', 'ADAPT')
->to($account_to_link_to->email, $account_to_link_to->first_name . ' ' . $account_to_link_to->last_name)
->subject('Validation Code to Link Accounts');
});
}
$response['type'] = 'success';
$response['message'] = "Please check your email for a validation code.";
} catch (Exception $e) {
$h = new Handler(app());
$h->report($e);
$response['message'] = "There was an error sending the email. Please try again or contact us for assistance.";
}
return $response;

}
}
Loading

0 comments on commit 77589f3

Please sign in to comment.