mirror of
https://github.com/Bubka/2FAuth.git
synced 2025-04-10 18:48:17 +02:00
Add Logo fetching service - Close #99
This commit is contained in:
parent
64da81b5a7
commit
bf32b37176
@ -3,6 +3,7 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use App\Services\LogoService;
|
||||||
use App\Models\Dto\TotpDto;
|
use App\Models\Dto\TotpDto;
|
||||||
use App\Models\Dto\HotpDto;
|
use App\Models\Dto\HotpDto;
|
||||||
use App\Events\TwoFAccountDeleted;
|
use App\Events\TwoFAccountDeleted;
|
||||||
@ -26,6 +27,7 @@
|
|||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use ParagonIE\ConstantTime\Base32;
|
use ParagonIE\ConstantTime\Base32;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
|
||||||
class TwoFAccount extends Model implements Sortable
|
class TwoFAccount extends Model implements Sortable
|
||||||
{
|
{
|
||||||
@ -434,9 +436,12 @@ public function fillWithURI(string $uri, bool $isSteamTotp = false)
|
|||||||
if ($isSteamTotp || strtolower($this->service) === 'steam') {
|
if ($isSteamTotp || strtolower($this->service) === 'steam') {
|
||||||
$this->enforceAsSteam();
|
$this->enforceAsSteam();
|
||||||
}
|
}
|
||||||
else if ($this->generator->hasParameter('image')) {
|
if ($this->generator->hasParameter('image')) {
|
||||||
$this->icon = $this->storeImageAsIcon($this->generator->getParameter('image'));
|
$this->icon = $this->storeImageAsIcon($this->generator->getParameter('image'));
|
||||||
}
|
}
|
||||||
|
if (!$this->icon) {
|
||||||
|
$this->icon = $this->defaultLogo();
|
||||||
|
}
|
||||||
|
|
||||||
Log::info(sprintf('TwoFAccount filled with an URI'));
|
Log::info(sprintf('TwoFAccount filled with an URI'));
|
||||||
|
|
||||||
@ -453,9 +458,6 @@ private function enforceAsSteam()
|
|||||||
$this->digits = 5;
|
$this->digits = 5;
|
||||||
$this->algorithm = self::SHA1;
|
$this->algorithm = self::SHA1;
|
||||||
$this->period = 30;
|
$this->period = 30;
|
||||||
// if (!$this->icon) {
|
|
||||||
// $this->icon = $this->storeImageAsIcon('https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/Steam_icon_logo.svg/langfr-320px-Steam_icon_logo.svg.png');
|
|
||||||
// }
|
|
||||||
|
|
||||||
Log::info(sprintf('TwoFAccount configured as Steam account'));
|
Log::info(sprintf('TwoFAccount configured as Steam account'));
|
||||||
}
|
}
|
||||||
@ -567,6 +569,24 @@ private function storeImageAsIcon(string $url)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a logo in the tfa directory and store it as a new stand alone icon
|
||||||
|
*
|
||||||
|
* @return string|null The icon
|
||||||
|
*/
|
||||||
|
private function defaultLogo()
|
||||||
|
{
|
||||||
|
$logoService = App::make(LogoService::class);
|
||||||
|
$logoFilename = $logoService->getLogo($this->service);
|
||||||
|
|
||||||
|
if ($logoFilename) {
|
||||||
|
$newFilename = Str::random(40).'.svg';
|
||||||
|
return Storage::disk('icons')->put($newFilename, Storage::disk('logos')->get($logoFilename)) ? $newFilename : null;
|
||||||
|
}
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an acceptable value
|
* Returns an acceptable value
|
||||||
*/
|
*/
|
||||||
|
43
app/Providers/TwoFAuthServiceProvider.php
Normal file
43
app/Providers/TwoFAuthServiceProvider.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Services\LogoService;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Illuminate\Contracts\Support\DeferrableProvider;
|
||||||
|
|
||||||
|
class TwoFAuthServiceProvider extends ServiceProvider implements DeferrableProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Register services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
$this->app->singleton(LogoService::class, function ($app) {
|
||||||
|
return new LogoService();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bootstrap services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the services provided by the provider.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function provides()
|
||||||
|
{
|
||||||
|
return [LogoService::class];
|
||||||
|
}
|
||||||
|
}
|
121
app/Services/LogoService.php
Normal file
121
app/Services/LogoService.php
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class LogoService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \Illuminate\Support\Collection
|
||||||
|
*/
|
||||||
|
protected $tfas;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const TFA_JSON = 'tfa.json';
|
||||||
|
const TFA_URL = 'https://2fa.directory/api/v3/tfa.json';
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->setTfaCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the logo's filename for a given service
|
||||||
|
*
|
||||||
|
* @param string $serviceName Name of the service to fetch a logo for
|
||||||
|
* @return string|null The logo filename or null if no logo has been found
|
||||||
|
*/
|
||||||
|
public function getLogo(string $serviceName) : string
|
||||||
|
{
|
||||||
|
$domain = $this->tfas->get(strtolower($serviceName));
|
||||||
|
$logoFilename = $domain.'.svg';
|
||||||
|
|
||||||
|
if ($domain && !Storage::disk('logos')->exists($logoFilename)) {
|
||||||
|
$this->fetchLogo($logoFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Storage::disk('logos')->exists($logoFilename) ? $logoFilename : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build and set the TFA directoy collection
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function setTfaCollection() : void
|
||||||
|
{
|
||||||
|
// We fetch a fresh tfaDirectory if necessary to prevent too many API calls
|
||||||
|
if (Storage::disk('logos')->exists(self::TFA_JSON)) {
|
||||||
|
if (time() - Storage::disk('logos')->lastModified(self::TFA_JSON) > 86400) {
|
||||||
|
$this->cacheTfaDirectorySource();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->cacheTfaDirectorySource();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->tfas = Storage::disk('logos')->exists(self::TFA_JSON)
|
||||||
|
? collect(json_decode(Storage::disk('logos')->get(self::TFA_JSON)))
|
||||||
|
: collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch and cache fresh TFA.Directory data using the https://2fa.directory API
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function cacheTfaDirectorySource() : void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$response = Http::retry(3, 100)->get(self::TFA_URL);
|
||||||
|
|
||||||
|
$coll = collect(json_decode(htmlspecialchars_decode($response->body()), true))
|
||||||
|
->mapWithKeys(function ($item, $key) {
|
||||||
|
return [
|
||||||
|
strtolower(head($item)) => $item[1]["domain"]
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
Storage::disk('logos')->put(self::TFA_JSON, $coll->toJson())
|
||||||
|
? Log::info('Fresh tfa.json saved to logos dir')
|
||||||
|
: Log::notice('Cannot save tfa.json to logos dir');
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (\Exception $e) {
|
||||||
|
Log::error('Caching of tfa.json failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a logo and store it to the disk
|
||||||
|
*
|
||||||
|
* @param string $logoFile Logo filename to fetch
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function fetchLogo(string $logoFile) : void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$response = Http::retry(3, 100)
|
||||||
|
->get('https://raw.githubusercontent.com/2factorauth/twofactorauth/master/img/'.$logoFile[0].'/'.$logoFile);
|
||||||
|
|
||||||
|
if ($response->successful()) {
|
||||||
|
Storage::disk('logos')->put($logoFile, $response->body())
|
||||||
|
? Log::info(sprintf('Logo "%s" saved to logos dir.', $logoFile))
|
||||||
|
: Log::notice(sprintf('Cannot save logo "%s" to logos dir', $logoFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (\Exception $exception) {
|
||||||
|
Log::error(sprintf('Fetching of logo "%s" failed.', $logoFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -161,6 +161,7 @@
|
|||||||
Illuminate\Translation\TranslationServiceProvider::class,
|
Illuminate\Translation\TranslationServiceProvider::class,
|
||||||
Illuminate\Validation\ValidationServiceProvider::class,
|
Illuminate\Validation\ValidationServiceProvider::class,
|
||||||
Illuminate\View\ViewServiceProvider::class,
|
Illuminate\View\ViewServiceProvider::class,
|
||||||
|
App\Providers\TwoFAuthServiceProvider::class,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Package Service Providers...
|
* Package Service Providers...
|
||||||
|
@ -48,6 +48,23 @@
|
|||||||
'root' => storage_path('app'),
|
'root' => storage_path('app'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'icons' => [
|
||||||
|
'driver' => 'local',
|
||||||
|
'root' => storage_path('app/public/icons'),
|
||||||
|
'url' => env('APP_URL').'/storage/icons',
|
||||||
|
'visibility' => 'public',
|
||||||
|
],
|
||||||
|
|
||||||
|
'logos' => [
|
||||||
|
'driver' => 'local',
|
||||||
|
'root' => storage_path('app/logos'),
|
||||||
|
],
|
||||||
|
|
||||||
|
'imagesLink' => [
|
||||||
|
'driver' => 'local',
|
||||||
|
'root' => storage_path('app/imagesLink'),
|
||||||
|
],
|
||||||
|
|
||||||
'public' => [
|
'public' => [
|
||||||
'driver' => 'local',
|
'driver' => 'local',
|
||||||
'root' => storage_path('app/public'),
|
'root' => storage_path('app/public'),
|
||||||
|
Loading…
Reference in New Issue
Block a user