feat: add Discord facade
This commit is contained in:
parent
d58ea51de1
commit
c793596a3a
32
app/Facades/Discord.php
Normal file
32
app/Facades/Discord.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright © 2020 Miguel Nogueira
|
||||
*
|
||||
* This file is part of Raspberry Staff Manager.
|
||||
*
|
||||
* Raspberry Staff Manager is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Raspberry Staff Manager is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Raspberry Staff Manager. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Facades;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
class Discord extends Facade
|
||||
{
|
||||
protected static function getFacadeAccessor()
|
||||
{
|
||||
return 'discordServiceFacade';
|
||||
}
|
||||
}
|
@ -24,8 +24,11 @@ namespace App\Helpers;
|
||||
|
||||
use App\Exceptions\AccountNotLinkedException;
|
||||
use App\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Client\RequestException;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
@ -50,7 +53,7 @@ class Discord
|
||||
|
||||
|
||||
public function __construct() {
|
||||
if (!is_null($this->workingGuild)) {
|
||||
if (isset($this->workingGuild)) {
|
||||
$this->setWorkingGuild(config('services.discord.home_guild'));
|
||||
}
|
||||
}
|
||||
@ -85,6 +88,47 @@ class Discord
|
||||
throw new AccountNotLinkedException('Specified website user has not linked their Discord account yet.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the current user's authentication info. Caches the data for the access_token's TTL, thus
|
||||
* preventing unnecessary API requests.
|
||||
*
|
||||
* @return object Current user's authentication info (has no sensitive fields)
|
||||
* @throws RequestException
|
||||
*/
|
||||
public function getAuthorizationInfo(): object {
|
||||
|
||||
if (Cache::has($this->user->discord_user_id)) {
|
||||
return unserialize(Cache::get($this->user->discord_user_id));
|
||||
}
|
||||
else {
|
||||
$authInfo = (object) Http::withToken($this->user->discord_token)
|
||||
->get(config('services.discord.base_url') . '/oauth2/@me')
|
||||
->throw()
|
||||
->json();
|
||||
|
||||
Cache::put($this->user->discord_user_id, serialize($authInfo), Carbon::parse($authInfo->expires));
|
||||
|
||||
return $authInfo;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the user's token is close to expiring.
|
||||
* Tokens should be refreshed the day before they expire.
|
||||
*
|
||||
* @return bool Whether the user's token needs to be refreshed
|
||||
* @throws RequestException
|
||||
*/
|
||||
public function needsRefresh(): bool {
|
||||
return Carbon::parse($this->getAuthorizationInfo()->expires)->diffInDays() == 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function exchangeRefreshToken() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Facades\Discord;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
@ -41,21 +42,36 @@ class DiscordController extends Controller
|
||||
public function discordCallback() {
|
||||
|
||||
$discordUser = Socialite::driver('discord')->user();
|
||||
$appUser = User::upsert([
|
||||
$appUser = User::where('email', $discordUser->getEmail())->first();
|
||||
|
||||
if ($appUser) {
|
||||
|
||||
$appUser->discord_token = $discordUser->token;
|
||||
$appUser->discord_refresh_token = $discordUser->refreshToken;
|
||||
$appUser->discord_user_id = $discordUser->getId();
|
||||
$appUser->discord_pfp = $discordUser->getAvatar();
|
||||
$appUser->save();
|
||||
|
||||
Auth::login($appUser, true);
|
||||
|
||||
} else {
|
||||
|
||||
$oAuthUser = User::create([
|
||||
'uuid' => Str::uuid(),
|
||||
'name' => $discordUser->getName(),
|
||||
'email' => $discordUser->getEmail(),
|
||||
'email_verified_at' => now(), // verify the account since it came from a trusted provider
|
||||
'username' => $discordUser->getNickname(),
|
||||
'currentIp' => \request()->ip(),
|
||||
'registrationIp' => request()->ip(),
|
||||
'discord_user_id' => $discordUser->getId(),
|
||||
'discord_pfp' => $discordUser->getAvatar(),
|
||||
'discord_token' => $discordUser->token,
|
||||
'discord_refresh_token' => $discordUser->refreshToken
|
||||
], [
|
||||
'email' => $discordUser->getEmail()
|
||||
]);
|
||||
|
||||
Auth::login(User::where('email', $discordUser->getEmail())->first());
|
||||
Auth::login($oAuthUser, true);
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->route('dashboard');
|
||||
|
@ -41,8 +41,10 @@ class UserObserver
|
||||
* @param \App\User $user
|
||||
* @return void
|
||||
*/
|
||||
public function created(ProfileService $profileService, User $user)
|
||||
public function created(User $user)
|
||||
{
|
||||
$profileService = new ProfileService();
|
||||
|
||||
try
|
||||
{
|
||||
$profileService->createProfile($user);
|
||||
|
31
app/Providers/DiscordOuthProvider.php
Normal file
31
app/Providers/DiscordOuthProvider.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Helpers\Discord;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class DiscordOuthProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
\App::bind('discordServiceFacade', function () {
|
||||
return new Discord();
|
||||
});
|
||||
}
|
||||
}
|
47
app/User.php
47
app/User.php
@ -42,7 +42,19 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name', 'email', 'password', 'originalIP', 'username', 'uuid', 'dob',
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
'originalIP',
|
||||
'registrationIp',
|
||||
'username',
|
||||
'uuid',
|
||||
'dob',
|
||||
'email_verified_at',
|
||||
'currentIp',
|
||||
'discord_user_id',
|
||||
'discord_token',
|
||||
'discord_refresh_token'
|
||||
];
|
||||
|
||||
/**
|
||||
@ -51,7 +63,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
* @var array
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password', 'remember_token',
|
||||
'password', 'remember_token', 'discord_token', 'discord_refresh_token'
|
||||
];
|
||||
|
||||
/**
|
||||
@ -61,6 +73,8 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
*/
|
||||
protected $casts = [
|
||||
'email_verified_at' => 'datetime',
|
||||
'discord_token' => 'encrypted',
|
||||
'discord_refresh_token' => 'encrypted'
|
||||
];
|
||||
|
||||
// RELATIONSHIPS
|
||||
@ -101,35 +115,6 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
return $this->hasMany('App\Absence', 'requesterID');
|
||||
}
|
||||
|
||||
|
||||
// ACCESSORS AND MUTATORS
|
||||
|
||||
/**
|
||||
* Encrypt/decrypt the Discord access token. Data is encrypted at rest.
|
||||
*
|
||||
* @return Attribute
|
||||
*/
|
||||
public function discordToken(): Attribute {
|
||||
return Attribute::make(
|
||||
get: fn ($value) => Crypt::decryptString($value),
|
||||
set: fn ($value) => Crypt::encryptString($value)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt/decrypt the Discord refresh token. Data is encrypted at rest.
|
||||
*
|
||||
* @return Attribute
|
||||
*/
|
||||
public function discordRefreshToken(): Attribute {
|
||||
return Attribute::make(
|
||||
get: fn ($value) => Crypt::decryptString($value),
|
||||
set: fn ($value) => Crypt::encryptString($value)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// UTILITY LOGIC
|
||||
|
||||
public function isVerified(): bool {
|
||||
|
@ -291,6 +291,7 @@ return [
|
||||
\App\Providers\OptionsProvider::class,
|
||||
App\Providers\DigitalStorageProvider::class,
|
||||
App\Providers\JSONProvider::class,
|
||||
App\Providers\DiscordOuthProvider::class,
|
||||
NotificationChannels\Discord\DiscordServiceProvider::class,
|
||||
|
||||
],
|
||||
@ -349,7 +350,8 @@ return [
|
||||
'Markdown' => GrahamCampbell\Markdown\Facades\Markdown::class,
|
||||
'ContextAwareValidator' => App\Facades\ContextAwareValidation::class,
|
||||
'Settings' => App\Facades\Options::class,
|
||||
'JSON' => App\Facades\JSON::class
|
||||
'JSON' => App\Facades\JSON::class,
|
||||
'DiscordOauth' => App\Facades\Discord::class
|
||||
|
||||
],
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddIntProfilePicToUsers extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->longText('discord_pfp')->after('discord_user_id')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('discord_pfp');
|
||||
});
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user