diff --git a/lms/hooks.py b/lms/hooks.py index 256513c3..9664c303 100644 --- a/lms/hooks.py +++ b/lms/hooks.py @@ -25,6 +25,8 @@ web_include_js = [ "/assets/lms/js/eduvia_sso_login.js", ] +before_request = ["lms.integrations.eduvia_sso.try_sso_login"] + # include custom scss in every website theme (without file extension ".scss") # website_theme_scss = "lms/public/scss/website" diff --git a/lms/public/js/eduvia_sso_login.js b/lms/public/js/eduvia_sso_login.js index 3303e6e7..1b3aa83e 100644 --- a/lms/public/js/eduvia_sso_login.js +++ b/lms/public/js/eduvia_sso_login.js @@ -1,25 +1,96 @@ document.addEventListener('DOMContentLoaded', () => { - if (location.pathname !== '/login') return; - - const target = - document.querySelector('.page-card .page-card-actions') || - document.querySelector('form#login_form') || - document.querySelector('.page-card'); - - if (!target || document.getElementById('eduvia-login-btn')) return; - - const btn = document.createElement('a'); - btn.id = 'eduvia-login-btn'; - btn.href = '/eduvia/sso/start?return=' + encodeURIComponent(location.href); - btn.className = 'btn btn-primary btn-block mt-3'; - btn.innerText = 'Se connecter avec Eduvia'; - - if (target.tagName && target.tagName.toLowerCase() === 'form') { - const div = document.createElement('div'); - div.className = 'mt-3'; - div.appendChild(btn); - target.appendChild(div); + // ajoute le bouton que sur la page de login - s'il n'y en a pas deja + if (!location.pathname.includes('/login')) return; + + const target = + document.querySelector('.page-card .page-card-actions') || + document.querySelector('form#login_form') || + document.querySelector('.page-card'); + + if (!target || document.getElementById('eduvia-login-btn')) return; + + const btn = document.createElement('a'); + btn.id = 'eduvia-login-btn'; + btn.className = 'btn btn-primary btn-block mt-3'; + btn.innerText = 'Se connecter avec Eduvia'; + btn.href = '#'; + + const insertIntoForm = (container) => { + if (container && container.tagName && container.tagName.toLowerCase() === 'form') { + const wrap = document.createElement('div'); + wrap.className = 'mt-3'; + wrap.appendChild(btn); + container.appendChild(wrap); } else { target.appendChild(btn); } - }); \ No newline at end of file + }; + + insertIntoForm(target); + + btn.addEventListener( + 'click', + async (e) => { + e.preventDefault(); + if (btn.dataset.loading === '1') return; + + // Recupere user/pass depuis le formulaire s'ils existent -sinon prompt() + const emailEl = + document.querySelector('#login_email') || + document.querySelector('input[name="login_email"]') || + document.querySelector('input[type="email"]'); + + const passEl = + document.querySelector('#login_password') || + document.querySelector('input[name="login_password"]') || + document.querySelector('input[type="password"]'); + + const user = + (emailEl && emailEl.value && emailEl.value.trim()) || + (typeof prompt === 'function' ? prompt('Utilisateur Eduvia') : ''); + + if (!user) return; + + const password = + (passEl && passEl.value) || + (typeof prompt === 'function' ? prompt('Mot de passe Eduvia') : ''); + + if (password == null) return; + + const form = new FormData(); + form.set('user', user); + form.set('password', password); + + const originalText = btn.innerText; + btn.dataset.loading = '1'; + btn.innerText = 'Connexion en cours...'; + btn.classList.add('disabled'); + + try { + const res = await fetch('/api/method/lms.lms.api.eduvia_proxy_login', { + method: 'POST', + body: form, + credentials: 'include', + }); + + // Frappe renvoie { message: {...} } + const payload = await res.json().catch(() => ({})); + const msg = payload && (payload.message || payload); + + if (res.ok && msg && msg.success) { + // Connecte: redirige vers la home (ou recharge) + window.location.assign('/'); + } else { + alert((msg && msg.error) || 'Login Eduvia échoué'); + } + } catch (err) { + alert('Erreur réseau'); + } finally { + btn.dataset.loading = ''; + btn.innerText = originalText; + btn.classList.remove('disabled'); + } + }, + true + ); +}); \ No newline at end of file diff --git a/lms/www/eduvia/sso/mock.py b/lms/www/eduvia/sso/mock.py deleted file mode 100644 index 5838ec1a..00000000 --- a/lms/www/eduvia/sso/mock.py +++ /dev/null @@ -1,78 +0,0 @@ -import time -import uuid -from datetime import datetime, timedelta, timezone - -import frappe - -try: - import jwt # PyJWT -except Exception: - jwt = None - - -def _now_utc(): - return datetime.now(timezone.utc) - - -def get_context(context): - conf = frappe.get_conf() - - if not conf.get("eduvia_sso_mock_enabled"): - frappe.throw("Mock désactivé. Ajoutez eduvia_sso_mock_enabled: 1 dans site_config.json") - - if jwt is None: - frappe.throw("PyJWT manquant. Installez-le: bench pip install PyJWT") - - shared_secret = conf.get("eduvia_sso_shared_secret") - if not shared_secret: - frappe.throw("Ajoutez eduvia_sso_shared_secret dans site_config.json") - - algorithm = conf.get("eduvia_sso_algorithm", "HS256") - cookie_name = conf.get("eduvia_sso_cookie_name", "eduvia_sso") - # Optionnel: mets "lms.localhost" en dev HTTP, ou ".eduvia.app" en prod HTTPS - cookie_domain = conf.get("eduvia_sso_cookie_domain") - cookie_path = conf.get("eduvia_sso_cookie_path", "/") - cookie_ttl_seconds = int(conf.get("eduvia_sso_cookie_ttl_seconds", 120)) - issuer = conf.get("eduvia_sso_issuer", "eduvia") - audience = conf.get("eduvia_sso_audience", "lms-eduvia") - - user_email = frappe.form_dict.get("email") or "demo.user@eduvia.local" - return_url = frappe.form_dict.get("return") or "/" - - now = _now_utc() - expires = now + timedelta(seconds=cookie_ttl_seconds) - claims = { - "iss": issuer, - "aud": audience, - "sub": user_email, - "email": user_email, - "iat": int(now.timestamp()), - "exp": int(expires.timestamp()), - "jti": str(uuid.uuid4()), - } - - token = jwt.encode(claims, shared_secret, algorithm=algorithm) - - is_https = getattr(getattr(frappe, "request", None), "scheme", "") == "https" - default_secure = 1 if is_https else 0 - secure_flag = bool(int(conf.get("eduvia_sso_cookie_secure", default_secure))) - - # Set the cookie - cookies = frappe.local.response.setdefault("cookie", {}) - cookie = { - "value": token, - "path": cookie_path, - "secure": False, # change to True for production - "httponly": True, - "samesite": "Lax", - "max_age": cookie_ttl_seconds, - "expires": int(time.time()) + cookie_ttl_seconds, - } - if cookie_domain: - cookie["domain"] = cookie_domain # otherwise host-only - - cookies[cookie_name] = cookie - - # Redirect to the original page - frappe.local.flags.redirect_location = return_url - raise frappe.Redirect \ No newline at end of file diff --git a/lms/www/eduvia/sso/start.py b/lms/www/eduvia/sso/start.py deleted file mode 100644 index c709dd74..00000000 --- a/lms/www/eduvia/sso/start.py +++ /dev/null @@ -1,19 +0,0 @@ -import frappe -from urllib.parse import urlencode - -def get_context(context): - return_url = frappe.form_dict.get("return") or "/" - email = frappe.form_dict.get("email") - conf = frappe.get_conf() - - if conf.get("eduvia_sso_mock_enabled"): - params = {"return": return_url} - if email: - params["email"] = email - target = f"{frappe.utils.get_url('/eduvia/sso/mock')}?{urlencode(params)}" - else: - eduvia_start = conf.get("eduvia_sso_start_url") or "https://app.eduvia.app/sso/start" - target = f"{eduvia_start}?{urlencode({'return': return_url})}" - - frappe.local.flags.redirect_location = target - raise frappe.Redirect \ No newline at end of file