Files
Virtnet.bond/extensions/Others/Authentik/Authentik.php
2025-11-30 11:57:22 +01:00

363 lines
14 KiB
PHP

<?php
namespace Paymenter\Extensions\Others\Authentik;
use App\Attributes\ExtensionMeta;
use App\Classes\Extension\Extension;
use App\Events\SocialiteWasCalled;
use App\Helpers\ExtensionHelper;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Lang;
use Paymenter\Extensions\Others\Authentik\Providers\AuthentikProvider;
use Paymenter\Extensions\Others\Authentik\Http\Controllers\AuthentikController;
use Paymenter\Extensions\Others\Authentik\Middleware\ForceAuthentikOnly;
use Paymenter\Extensions\Others\Authentik\Livewire\AuthentikLoginOverride;
use Livewire\Livewire;
#[ExtensionMeta(
name: 'Authentik SSO - Forclus.NET',
description: 'Integración completa con Authentik para autenticación SSO - Desarrollado por ForclusNET',
version: '1.0.0',
author: 'ForclusNET',
url: 'https://forclus.net'
)]
class Authentik extends Extension
{
public function __construct(public $config = []) {}
/**
* Get all the configuration for the extension
*
* @param array $values
* @return array
*/
public function getConfig($values = [])
{
return [
[
'name' => 'enabled',
'label' => __('authentik::messages.authentik_enabled'),
'description' => __('authentik::messages.authentik_enabled'),
'type' => 'checkbox',
'database_type' => 'boolean',
'default' => false,
'required' => false,
],
[
'name' => 'client_id',
'label' => __('authentik::messages.authentik_client_id'),
'type' => 'text',
'required' => false,
],
[
'name' => 'client_secret',
'label' => __('authentik::messages.authentik_client_secret'),
'type' => 'text',
'required' => false,
],
[
'name' => 'base_url',
'label' => __('authentik::messages.authentik_base_url'),
'type' => 'text',
'required' => false,
'description' => __('authentik::messages.authentik_base_url_description'),
],
[
'name' => 'auto_create_users',
'label' => __('authentik::messages.authentik_auto_create_users'),
'description' => __('authentik::messages.authentik_auto_create_users_description'),
'type' => 'checkbox',
'database_type' => 'boolean',
'default' => true,
'required' => false,
],
[
'name' => 'require_existing_user',
'label' => __('authentik::messages.authentik_require_existing_user'),
'description' => __('authentik::messages.authentik_require_existing_user_description'),
'type' => 'checkbox',
'database_type' => 'boolean',
'default' => false,
'required' => false,
],
[
'name' => 'force_authentik_only',
'label' => __('authentik::messages.authentik_force_authentik_only'),
'description' => __('authentik::messages.authentik_force_authentik_only_description'),
'type' => 'checkbox',
'database_type' => 'boolean',
'default' => false,
'required' => false,
],
[
'name' => 'button_text',
'label' => __('authentik::messages.authentik_button_text'),
'description' => __('authentik::messages.authentik_button_text_description'),
'type' => 'text',
'default' => __('authentik::messages.authentik_login_button'),
'required' => false,
],
[
'name' => 'button_image',
'label' => __('authentik::messages.authentik_button_image'),
'description' => __('authentik::messages.authentik_button_image_description'),
'type' => 'text',
'default' => '',
'required' => false,
],
[
'name' => 'button_image_alt',
'label' => __('authentik::messages.authentik_button_image_alt'),
'description' => __('authentik::messages.authentik_button_image_alt_description'),
'type' => 'text',
'default' => 'Authentik',
'required' => false,
],
[
'name' => 'button_bg_color',
'label' => __('authentik::messages.authentik_button_bg_color'),
'description' => __('authentik::messages.authentik_button_bg_color_description'),
'type' => 'text',
'default' => '#3B82F6',
'required' => false,
],
[
'name' => 'button_text_color',
'label' => __('authentik::messages.authentik_button_text_color'),
'description' => __('authentik::messages.authentik_button_text_color_description'),
'type' => 'text',
'default' => '#FFFFFF',
'required' => false,
],
[
'name' => 'callback_url',
'label' => __('authentik::messages.authentik_callback_url'),
'description' => __('authentik::messages.authentik_callback_url_description'),
'type' => 'text',
'default' => url('/oauth/authentik/callback'),
'required' => false,
'disabled' => true,
],
];
}
public function enabled()
{
// La extensión está activada
// Modificar automáticamente la vista de login
try {
$this->modifyLoginView();
\Log::info('Extensión Authentik activada - Vista de login modificada automáticamente');
} catch (\Exception $e) {
\Log::error('Error al modificar la vista de login al activar la extensión: ' . $e->getMessage());
}
}
public function disabled()
{
// La extensión está desactivada
// Restaurar la vista de login original
try {
$this->restoreLoginView();
\Log::info('Extensión Authentik desactivada - Vista de login restaurada automáticamente');
} catch (\Exception $e) {
\Log::error('Error al restaurar la vista de login al desactivar la extensión: ' . $e->getMessage());
}
}
public function boot()
{
// Registrar las rutas
$this->registerRoutes();
// Registrar las vistas y idiomas
View::addNamespace('authentik', __DIR__ . '/resources/views');
Lang::addNamespace('authentik', __DIR__ . '/resources/lang');
// Registrar el provider Authentik con Socialite
Event::listen(SocialiteWasCalled::class, function (SocialiteWasCalled $event) {
$event->extendSocialite('authentik', AuthentikProvider::class);
});
// Registrar los permisos
Event::listen('permissions', function () {
return [
'admin.authentik.view' => 'Ver configuración de Authentik',
'admin.authentik.update' => 'Actualizar configuración de Authentik',
];
});
// Registrar los permisos API
Event::listen('api.permissions', function () {
return [
'admin.authentik.view' => 'Ver configuración de Authentik',
'admin.authentik.update' => 'Actualizar configuración de Authentik',
];
});
// El middleware está desactivado para evitar bucles de redirección
// La lógica de "force_authentik_only" se maneja directamente en la vista de login
// ExtensionHelper::registerMiddleware(ForceAuthentikOnly::class);
// Registrar el componente Livewire para el override del login
Livewire::component('authentik-login-override', AuthentikLoginOverride::class);
// Hook para modificar la página de login
Event::listen('view.login', function ($view) {
$extension = Extension::where('extension', 'Authentik')->first();
if ($extension && $extension->enabled) {
$settings = $extension->settings->pluck('value', 'key')->toArray();
if (!empty($settings['force_authentik_only'])) {
// Reemplazar el contenido de la vista de login
$view->with('forceAuthentikOnly', true);
$view->with('authentikEnabled', true);
}
}
});
}
/**
* Registrar las rutas de la extensión
*/
private function registerRoutes()
{
require __DIR__ . '/routes/web.php';
}
/**
* Prueba de la configuración Authentik
*/
public function testConfig()
{
$baseUrl = $this->config['base_url'] ?? '';
$clientId = $this->config['client_id'] ?? '';
$clientSecret = $this->config['client_secret'] ?? '';
if (empty($baseUrl) || empty($clientId) || empty($clientSecret)) {
return 'Configuración incompleta. Por favor, complete todos los campos requeridos.';
}
try {
// Prueba de conexión a la API Authentik
$response = \Http::timeout(10)
->get($baseUrl . '/api/v3/core/users/me/');
if ($response->successful()) {
return 'Configuración válida. Conexión a Authentik exitosa.';
} else {
return 'Error de conexión a Authentik: ' . $response->status();
}
} catch (\Exception $e) {
return 'Error de prueba: ' . $e->getMessage();
}
}
/**
* Modificar automáticamente la vista de login
*/
private function modifyLoginView()
{
$loginViewPath = base_path('themes/default/views/auth/login.blade.php');
if (!file_exists($loginViewPath)) {
\Log::warning('Vista de login no encontrada en: ' . $loginViewPath);
return false;
}
// Leer el contenido actual
$content = file_get_contents($loginViewPath);
// Verificar si ya está modificada
if (strpos($content, 'force_authentik_only') !== false) {
\Log::info('La vista de login ya está modificada.');
return true;
}
// Crear el contenido modificado
$newContent = '@php
// Verificar si el modo "force_authentik_only" está activado
$extension = \App\Models\Extension::where(\'extension\', \'Authentik\')->first();
$forceAuthentikOnly = false;
$authentikEnabled = false;
if ($extension && $extension->enabled) {
$settings = $extension->settings->pluck(\'value\', \'key\')->toArray();
$forceAuthentikOnly = !empty($settings[\'force_authentik_only\']);
$authentikEnabled = !empty($settings[\'client_id\']) && !empty($settings[\'client_secret\']) && !empty($settings[\'base_url\']);
}
@endphp
@if($forceAuthentikOnly && $authentikEnabled)
{{-- Mode Authentik uniquement --}}
<div class="mx-auto flex flex-col gap-2 mt-4 shadow-sm px-6 sm:px-14 pb-10 bg-primary-800 rounded-md xl:max-w-[40%] w-full">
<div class="flex flex-col items-center mt-4 mb-10">
<x-logo />
<h1 class="text-2xl text-center mt-2">{{ __(\'auth.sign_in_title\') }}</h1>
<p class="text-sm text-center mt-2 text-gray-300">
{{ __(\'authentik::messages.authentik_force_only_description\') }}
</p>
</div>
<a href="{{ route(\'oauth.authentik.redirect\') }}"
class="w-full flex items-center justify-center px-4 py-3 bg-blue-600 border border-transparent rounded-md font-semibold text-sm text-white uppercase tracking-widest hover:bg-blue-700 focus:bg-blue-700 active:bg-blue-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M3 3a1 1 0 000 2v8a2 2 0 002 2h2.586l-1.293 1.293a1 1 0 101.414 1.414L10 15.414l2.293 2.293a1 1 0 001.414-1.414L12.414 15H15a2 2 0 002-2V5a1 1 0 100-2H3zm11.707 4.707a1 1 0 00-1.414-1.414L10 9.586 8.707 8.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
</svg>
{{ __(\'authentik::messages.authentik_login_button\') }}
</a>
</div>
@else
{{-- Mode normal --}}
' . $content . '
@endif';
// Crear backup
$backupPath = $loginViewPath . '.backup.' . date('Y-m-d-H-i-s');
file_put_contents($backupPath, $content);
\Log::info('Backup creado en: ' . $backupPath);
// Escribir el nuevo contenido
if (file_put_contents($loginViewPath, $newContent)) {
\Log::info('Vista de login modificada exitosamente.');
return true;
} else {
\Log::error('Error al modificar la vista de login.');
return false;
}
}
/**
* Restaurar la vista de login original
*/
private function restoreLoginView()
{
$loginViewPath = base_path('themes/default/views/auth/login.blade.php');
// Buscar el backup más reciente
$backupFiles = glob($loginViewPath . '.backup.*');
if (empty($backupFiles)) {
\Log::warning('No se encontraron archivos de backup.');
return false;
}
// Ordenar por fecha de modificación (más reciente primero)
usort($backupFiles, function($a, $b) {
return filemtime($b) - filemtime($a);
});
$latestBackup = $backupFiles[0];
$originalContent = file_get_contents($latestBackup);
if (file_put_contents($loginViewPath, $originalContent)) {
\Log::info('Vista de login restaurada desde: ' . $latestBackup);
return true;
} else {
\Log::error('Error al restaurar la vista de login.');
return false;
}
}
}