mirror of
https://github.com/usetrmnl/byos_laravel.git
synced 2026-01-13 15:07:49 +00:00
chore: pint
This commit is contained in:
parent
25f36eaf54
commit
4c66761baa
12 changed files with 142 additions and 121 deletions
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
use InvalidArgumentException;
|
||||||
use Laravel\Socialite\Facades\Socialite;
|
use Laravel\Socialite\Facades\Socialite;
|
||||||
|
|
||||||
class OidcTestCommand extends Command
|
class OidcTestCommand extends Command
|
||||||
|
|
@ -31,7 +33,7 @@ class OidcTestCommand extends Command
|
||||||
|
|
||||||
// Check if OIDC is enabled
|
// Check if OIDC is enabled
|
||||||
$enabled = config('services.oidc.enabled');
|
$enabled = config('services.oidc.enabled');
|
||||||
$this->line("OIDC Enabled: " . ($enabled ? '✅ Yes' : '❌ No'));
|
$this->line('OIDC Enabled: '.($enabled ? '✅ Yes' : '❌ No'));
|
||||||
|
|
||||||
// Check configuration values
|
// Check configuration values
|
||||||
$endpoint = config('services.oidc.endpoint');
|
$endpoint = config('services.oidc.endpoint');
|
||||||
|
|
@ -40,11 +42,11 @@ class OidcTestCommand extends Command
|
||||||
$redirect = config('services.oidc.redirect');
|
$redirect = config('services.oidc.redirect');
|
||||||
$scopes = config('services.oidc.scopes', []);
|
$scopes = config('services.oidc.scopes', []);
|
||||||
|
|
||||||
$this->line("OIDC Endpoint: " . ($endpoint ? "✅ {$endpoint}" : '❌ Not set'));
|
$this->line('OIDC Endpoint: '.($endpoint ? "✅ {$endpoint}" : '❌ Not set'));
|
||||||
$this->line("Client ID: " . ($clientId ? "✅ {$clientId}" : '❌ Not set'));
|
$this->line('Client ID: '.($clientId ? "✅ {$clientId}" : '❌ Not set'));
|
||||||
$this->line("Client Secret: " . ($clientSecret ? '✅ Set' : '❌ Not set'));
|
$this->line('Client Secret: '.($clientSecret ? '✅ Set' : '❌ Not set'));
|
||||||
$this->line("Redirect URL: " . ($redirect ? "✅ {$redirect}" : '❌ Not set'));
|
$this->line('Redirect URL: '.($redirect ? "✅ {$redirect}" : '❌ Not set'));
|
||||||
$this->line("Scopes: " . (empty($scopes) ? '❌ Not set' : '✅ ' . implode(', ', $scopes)));
|
$this->line('Scopes: '.(empty($scopes) ? '❌ Not set' : '✅ '.implode(', ', $scopes)));
|
||||||
|
|
||||||
$this->newLine();
|
$this->newLine();
|
||||||
|
|
||||||
|
|
@ -53,38 +55,45 @@ class OidcTestCommand extends Command
|
||||||
// Only test driver if we have basic configuration
|
// Only test driver if we have basic configuration
|
||||||
if ($endpoint && $clientId && $clientSecret) {
|
if ($endpoint && $clientId && $clientSecret) {
|
||||||
$driver = Socialite::driver('oidc');
|
$driver = Socialite::driver('oidc');
|
||||||
$this->line("OIDC Driver: ✅ Successfully registered and accessible");
|
$this->line('OIDC Driver: ✅ Successfully registered and accessible');
|
||||||
|
|
||||||
if ($enabled) {
|
if ($enabled) {
|
||||||
$this->info("✅ OIDC is fully configured and ready to use!");
|
$this->info('✅ OIDC is fully configured and ready to use!');
|
||||||
$this->line("You can test the login flow at: /auth/oidc/redirect");
|
$this->line('You can test the login flow at: /auth/oidc/redirect');
|
||||||
} else {
|
} else {
|
||||||
$this->warn("⚠️ OIDC driver is working but OIDC_ENABLED is false.");
|
$this->warn('⚠️ OIDC driver is working but OIDC_ENABLED is false.');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->line("OIDC Driver: ✅ Registered (configuration test skipped due to missing values)");
|
$this->line('OIDC Driver: ✅ Registered (configuration test skipped due to missing values)');
|
||||||
$this->warn("⚠️ OIDC driver is registered but missing required configuration.");
|
$this->warn('⚠️ OIDC driver is registered but missing required configuration.');
|
||||||
$this->line("Please set the following environment variables:");
|
$this->line('Please set the following environment variables:');
|
||||||
if (!$enabled) $this->line(" - OIDC_ENABLED=true");
|
if (! $enabled) {
|
||||||
if (!$endpoint) {
|
$this->line(' - OIDC_ENABLED=true');
|
||||||
$this->line(" - OIDC_ENDPOINT=https://your-oidc-provider.com (base URL)");
|
}
|
||||||
$this->line(" OR");
|
if (! $endpoint) {
|
||||||
$this->line(" - OIDC_ENDPOINT=https://your-oidc-provider.com/.well-known/openid-configuration (full URL)");
|
$this->line(' - OIDC_ENDPOINT=https://your-oidc-provider.com (base URL)');
|
||||||
|
$this->line(' OR');
|
||||||
|
$this->line(' - OIDC_ENDPOINT=https://your-oidc-provider.com/.well-known/openid-configuration (full URL)');
|
||||||
|
}
|
||||||
|
if (! $clientId) {
|
||||||
|
$this->line(' - OIDC_CLIENT_ID=your-client-id');
|
||||||
|
}
|
||||||
|
if (! $clientSecret) {
|
||||||
|
$this->line(' - OIDC_CLIENT_SECRET=your-client-secret');
|
||||||
}
|
}
|
||||||
if (!$clientId) $this->line(" - OIDC_CLIENT_ID=your-client-id");
|
|
||||||
if (!$clientSecret) $this->line(" - OIDC_CLIENT_SECRET=your-client-secret");
|
|
||||||
}
|
}
|
||||||
} catch (\InvalidArgumentException $e) {
|
} catch (InvalidArgumentException $e) {
|
||||||
if (str_contains($e->getMessage(), 'Driver [oidc] not supported')) {
|
if (str_contains($e->getMessage(), 'Driver [oidc] not supported')) {
|
||||||
$this->error("❌ OIDC Driver registration failed: Driver not supported");
|
$this->error('❌ OIDC Driver registration failed: Driver not supported');
|
||||||
} else {
|
} else {
|
||||||
$this->error("❌ OIDC Driver error: " . $e->getMessage());
|
$this->error('❌ OIDC Driver error: '.$e->getMessage());
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
$this->warn("⚠️ OIDC Driver registered but configuration error: " . $e->getMessage());
|
$this->warn('⚠️ OIDC Driver registered but configuration error: '.$e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->newLine();
|
$this->newLine();
|
||||||
|
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use Exception;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
@ -17,23 +18,25 @@ class OidcController extends Controller
|
||||||
*/
|
*/
|
||||||
public function redirect()
|
public function redirect()
|
||||||
{
|
{
|
||||||
if (!config('services.oidc.enabled')) {
|
if (! config('services.oidc.enabled')) {
|
||||||
return redirect()->route('login')->withErrors(['oidc' => 'OIDC authentication is not enabled.']);
|
return redirect()->route('login')->withErrors(['oidc' => 'OIDC authentication is not enabled.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if all required OIDC configuration is present
|
// Check if all required OIDC configuration is present
|
||||||
$requiredConfig = ['endpoint', 'client_id', 'client_secret'];
|
$requiredConfig = ['endpoint', 'client_id', 'client_secret'];
|
||||||
foreach ($requiredConfig as $key) {
|
foreach ($requiredConfig as $key) {
|
||||||
if (!config("services.oidc.{$key}")) {
|
if (! config("services.oidc.{$key}")) {
|
||||||
Log::error("OIDC configuration missing: {$key}");
|
Log::error("OIDC configuration missing: {$key}");
|
||||||
|
|
||||||
return redirect()->route('login')->withErrors(['oidc' => 'OIDC is not properly configured.']);
|
return redirect()->route('login')->withErrors(['oidc' => 'OIDC is not properly configured.']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return Socialite::driver('oidc')->redirect();
|
return Socialite::driver('oidc')->redirect();
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
Log::error('OIDC redirect error: ' . $e->getMessage());
|
Log::error('OIDC redirect error: '.$e->getMessage());
|
||||||
|
|
||||||
return redirect()->route('login')->withErrors(['oidc' => 'Failed to initiate OIDC authentication.']);
|
return redirect()->route('login')->withErrors(['oidc' => 'Failed to initiate OIDC authentication.']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -43,32 +46,34 @@ class OidcController extends Controller
|
||||||
*/
|
*/
|
||||||
public function callback(Request $request)
|
public function callback(Request $request)
|
||||||
{
|
{
|
||||||
if (!config('services.oidc.enabled')) {
|
if (! config('services.oidc.enabled')) {
|
||||||
return redirect()->route('login')->withErrors(['oidc' => 'OIDC authentication is not enabled.']);
|
return redirect()->route('login')->withErrors(['oidc' => 'OIDC authentication is not enabled.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if all required OIDC configuration is present
|
// Check if all required OIDC configuration is present
|
||||||
$requiredConfig = ['endpoint', 'client_id', 'client_secret'];
|
$requiredConfig = ['endpoint', 'client_id', 'client_secret'];
|
||||||
foreach ($requiredConfig as $key) {
|
foreach ($requiredConfig as $key) {
|
||||||
if (!config("services.oidc.{$key}")) {
|
if (! config("services.oidc.{$key}")) {
|
||||||
Log::error("OIDC configuration missing: {$key}");
|
Log::error("OIDC configuration missing: {$key}");
|
||||||
|
|
||||||
return redirect()->route('login')->withErrors(['oidc' => 'OIDC is not properly configured.']);
|
return redirect()->route('login')->withErrors(['oidc' => 'OIDC is not properly configured.']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$oidcUser = Socialite::driver('oidc')->user();
|
$oidcUser = Socialite::driver('oidc')->user();
|
||||||
|
|
||||||
// Find or create the user
|
// Find or create the user
|
||||||
$user = $this->findOrCreateUser($oidcUser);
|
$user = $this->findOrCreateUser($oidcUser);
|
||||||
|
|
||||||
// Log the user in
|
// Log the user in
|
||||||
Auth::login($user, true);
|
Auth::login($user, true);
|
||||||
|
|
||||||
return redirect()->intended(route('dashboard', absolute: false));
|
return redirect()->intended(route('dashboard', absolute: false));
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
Log::error('OIDC callback error: ' . $e->getMessage());
|
Log::error('OIDC callback error: '.$e->getMessage());
|
||||||
|
|
||||||
return redirect()->route('login')->withErrors(['oidc' => 'Failed to authenticate with OIDC provider.']);
|
return redirect()->route('login')->withErrors(['oidc' => 'Failed to authenticate with OIDC provider.']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -80,26 +85,28 @@ class OidcController extends Controller
|
||||||
{
|
{
|
||||||
// First, try to find user by OIDC subject ID
|
// First, try to find user by OIDC subject ID
|
||||||
$user = User::where('oidc_sub', $oidcUser->getId())->first();
|
$user = User::where('oidc_sub', $oidcUser->getId())->first();
|
||||||
|
|
||||||
if ($user) {
|
if ($user) {
|
||||||
// Update user information from OIDC
|
// Update user information from OIDC
|
||||||
$user->update([
|
$user->update([
|
||||||
'name' => $oidcUser->getName() ?: $user->name,
|
'name' => $oidcUser->getName() ?: $user->name,
|
||||||
'email' => $oidcUser->getEmail() ?: $user->email,
|
'email' => $oidcUser->getEmail() ?: $user->email,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not found by OIDC sub, try to find by email
|
// If not found by OIDC sub, try to find by email
|
||||||
if ($oidcUser->getEmail()) {
|
if ($oidcUser->getEmail()) {
|
||||||
$user = User::where('email', $oidcUser->getEmail())->first();
|
$user = User::where('email', $oidcUser->getEmail())->first();
|
||||||
|
|
||||||
if ($user) {
|
if ($user) {
|
||||||
// Link the existing user with OIDC
|
// Link the existing user with OIDC
|
||||||
$user->update([
|
$user->update([
|
||||||
'oidc_sub' => $oidcUser->getId(),
|
'oidc_sub' => $oidcUser->getId(),
|
||||||
'name' => $oidcUser->getName() ?: $user->name,
|
'name' => $oidcUser->getName() ?: $user->name,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,9 +115,9 @@ class OidcController extends Controller
|
||||||
return User::create([
|
return User::create([
|
||||||
'oidc_sub' => $oidcUser->getId(),
|
'oidc_sub' => $oidcUser->getId(),
|
||||||
'name' => $oidcUser->getName() ?: 'OIDC User',
|
'name' => $oidcUser->getName() ?: 'OIDC User',
|
||||||
'email' => $oidcUser->getEmail() ?: $oidcUser->getId() . '@oidc.local',
|
'email' => $oidcUser->getEmail() ?: $oidcUser->getId().'@oidc.local',
|
||||||
'password' => bcrypt(Str::random(32)), // Random password since we're using OIDC
|
'password' => bcrypt(Str::random(32)), // Random password since we're using OIDC
|
||||||
'email_verified_at' => now(), // OIDC users are considered verified
|
'email_verified_at' => now(), // OIDC users are considered verified
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Liquid\FileSystems;
|
namespace App\Liquid\FileSystems;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
use Keepsuit\Liquid\Contracts\LiquidFileSystem;
|
use Keepsuit\Liquid\Contracts\LiquidFileSystem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -52,10 +53,10 @@ class InlineTemplatesFileSystem implements LiquidFileSystem
|
||||||
|
|
||||||
public function readTemplateFile(string $templateName): string
|
public function readTemplateFile(string $templateName): string
|
||||||
{
|
{
|
||||||
if (!isset($this->templates[$templateName])) {
|
if (! isset($this->templates[$templateName])) {
|
||||||
throw new \InvalidArgumentException("Template '{$templateName}' not found in inline templates");
|
throw new InvalidArgumentException("Template '{$templateName}' not found in inline templates");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->templates[$templateName];
|
return $this->templates[$templateName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ use Keepsuit\Liquid\TagBlock;
|
||||||
class TemplateTag extends TagBlock
|
class TemplateTag extends TagBlock
|
||||||
{
|
{
|
||||||
protected string $templateName;
|
protected string $templateName;
|
||||||
|
|
||||||
protected Raw $body;
|
protected Raw $body;
|
||||||
|
|
||||||
public static function tagName(): string
|
public static function tagName(): string
|
||||||
|
|
@ -36,16 +37,16 @@ class TemplateTag extends TagBlock
|
||||||
{
|
{
|
||||||
// Get the template name from the tag parameters
|
// Get the template name from the tag parameters
|
||||||
$templateNameExpression = $context->params->expression();
|
$templateNameExpression = $context->params->expression();
|
||||||
|
|
||||||
$this->templateName = match (true) {
|
$this->templateName = match (true) {
|
||||||
is_string($templateNameExpression) => trim($templateNameExpression),
|
is_string($templateNameExpression) => trim($templateNameExpression),
|
||||||
is_numeric($templateNameExpression) => (string) $templateNameExpression,
|
is_numeric($templateNameExpression) => (string) $templateNameExpression,
|
||||||
$templateNameExpression instanceof VariableLookup => (string) $templateNameExpression,
|
$templateNameExpression instanceof VariableLookup => (string) $templateNameExpression,
|
||||||
default => throw new SyntaxException("Template name must be a string, number, or variable"),
|
default => throw new SyntaxException('Template name must be a string, number, or variable'),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Validate template name (letters, numbers, underscores, and slashes only)
|
// Validate template name (letters, numbers, underscores, and slashes only)
|
||||||
if (!preg_match('/^[a-zA-Z0-9_\/]+$/', $this->templateName)) {
|
if (! preg_match('/^[a-zA-Z0-9_\/]+$/', $this->templateName)) {
|
||||||
throw new SyntaxException("Invalid template name '{$this->templateName}' - template names must contain only letters, numbers, underscores, and slashes");
|
throw new SyntaxException("Invalid template name '{$this->templateName}' - template names must contain only letters, numbers, underscores, and slashes");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,7 +75,7 @@ class TemplateTag extends TagBlock
|
||||||
// Get the file system from the environment
|
// Get the file system from the environment
|
||||||
$fileSystem = $context->environment->fileSystem;
|
$fileSystem = $context->environment->fileSystem;
|
||||||
|
|
||||||
if (!$fileSystem instanceof InlineTemplatesFileSystem) {
|
if (! $fileSystem instanceof InlineTemplatesFileSystem) {
|
||||||
// If no inline file system is available, just return empty string
|
// If no inline file system is available, just return empty string
|
||||||
// This allows the template to be used in contexts where inline templates aren't supported
|
// This allows the template to be used in contexts where inline templates aren't supported
|
||||||
return '';
|
return '';
|
||||||
|
|
@ -96,4 +97,4 @@ class TemplateTag extends TagBlock
|
||||||
{
|
{
|
||||||
return $this->body;
|
return $this->body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -182,10 +182,12 @@ class Device extends Model
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Firmware::class, 'update_firmware_id');
|
return $this->belongsTo(Firmware::class, 'update_firmware_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deviceModel(): BelongsTo
|
public function deviceModel(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(DeviceModel::class);
|
return $this->belongsTo(DeviceModel::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function logs(): HasMany
|
public function logs(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(DeviceLog::class);
|
return $this->hasMany(DeviceLog::class);
|
||||||
|
|
@ -224,19 +226,20 @@ class Device extends Model
|
||||||
if ($from < $to) {
|
if ($from < $to) {
|
||||||
// Normal range, same day
|
// Normal range, same day
|
||||||
return $now->between($from, $to) ? (int) $now->diffInSeconds($to, false) : null;
|
return $now->between($from, $to) ? (int) $now->diffInSeconds($to, false) : null;
|
||||||
} else {
|
|
||||||
// Overnight range
|
|
||||||
if ($now->gte($from)) {
|
|
||||||
// After 'from', before midnight
|
|
||||||
return (int) $now->diffInSeconds($to->copy()->addDay(), false);
|
|
||||||
} elseif ($now->lt($to)) {
|
|
||||||
// After midnight, before 'to'
|
|
||||||
return (int) $now->diffInSeconds($to, false);
|
|
||||||
} else {
|
|
||||||
// Not in sleep window
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// Overnight range
|
||||||
|
if ($now->gte($from)) {
|
||||||
|
// After 'from', before midnight
|
||||||
|
return (int) $now->diffInSeconds($to->copy()->addDay(), false);
|
||||||
|
}
|
||||||
|
if ($now->lt($to)) {
|
||||||
|
// After midnight, before 'to'
|
||||||
|
return (int) $now->diffInSeconds($to, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not in sleep window
|
||||||
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isPauseActive(): bool
|
public function isPauseActive(): bool
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ class AppServiceProvider extends ServiceProvider
|
||||||
// Register OIDC provider with Socialite
|
// Register OIDC provider with Socialite
|
||||||
Socialite::extend('oidc', function ($app) {
|
Socialite::extend('oidc', function ($app) {
|
||||||
$config = $app['config']['services.oidc'] ?? [];
|
$config = $app['config']['services.oidc'] ?? [];
|
||||||
|
|
||||||
return new OidcProvider(
|
return new OidcProvider(
|
||||||
$app['request'],
|
$app['request'],
|
||||||
$config['client_id'] ?? null,
|
$config['client_id'] ?? null,
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ class ImageGenerationService
|
||||||
if (config('app.puppeteer_wait_for_network_idle')) {
|
if (config('app.puppeteer_wait_for_network_idle')) {
|
||||||
$browsershot->waitUntilNetworkIdle();
|
$browsershot->waitUntilNetworkIdle();
|
||||||
}
|
}
|
||||||
if (config('app.puppeteer_window_size_strategy') == 'v2') {
|
if (config('app.puppeteer_window_size_strategy') === 'v2') {
|
||||||
$browsershot->windowSize($imageSettings['width'], $imageSettings['height']);
|
$browsershot->windowSize($imageSettings['width'], $imageSettings['height']);
|
||||||
} else {
|
} else {
|
||||||
$browsershot->windowSize(800, 480);
|
$browsershot->windowSize(800, 480);
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
use Laravel\Socialite\Two\AbstractProvider;
|
use Laravel\Socialite\Two\AbstractProvider;
|
||||||
use Laravel\Socialite\Two\ProviderInterface;
|
use Laravel\Socialite\Two\ProviderInterface;
|
||||||
use Laravel\Socialite\Two\User;
|
use Laravel\Socialite\Two\User;
|
||||||
use GuzzleHttp\Client;
|
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
|
|
||||||
class OidcProvider extends AbstractProvider implements ProviderInterface
|
class OidcProvider extends AbstractProvider implements ProviderInterface
|
||||||
{
|
{
|
||||||
|
|
@ -36,19 +36,19 @@ class OidcProvider extends AbstractProvider implements ProviderInterface
|
||||||
public function __construct($request, $clientId, $clientSecret, $redirectUrl, $scopes = [], $guzzle = [])
|
public function __construct($request, $clientId, $clientSecret, $redirectUrl, $scopes = [], $guzzle = [])
|
||||||
{
|
{
|
||||||
parent::__construct($request, $clientId, $clientSecret, $redirectUrl, $guzzle);
|
parent::__construct($request, $clientId, $clientSecret, $redirectUrl, $guzzle);
|
||||||
|
|
||||||
$endpoint = config('services.oidc.endpoint');
|
$endpoint = config('services.oidc.endpoint');
|
||||||
if (!$endpoint) {
|
if (! $endpoint) {
|
||||||
throw new \Exception('OIDC endpoint is not configured. Please set OIDC_ENDPOINT environment variable.');
|
throw new Exception('OIDC endpoint is not configured. Please set OIDC_ENDPOINT environment variable.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle both full well-known URL and base URL
|
// Handle both full well-known URL and base URL
|
||||||
if (str_ends_with($endpoint, '/.well-known/openid-configuration')) {
|
if (str_ends_with($endpoint, '/.well-known/openid-configuration')) {
|
||||||
$this->baseUrl = str_replace('/.well-known/openid-configuration', '', $endpoint);
|
$this->baseUrl = str_replace('/.well-known/openid-configuration', '', $endpoint);
|
||||||
} else {
|
} else {
|
||||||
$this->baseUrl = rtrim($endpoint, '/');
|
$this->baseUrl = rtrim($endpoint, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->scopes = $scopes ?: ['openid', 'profile', 'email'];
|
$this->scopes = $scopes ?: ['openid', 'profile', 'email'];
|
||||||
$this->loadOidcConfiguration();
|
$this->loadOidcConfiguration();
|
||||||
}
|
}
|
||||||
|
|
@ -59,21 +59,21 @@ class OidcProvider extends AbstractProvider implements ProviderInterface
|
||||||
protected function loadOidcConfiguration()
|
protected function loadOidcConfiguration()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$url = $this->baseUrl . '/.well-known/openid-configuration';
|
$url = $this->baseUrl.'/.well-known/openid-configuration';
|
||||||
$client = new Client();
|
$client = new Client();
|
||||||
$response = $client->get($url);
|
$response = $client->get($url);
|
||||||
$this->oidcConfig = json_decode($response->getBody()->getContents(), true);
|
$this->oidcConfig = json_decode($response->getBody()->getContents(), true);
|
||||||
|
|
||||||
if (!$this->oidcConfig) {
|
if (! $this->oidcConfig) {
|
||||||
throw new \Exception('OIDC configuration is empty or invalid JSON');
|
throw new Exception('OIDC configuration is empty or invalid JSON');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($this->oidcConfig['authorization_endpoint'])) {
|
if (! isset($this->oidcConfig['authorization_endpoint'])) {
|
||||||
throw new \Exception('authorization_endpoint not found in OIDC configuration');
|
throw new Exception('authorization_endpoint not found in OIDC configuration');
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (Exception $e) {
|
||||||
throw new \Exception('Failed to load OIDC configuration: ' . $e->getMessage());
|
throw new Exception('Failed to load OIDC configuration: '.$e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,9 +82,10 @@ class OidcProvider extends AbstractProvider implements ProviderInterface
|
||||||
*/
|
*/
|
||||||
protected function getAuthUrl($state)
|
protected function getAuthUrl($state)
|
||||||
{
|
{
|
||||||
if (!$this->oidcConfig || !isset($this->oidcConfig['authorization_endpoint'])) {
|
if (! $this->oidcConfig || ! isset($this->oidcConfig['authorization_endpoint'])) {
|
||||||
throw new \Exception('OIDC configuration not loaded or authorization_endpoint not found.');
|
throw new Exception('OIDC configuration not loaded or authorization_endpoint not found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->buildAuthUrlFromBase($this->oidcConfig['authorization_endpoint'], $state);
|
return $this->buildAuthUrlFromBase($this->oidcConfig['authorization_endpoint'], $state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,9 +94,10 @@ class OidcProvider extends AbstractProvider implements ProviderInterface
|
||||||
*/
|
*/
|
||||||
protected function getTokenUrl()
|
protected function getTokenUrl()
|
||||||
{
|
{
|
||||||
if (!$this->oidcConfig || !isset($this->oidcConfig['token_endpoint'])) {
|
if (! $this->oidcConfig || ! isset($this->oidcConfig['token_endpoint'])) {
|
||||||
throw new \Exception('OIDC configuration not loaded or token_endpoint not found.');
|
throw new Exception('OIDC configuration not loaded or token_endpoint not found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->oidcConfig['token_endpoint'];
|
return $this->oidcConfig['token_endpoint'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,13 +106,13 @@ class OidcProvider extends AbstractProvider implements ProviderInterface
|
||||||
*/
|
*/
|
||||||
protected function getUserByToken($token)
|
protected function getUserByToken($token)
|
||||||
{
|
{
|
||||||
if (!$this->oidcConfig || !isset($this->oidcConfig['userinfo_endpoint'])) {
|
if (! $this->oidcConfig || ! isset($this->oidcConfig['userinfo_endpoint'])) {
|
||||||
throw new \Exception('OIDC configuration not loaded or userinfo_endpoint not found.');
|
throw new Exception('OIDC configuration not loaded or userinfo_endpoint not found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = $this->getHttpClient()->get($this->oidcConfig['userinfo_endpoint'], [
|
$response = $this->getHttpClient()->get($this->oidcConfig['userinfo_endpoint'], [
|
||||||
'headers' => [
|
'headers' => [
|
||||||
'Authorization' => 'Bearer ' . $token,
|
'Authorization' => 'Bearer '.$token,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -153,4 +155,4 @@ class OidcProvider extends AbstractProvider implements ProviderInterface
|
||||||
'grant_type' => 'authorization_code',
|
'grant_type' => 'authorization_code',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ return [
|
||||||
'endpoint' => env('OIDC_ENDPOINT'),
|
'endpoint' => env('OIDC_ENDPOINT'),
|
||||||
'client_id' => env('OIDC_CLIENT_ID'),
|
'client_id' => env('OIDC_CLIENT_ID'),
|
||||||
'client_secret' => env('OIDC_CLIENT_SECRET'),
|
'client_secret' => env('OIDC_CLIENT_SECRET'),
|
||||||
'redirect' => env('APP_URL', 'http://localhost:8000') . '/auth/oidc/callback',
|
'redirect' => env('APP_URL', 'http://localhost:8000').'/auth/oidc/callback',
|
||||||
'scopes' => explode(',', env('OIDC_SCOPES', 'openid,profile,email')),
|
'scopes' => explode(',', env('OIDC_SCOPES', 'openid,profile,email')),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use App\Models\{Device, User};
|
use App\Models\Device;
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
@ -16,8 +17,8 @@ test('dashboard shows device image with correct rotation', function () {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Mock the file existence check
|
// Mock the file existence check
|
||||||
\Illuminate\Support\Facades\Storage::fake('public');
|
Illuminate\Support\Facades\Storage::fake('public');
|
||||||
\Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content');
|
Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content');
|
||||||
|
|
||||||
$response = $this->actingAs($user)
|
$response = $this->actingAs($user)
|
||||||
->get(route('dashboard'));
|
->get(route('dashboard'));
|
||||||
|
|
@ -36,8 +37,8 @@ test('device configure page shows device image with correct rotation', function
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Mock the file existence check
|
// Mock the file existence check
|
||||||
\Illuminate\Support\Facades\Storage::fake('public');
|
Illuminate\Support\Facades\Storage::fake('public');
|
||||||
\Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content');
|
Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content');
|
||||||
|
|
||||||
$response = $this->actingAs($user)
|
$response = $this->actingAs($user)
|
||||||
->get(route('devices.configure', $device));
|
->get(route('devices.configure', $device));
|
||||||
|
|
@ -56,8 +57,8 @@ test('device with no rotation shows no transform style', function () {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Mock the file existence check
|
// Mock the file existence check
|
||||||
\Illuminate\Support\Facades\Storage::fake('public');
|
Illuminate\Support\Facades\Storage::fake('public');
|
||||||
\Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content');
|
Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content');
|
||||||
|
|
||||||
$response = $this->actingAs($user)
|
$response = $this->actingAs($user)
|
||||||
->get(route('dashboard'));
|
->get(route('dashboard'));
|
||||||
|
|
@ -75,8 +76,8 @@ test('device with null rotation defaults to 0', function () {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Mock the file existence check
|
// Mock the file existence check
|
||||||
\Illuminate\Support\Facades\Storage::fake('public');
|
Illuminate\Support\Facades\Storage::fake('public');
|
||||||
\Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content');
|
Illuminate\Support\Facades\Storage::disk('public')->put('images/generated/test-image-uuid.png', 'fake-image-content');
|
||||||
|
|
||||||
$response = $this->actingAs($user)
|
$response = $this->actingAs($user)
|
||||||
->get(route('dashboard'));
|
->get(route('dashboard'));
|
||||||
|
|
|
||||||
|
|
@ -266,7 +266,7 @@ it('maintains correct yaml field order', function () {
|
||||||
|
|
||||||
// Extract and read the settings.yml file
|
// Extract and read the settings.yml file
|
||||||
$zip->extractTo(sys_get_temp_dir(), 'settings.yml');
|
$zip->extractTo(sys_get_temp_dir(), 'settings.yml');
|
||||||
$yamlContent = file_get_contents(sys_get_temp_dir() . '/settings.yml');
|
$yamlContent = file_get_contents(sys_get_temp_dir().'/settings.yml');
|
||||||
$zip->close();
|
$zip->close();
|
||||||
|
|
||||||
// Check that the YAML content has the expected field order
|
// Check that the YAML content has the expected field order
|
||||||
|
|
@ -285,11 +285,11 @@ it('maintains correct yaml field order', function () {
|
||||||
|
|
||||||
$lines = explode("\n", $yamlContent);
|
$lines = explode("\n", $yamlContent);
|
||||||
$fieldLines = [];
|
$fieldLines = [];
|
||||||
|
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
$line = trim($line);
|
$line = trim($line);
|
||||||
if (preg_match('/^([a-zA-Z_]+):/', $line, $matches)) {
|
if (preg_match('/^([a-zA-Z_]+):/', $line, $matches)) {
|
||||||
$fieldLines[] = $matches[1] . ':';
|
$fieldLines[] = $matches[1].':';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -304,5 +304,5 @@ it('maintains correct yaml field order', function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
unlink(sys_get_temp_dir() . '/settings.yml');
|
unlink(sys_get_temp_dir().'/settings.yml');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,6 @@
|
||||||
use App\Models\Plugin;
|
use App\Models\Plugin;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Http\UploadedFile;
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
|
||||||
use Livewire\Volt\Volt;
|
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
|
@ -22,23 +18,23 @@ test('plugin import extracts default values from custom_fields and stores in con
|
||||||
'field_type' => 'string',
|
'field_type' => 'string',
|
||||||
'name' => 'Reading Days',
|
'name' => 'Reading Days',
|
||||||
'description' => 'Select days of the week to read',
|
'description' => 'Select days of the week to read',
|
||||||
'default' => 'Monday,Friday,Saturday,Sunday'
|
'default' => 'Monday,Friday,Saturday,Sunday',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'keyname' => 'refresh_interval',
|
'keyname' => 'refresh_interval',
|
||||||
'field_type' => 'number',
|
'field_type' => 'number',
|
||||||
'name' => 'Refresh Interval',
|
'name' => 'Refresh Interval',
|
||||||
'description' => 'How often to refresh data',
|
'description' => 'How often to refresh data',
|
||||||
'default' => 15
|
'default' => 15,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'keyname' => 'timezone',
|
'keyname' => 'timezone',
|
||||||
'field_type' => 'time_zone',
|
'field_type' => 'time_zone',
|
||||||
'name' => 'Timezone',
|
'name' => 'Timezone',
|
||||||
'description' => 'Select your timezone'
|
'description' => 'Select your timezone',
|
||||||
// No default value
|
// No default value
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
// Extract default values from custom_fields and populate configuration
|
// Extract default values from custom_fields and populate configuration
|
||||||
|
|
@ -53,7 +49,7 @@ test('plugin import extracts default values from custom_fields and stores in con
|
||||||
|
|
||||||
// Create the plugin directly
|
// Create the plugin directly
|
||||||
$plugin = Plugin::create([
|
$plugin = Plugin::create([
|
||||||
'uuid' => \Illuminate\Support\Str::uuid(),
|
'uuid' => Illuminate\Support\Str::uuid(),
|
||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
'name' => 'Test Plugin with Defaults',
|
'name' => 'Test Plugin with Defaults',
|
||||||
'data_stale_minutes' => 30,
|
'data_stale_minutes' => 30,
|
||||||
|
|
@ -68,11 +64,11 @@ test('plugin import extracts default values from custom_fields and stores in con
|
||||||
expect($plugin->configuration)->toHaveKey('reading_days');
|
expect($plugin->configuration)->toHaveKey('reading_days');
|
||||||
expect($plugin->configuration)->toHaveKey('refresh_interval');
|
expect($plugin->configuration)->toHaveKey('refresh_interval');
|
||||||
expect($plugin->configuration)->not->toHaveKey('timezone');
|
expect($plugin->configuration)->not->toHaveKey('timezone');
|
||||||
|
|
||||||
expect($plugin->getConfiguration('reading_days'))->toBe('Monday,Friday,Saturday,Sunday');
|
expect($plugin->getConfiguration('reading_days'))->toBe('Monday,Friday,Saturday,Sunday');
|
||||||
expect($plugin->getConfiguration('refresh_interval'))->toBe(15);
|
expect($plugin->getConfiguration('refresh_interval'))->toBe(15);
|
||||||
expect($plugin->getConfiguration('timezone'))->toBeNull();
|
expect($plugin->getConfiguration('timezone'))->toBeNull();
|
||||||
|
|
||||||
// Verify configuration template was stored correctly
|
// Verify configuration template was stored correctly
|
||||||
expect($plugin->configuration_template)->toBeArray();
|
expect($plugin->configuration_template)->toBeArray();
|
||||||
expect($plugin->configuration_template['custom_fields'])->toHaveCount(3);
|
expect($plugin->configuration_template['custom_fields'])->toHaveCount(3);
|
||||||
|
|
@ -89,9 +85,9 @@ test('plugin import handles custom_fields without default values', function () {
|
||||||
'keyname' => 'timezone',
|
'keyname' => 'timezone',
|
||||||
'field_type' => 'time_zone',
|
'field_type' => 'time_zone',
|
||||||
'name' => 'Timezone',
|
'name' => 'Timezone',
|
||||||
'description' => 'Select your timezone'
|
'description' => 'Select your timezone',
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
// Extract default values from custom_fields and populate configuration
|
// Extract default values from custom_fields and populate configuration
|
||||||
|
|
@ -106,7 +102,7 @@ test('plugin import handles custom_fields without default values', function () {
|
||||||
|
|
||||||
// Create the plugin directly
|
// Create the plugin directly
|
||||||
$plugin = Plugin::create([
|
$plugin = Plugin::create([
|
||||||
'uuid' => \Illuminate\Support\Str::uuid(),
|
'uuid' => Illuminate\Support\Str::uuid(),
|
||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
'name' => 'Test Plugin No Defaults',
|
'name' => 'Test Plugin No Defaults',
|
||||||
'data_stale_minutes' => 30,
|
'data_stale_minutes' => 30,
|
||||||
|
|
@ -119,8 +115,8 @@ test('plugin import handles custom_fields without default values', function () {
|
||||||
expect($plugin)->not->toBeNull();
|
expect($plugin)->not->toBeNull();
|
||||||
expect($plugin->configuration)->toBeArray();
|
expect($plugin->configuration)->toBeArray();
|
||||||
expect($plugin->configuration)->toBeEmpty();
|
expect($plugin->configuration)->toBeEmpty();
|
||||||
|
|
||||||
// Verify configuration template was stored correctly
|
// Verify configuration template was stored correctly
|
||||||
expect($plugin->configuration_template)->toBeArray();
|
expect($plugin->configuration_template)->toBeArray();
|
||||||
expect($plugin->configuration_template['custom_fields'])->toHaveCount(1);
|
expect($plugin->configuration_template['custom_fields'])->toHaveCount(1);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue