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\Exceptions\AccountNotLinkedException;
|
||||||
use App\User;
|
use App\User;
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\Client\RequestException;
|
use Illuminate\Http\Client\RequestException;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Crypt;
|
use Illuminate\Support\Facades\Crypt;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
@ -50,7 +53,7 @@ class Discord
|
|||||||
|
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
if (!is_null($this->workingGuild)) {
|
if (isset($this->workingGuild)) {
|
||||||
$this->setWorkingGuild(config('services.discord.home_guild'));
|
$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.');
|
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;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Facades\Discord;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\User;
|
use App\User;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
@ -41,21 +42,36 @@ class DiscordController extends Controller
|
|||||||
public function discordCallback() {
|
public function discordCallback() {
|
||||||
|
|
||||||
$discordUser = Socialite::driver('discord')->user();
|
$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(),
|
'uuid' => Str::uuid(),
|
||||||
'name' => $discordUser->getName(),
|
'name' => $discordUser->getName(),
|
||||||
'email' => $discordUser->getEmail(),
|
'email' => $discordUser->getEmail(),
|
||||||
'email_verified_at' => now(), // verify the account since it came from a trusted provider
|
'email_verified_at' => now(), // verify the account since it came from a trusted provider
|
||||||
'username' => $discordUser->getNickname(),
|
'username' => $discordUser->getNickname(),
|
||||||
'currentIp' => \request()->ip(),
|
'currentIp' => \request()->ip(),
|
||||||
|
'registrationIp' => request()->ip(),
|
||||||
'discord_user_id' => $discordUser->getId(),
|
'discord_user_id' => $discordUser->getId(),
|
||||||
|
'discord_pfp' => $discordUser->getAvatar(),
|
||||||
'discord_token' => $discordUser->token,
|
'discord_token' => $discordUser->token,
|
||||||
'discord_refresh_token' => $discordUser->refreshToken
|
'discord_refresh_token' => $discordUser->refreshToken
|
||||||
], [
|
|
||||||
'email' => $discordUser->getEmail()
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Auth::login(User::where('email', $discordUser->getEmail())->first());
|
Auth::login($oAuthUser, true);
|
||||||
|
}
|
||||||
|
|
||||||
return redirect()
|
return redirect()
|
||||||
->route('dashboard');
|
->route('dashboard');
|
||||||
|
@ -41,8 +41,10 @@ class UserObserver
|
|||||||
* @param \App\User $user
|
* @param \App\User $user
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function created(ProfileService $profileService, User $user)
|
public function created(User $user)
|
||||||
{
|
{
|
||||||
|
$profileService = new ProfileService();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$profileService->createProfile($user);
|
$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
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $fillable = [
|
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
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $hidden = [
|
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 = [
|
protected $casts = [
|
||||||
'email_verified_at' => 'datetime',
|
'email_verified_at' => 'datetime',
|
||||||
|
'discord_token' => 'encrypted',
|
||||||
|
'discord_refresh_token' => 'encrypted'
|
||||||
];
|
];
|
||||||
|
|
||||||
// RELATIONSHIPS
|
// RELATIONSHIPS
|
||||||
@ -101,35 +115,6 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||||||
return $this->hasMany('App\Absence', 'requesterID');
|
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
|
// UTILITY LOGIC
|
||||||
|
|
||||||
public function isVerified(): bool {
|
public function isVerified(): bool {
|
||||||
|
@ -291,6 +291,7 @@ return [
|
|||||||
\App\Providers\OptionsProvider::class,
|
\App\Providers\OptionsProvider::class,
|
||||||
App\Providers\DigitalStorageProvider::class,
|
App\Providers\DigitalStorageProvider::class,
|
||||||
App\Providers\JSONProvider::class,
|
App\Providers\JSONProvider::class,
|
||||||
|
App\Providers\DiscordOuthProvider::class,
|
||||||
NotificationChannels\Discord\DiscordServiceProvider::class,
|
NotificationChannels\Discord\DiscordServiceProvider::class,
|
||||||
|
|
||||||
],
|
],
|
||||||
@ -349,7 +350,8 @@ return [
|
|||||||
'Markdown' => GrahamCampbell\Markdown\Facades\Markdown::class,
|
'Markdown' => GrahamCampbell\Markdown\Facades\Markdown::class,
|
||||||
'ContextAwareValidator' => App\Facades\ContextAwareValidation::class,
|
'ContextAwareValidator' => App\Facades\ContextAwareValidation::class,
|
||||||
'Settings' => App\Facades\Options::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