2
0

cms.login.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. <?php
  2. // Appliquer les headers de sécurité pour la page de login
  3. securityHeaders::applyForLogin();
  4. ?>
  5. <!DOCTYPE html>
  6. <html lang="fr">
  7. <head>
  8. <meta charset="UTF-8">
  9. <title>CSE Invent : Identification</title>
  10. <?php pwa::printManifeste(); ?>
  11. <meta name="mobile-web-app-capable" content="yes">
  12. <meta name="apple-mobile-web-app-capable" content="yes">
  13. <meta name="application-name" content="CSE Invent : CMS">
  14. <meta name="apple-mobile-web-app-title" content="CSE Invent : CMS">
  15. <meta name="msapplication-starturl" content="/">
  16. <meta name="msapplication-TileColor" content="#da532c">
  17. <meta name="theme-color" content="#ffffff">
  18. <link rel="icon" type="image/x-icon" href="<?php cache::printFileWithTime("favicon.ico") ?>">
  19. <!-- Token CSRF pour les requêtes AJAX -->
  20. <?php echo csrf::metaTag('login-ajax'); ?>
  21. <script src="<?php cache::printFileWithTime("libs/js/jquery.min.js") ?>"></script>
  22. <script src="<?php cache::printFileWithTime("libs/bootstrap/js/bootstrap.min.js") ?>"></script>
  23. <link rel="stylesheet" href="<?php cache::printFileWithTime("libs/bootstrap/assets/dist/css/bootstrap.min.css") ?>">
  24. <link rel="stylesheet" href="<?php cache::printFileWithTime("css/login.css") ?>">
  25. </head>
  26. <body>
  27. <div class="wrapper fadeInDown">
  28. <div id="formContent">
  29. <div class="fadeIn first">
  30. <img src="img/logo.png" id="icon" alt="CSE Invent" />
  31. </div>
  32. <form method="post" action="/submit.php" id="form-authent">
  33. <?php echo csrf::inputField('login-form'); ?>
  34. <input type="hidden" name="from" value="login">
  35. <!-- Honeypot anti-bot (champ invisible) -->
  36. <div style="position:absolute;left:-9999px;" aria-hidden="true">
  37. <input type="text" name="website" tabindex="-1" autocomplete="off">
  38. </div>
  39. <input type="text" class="fadeIn second" name="email" id="email" placeholder="email" required autocomplete="username">
  40. <input type="password" class="fadeIn third" name="password" placeholder="mot de passe" required autocomplete="current-password">
  41. <input type="text" class="third" style="display:none;" name="authenticator" id="authenticator" maxlength="6" placeholder="Code Google Authenticator">
  42. <input type="button" class="fadeIn fourth" id="submit-authent" value="Se connecter">
  43. <!-- Message de rate limiting -->
  44. <div id="rate-limit-warning" class="alert alert-warning" style="display:none;margin:10px;"></div>
  45. </form>
  46. <div id="formFooter" <?php if (!alert::ifError()) {
  47. echo ' style="display:none"';
  48. } ?>>
  49. <div class="alert alert-danger" role="alert"><?php if (alert::ifError()) {
  50. echo alert::printAlert(alert::getError());
  51. } ?></div>
  52. </div>
  53. <script nonce="<?php echo securityHeaders::getNonce(); ?>">
  54. $(document).ready(function() {
  55. // Variable pour savoir si on attend le code Authenticator
  56. var authenticatorRequired = false;
  57. // Configuration CSRF pour les requêtes AJAX
  58. $(document).ajaxSend(function(event, jqxhr, settings) {
  59. if (settings.type === 'POST') {
  60. const csrfToken = $('meta[name="csrf-token"]').attr('content');
  61. if (csrfToken) {
  62. jqxhr.setRequestHeader('X-CSRF-Token', csrfToken);
  63. }
  64. }
  65. });
  66. $("#submit-authent").on("click", function(e) {
  67. var $btn = $(this);
  68. var $warning = $("#rate-limit-warning");
  69. // Si Authenticator est requis et le code est rempli, soumettre le formulaire
  70. if (authenticatorRequired) {
  71. if ($("#authenticator").val().length === 6) {
  72. $("#form-authent").submit();
  73. } else {
  74. $warning.html('<strong>⚠️ Veuillez entrer le code à 6 chiffres.</strong>').show();
  75. }
  76. return;
  77. }
  78. // Désactiver le bouton pendant la requête
  79. $btn.prop('disabled', true).val('Vérification...');
  80. $warning.hide();
  81. var formData = {
  82. email: $("#email").val(),
  83. from: "authenticator",
  84. csrf_token: $("input[name='csrf_token']").val()
  85. };
  86. $.ajax({
  87. type: "POST",
  88. url: "submit.php",
  89. data: formData,
  90. dataType: "json",
  91. xhrFields: {
  92. withCredentials: true
  93. },
  94. }).done(function(data) {
  95. console.log("Réponse authenticator:", data);
  96. if (data && data.blocked) {
  97. $warning.html('<strong>⚠️ ' + data.message + '</strong>').show();
  98. $btn.prop('disabled', true).val('Bloqué');
  99. if (data.remaining_time) {
  100. setTimeout(function() {
  101. $btn.prop('disabled', false).val('Se connecter');
  102. $warning.hide();
  103. }, data.remaining_time * 1000);
  104. }
  105. } else if (data && (data.authenticator == 1 || data.required === true)) {
  106. // Google Authenticator requis
  107. authenticatorRequired = true;
  108. $("#authenticator").show().focus();
  109. $("#authenticator").prop("required", true);
  110. $btn.prop('disabled', false).val('Valider');
  111. } else {
  112. // Pas de 2FA, soumettre directement
  113. $("#form-authent").submit();
  114. }
  115. }).fail(function(jqXHR, textStatus, errorThrown) {
  116. console.error("Erreur AJAX:", textStatus, errorThrown);
  117. $btn.prop('disabled', false).val('Se connecter');
  118. if (jqXHR.status === 429) {
  119. var response = jqXHR.responseJSON || {};
  120. $warning.html('<strong>⚠️ ' + (response.message || 'Trop de tentatives. Veuillez patienter.') + '</strong>').show();
  121. } else if (jqXHR.status === 403) {
  122. $warning.html('<strong>⚠️ Session expirée. Rechargez la page.</strong>').show();
  123. }
  124. });
  125. });
  126. // Permettre la soumission avec Entrée dans le champ Authenticator
  127. $("#authenticator").on("keypress", function(e) {
  128. if (e.which === 13 && $(this).val().length === 6) {
  129. e.preventDefault();
  130. $("#form-authent").submit();
  131. }
  132. });
  133. });
  134. </script>
  135. </div>
  136. </div>
  137. <?php pwa::printServiceWorker(); ?>
  138. </body>
  139. </html>
  140. <?php
  141. alert::destroyAlert();
  142. ?>