diff --git a/backend/app/routes/admin.py b/backend/app/routes/admin.py index eea708f..c0121f6 100644 --- a/backend/app/routes/admin.py +++ b/backend/app/routes/admin.py @@ -1454,8 +1454,11 @@ def global_settings(): }, 'auth_providers': { 'local': SystemSetting.get_value('auth_local_enabled', 'true') in ['true', 'True', '1', True], + 'local_enabled': SystemSetting.get_value('auth_local_enabled', 'true') in ['true', 'True', '1', True], 'google': SystemSetting.get_value('auth_google_enabled', 'false') in ['true', 'True', '1', True], - 'moodle': SystemSetting.get_value('auth_moodle_enabled', 'false') in ['true', 'True', '1', True] + 'google_enabled': SystemSetting.get_value('auth_google_enabled', 'false') in ['true', 'True', '1', True], + 'moodle': SystemSetting.get_value('auth_moodle_enabled', 'false') in ['true', 'True', '1', True], + 'moodle_enabled': SystemSetting.get_value('auth_moodle_enabled', 'false') in ['true', 'True', '1', True] }, 'google_oauth': { 'client_id': SystemSetting.get_value('google_client_id', ''), @@ -1555,3 +1558,61 @@ def toggle_demo_mode(): flash(_(msg_detail), 'success' if not target_state else 'info') return redirect(url_for('admin.global_settings')) + +@admin_bp.route('/settings/auth-providers', methods=['POST']) +@login_required +def update_auth_providers_web(): + """Actualiza los métodos de autenticación habilitados desde la interfaz de administración.""" + if not (current_user.is_admin() or (current_user.role and current_user.role.upper() == 'ADMIN')): + if request.is_json or request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({'status': 'error', 'message': 'Acción no autorizada'}), 403 + abort(403) + + from app.models.setting import SystemSetting + from app.models.audit_log import AuditLog + + data = request.get_json(silent=True) or request.form.to_dict() or {} + local_input = data.get('local_enabled') if 'local_enabled' in data else data.get('local', True) + google_input = data.get('google_enabled') if 'google_enabled' in data else data.get('google', False) + moodle_input = data.get('moodle_enabled') if 'moodle_enabled' in data else data.get('moodle', False) + + local_val = 'true' if local_input in [True, 'true', '1', 'on'] else 'false' + google_val = 'true' if google_input in [True, 'true', '1', 'on'] else 'false' + moodle_val = 'true' if moodle_input in [True, 'true', '1', 'on'] else 'false' + + SystemSetting.set_value('auth_local_enabled', local_val, 'Habilitar login nativo con usuario/contraseña', category='sso') + SystemSetting.set_value('auth_google_enabled', google_val, 'Habilitar login SSO con Google Workspace', category='sso') + SystemSetting.set_value('auth_moodle_enabled', moodle_val, 'Habilitar login delegado con Moodle', category='sso') + + try: + audit = AuditLog( + user_id=current_user.id, + user_email=current_user.email, + action='UPDATE_AUTH_PROVIDERS', + module='settings', + details=f"Métodos de login actualizados: Local={local_val}, Google={google_val}, Moodle={moodle_val}" + ) + db.session.add(audit) + db.session.commit() + except Exception: + pass + + resp_data = { + 'status': 'success', + 'message': 'Métodos de autenticación actualizados correctamente.', + 'auth_providers': { + 'local': local_val == 'true', + 'local_enabled': local_val == 'true', + 'google': google_val == 'true', + 'google_enabled': google_val == 'true', + 'moodle': moodle_val == 'true', + 'moodle_enabled': moodle_val == 'true' + } + } + + if request.is_json or request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify(resp_data), 200 + + flash(_('Métodos de autenticación actualizados correctamente.'), 'success') + return redirect(url_for('admin.global_settings')) + diff --git a/backend/app/routes/api/admin.py b/backend/app/routes/api/admin.py index a90cca6..18a67b5 100644 --- a/backend/app/routes/api/admin.py +++ b/backend/app/routes/api/admin.py @@ -1746,8 +1746,11 @@ def get_all_settings(): } auth_providers = { + 'local': SystemSetting.get_value('auth_local_enabled', 'true') in ['true', 'True', '1', True], 'local_enabled': SystemSetting.get_value('auth_local_enabled', 'true') in ['true', 'True', '1', True], + 'google': SystemSetting.get_value('auth_google_enabled', 'false') in ['true', 'True', '1', True], 'google_enabled': SystemSetting.get_value('auth_google_enabled', 'false') in ['true', 'True', '1', True], + 'moodle': SystemSetting.get_value('auth_moodle_enabled', 'false') in ['true', 'True', '1', True], 'moodle_enabled': SystemSetting.get_value('auth_moodle_enabled', 'false') in ['true', 'True', '1', True] } @@ -1914,9 +1917,15 @@ def update_auth_providers(): return jsonify({'error': 'Forbidden', 'message': 'Solo administradores pueden modificar los métodos de autenticación.'}), 403 data = request.get_json(silent=True) or request.form.to_dict() or {} - local_val = 'true' if data.get('local_enabled', True) in [True, 'true', '1', 'on'] else 'false' - google_val = 'true' if data.get('google_enabled', False) in [True, 'true', '1', 'on'] else 'false' - moodle_val = 'true' if data.get('moodle_enabled', False) in [True, 'true', '1', 'on'] else 'false' + + # Soporte para claves tanto cortas (local) como largas (local_enabled) + local_input = data.get('local_enabled') if 'local_enabled' in data else data.get('local', True) + google_input = data.get('google_enabled') if 'google_enabled' in data else data.get('google', False) + moodle_input = data.get('moodle_enabled') if 'moodle_enabled' in data else data.get('moodle', False) + + local_val = 'true' if local_input in [True, 'true', '1', 'on'] else 'false' + google_val = 'true' if google_input in [True, 'true', '1', 'on'] else 'false' + moodle_val = 'true' if moodle_input in [True, 'true', '1', 'on'] else 'false' SystemSetting.set_value('auth_local_enabled', local_val, 'Habilitar login nativo con usuario/contraseña', category='sso') SystemSetting.set_value('auth_google_enabled', google_val, 'Habilitar login SSO con Google Workspace', category='sso') @@ -1937,7 +1946,15 @@ def update_auth_providers(): return jsonify({ 'status': 'success', - 'message': 'Métodos de autenticación actualizados correctamente.' + 'message': 'Métodos de autenticación actualizados correctamente.', + 'auth_providers': { + 'local': local_val == 'true', + 'local_enabled': local_val == 'true', + 'google': google_val == 'true', + 'google_enabled': google_val == 'true', + 'moodle': moodle_val == 'true', + 'moodle_enabled': moodle_val == 'true' + } }), 200 diff --git a/frontend/src/app.js b/frontend/src/app.js index 6ab0beb..bda26f6 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -218,6 +218,7 @@ app.use('/api/v1', async (req, res) => { const targetUrl = `${process.env.FLASK_API_URL || 'http://localhost:5000/api/v1'}${req.url}`; const headers = { ...req.headers }; delete headers.host; + delete headers['content-length']; if (token && !headers.authorization) { headers.authorization = `Bearer ${token}`; } diff --git a/frontend/src/routes/admin.js b/frontend/src/routes/admin.js index 5a5c41e..d5294a9 100644 --- a/frontend/src/routes/admin.js +++ b/frontend/src/routes/admin.js @@ -1321,6 +1321,43 @@ const handleGlobalSettings = async (req, res) => { console.warn('Global settings fetch notice:', e.message); } + // Normalización robusta de auth_providers para asegurar compatibilidad total con Nunjucks + if (settings && settings.auth_providers) { + const ap = settings.auth_providers; + const isLocal = ap.local !== undefined ? Boolean(ap.local) : (ap.local_enabled !== undefined ? Boolean(ap.local_enabled) : true); + const isGoogle = ap.google !== undefined ? Boolean(ap.google) : (ap.google_enabled !== undefined ? Boolean(ap.google_enabled) : false); + const isMoodle = ap.moodle !== undefined ? Boolean(ap.moodle) : (ap.moodle_enabled !== undefined ? Boolean(ap.moodle_enabled) : false); + settings.auth_providers = { + local: isLocal, + local_enabled: isLocal, + google: isGoogle, + google_enabled: isGoogle, + moodle: isMoodle, + moodle_enabled: isMoodle + }; + } else { + settings.auth_providers = { + local: true, + local_enabled: true, + google: false, + google_enabled: false, + moodle: false, + moodle_enabled: false + }; + } + + if (settings && settings.smtp) { + settings.smtp.password_masked = settings.smtp.password_masked || settings.smtp.password || ''; + settings.smtp.from_email = settings.smtp.from_email || settings.smtp.sender_email || ''; + settings.smtp.from_name = settings.smtp.from_name || settings.smtp.sender_name || 'Admin Edu-Space'; + } + if (settings && settings.google_oauth) { + settings.google_oauth.client_secret_masked = settings.google_oauth.client_secret_masked || settings.google_oauth.client_secret || ''; + } + if (settings && settings.moodle) { + settings.moodle.ws_token_masked = settings.moodle.ws_token_masked || settings.moodle.ws_token || ''; + } + try { const demoResp = await req.apiClient.get('/settings/demo-mode'); if (demoResp.data) { @@ -1359,6 +1396,37 @@ router.post('/settings/demo-mode', async (req, res) => { } }); +// Endpoint explícito para consultar y actualizar métodos de autenticación +router.get('/settings/auth-providers', async (req, res) => { + try { + const resp = await req.apiClient.get('/settings/all'); + const ap = resp.data?.settings?.auth_providers || resp.data?.data?.auth_providers || {}; + return res.json({ + status: 'success', + auth_providers: { + local: ap.local !== undefined ? Boolean(ap.local) : (ap.local_enabled !== undefined ? Boolean(ap.local_enabled) : true), + google: ap.google !== undefined ? Boolean(ap.google) : (ap.google_enabled !== undefined ? Boolean(ap.google_enabled) : false), + moodle: ap.moodle !== undefined ? Boolean(ap.moodle) : (ap.moodle_enabled !== undefined ? Boolean(ap.moodle_enabled) : false) + } + }); + } catch (err) { + return res.status(500).json({ status: 'error', message: err.message }); + } +}); + +router.post('/settings/auth-providers', async (req, res) => { + try { + const resp = await req.apiClient.post('/settings/auth-providers', req.body); + return res.status(resp.status || 200).json(resp.data); + } catch (err) { + console.error('Error in POST /admin/settings/auth-providers:', err.response?.data || err.message); + return res.status(err.response?.status || 500).json(err.response?.data || { + status: 'error', + message: 'Error al actualizar métodos de autenticación: ' + (err.response?.data?.message || err.message) + }); + } +}); + // Función forwarder para acciones de configuración hacia Flask const forwardSettingsAction = async (req, res) => { try { @@ -1381,7 +1449,6 @@ const forwardSettingsAction = async (req, res) => { router.post('/settings/smtp', forwardSettingsAction); router.post('/settings/smtp/test', forwardSettingsAction); -router.post('/settings/auth-providers', forwardSettingsAction); router.post('/settings/google-oauth', forwardSettingsAction); router.post('/settings/moodle', forwardSettingsAction); router.post('/settings/moodle/test', forwardSettingsAction); diff --git a/frontend/views/admin/settings/global_config.html b/frontend/views/admin/settings/global_config.html index eb41ca2..8bccba9 100644 --- a/frontend/views/admin/settings/global_config.html +++ b/frontend/views/admin/settings/global_config.html @@ -186,44 +186,90 @@