[Résolu] Comment construire la session après la connexion

Information importante

En raison d'un grand nombre d'inscriptions de spammers sur notre site, polluant sans relache notre forum, nous suspendons la création de compte via le formulaire de "sign up".

Il est néanmoins toujours possible de devenir adhérent•e en faisant la demande sur cette page, rubrique "Inscription" : https://www.drupal.fr/contact


De plus, le forum est désormais "interdit en écriture". Il n'est plus autorisé d'y écrire un sujet/billet/commentaire.

Pour contacter la communauté, merci de rejoindre le slack "drupalfrance".

Si vous voulez contacter le bureau de l'association, utilisez le formulaire disponible ici, ou envoyez-nous un DM sur twitter.

Bonjour à tous,

Le titre n'est pas clair mais je vais vous expliquer mon problème.

J'ai modifié le processus de connexion afin d'ajouter un module qui vérifie la validité d'un code utilisateur. En fait, je redirige dans tout les cas l'utilisateur connecté vers une nouvelle page qui va vérifier la validité de son code.

Deux cas sont possibles :
- L'utilisateur a un code valide donc il est renvoyé vers la page d'accueil.
- L'utilisateur n'a pas de code valide donc je détruit sa session et je le redirige vers un formulaire qui lui demande de rentrer un nouveau code valide.

Je n'arrive pas dans le deuxième cas à construire une nouvelle session une fois la première détruite.

Quelqu'un aurait une idée ?

Merci

Forum : 
Version de Drupal : 

Peux tu indiquer ta méthode pour détruire la session ?
Je pense que ça aidera pour t'aider.

Je ne connais pas le contexte mais je ne trouve pas ça très logique de connecter l'utilisateur pour ensuite vérifier son authentification...

Pourquoi ne pas avoir implémenté une validation sur le champ de ton "code" ou sur le formulaire en lui-même ?
Ca t'éviterai tout désagrément et d'un point de vue utilisateur, ça parait plus intéressant.

J'ai utilisé le code de déconnexion du module user :

// Destroy the current session:
session_destroy();
module_invoke_all('user', 'logout', NULL, $user);
 
// Load the anonymous user
$user = drupal_anonymous_user();

Sinon, moi aussi je ne trouve pas ça très logique de connecter l'utilisateur pour le déconnecter ensuite.
Mais c'est la seule façon que j'ai trouvé.

Je n'avais pas pensé à la validation sur le formulaire en lui-même donc je vais essayé ça.

Par contre, j'aurais encore un problème parce que je n'arrive pas à générer la session.

J'avais oublié, merci de m'aider webastien.

J'ai essayé dans mon hook_validate de vérifier la validité du code et je me suis aperçu que la session était déjà générée avant que je fasse la vérification.

Mon code est le suivant :

function ctuser_form_alter($form_id, &$form){ 
    if ($form_id == 'user_login_block' || $form_id == 'user_login'){
        $form['#validate'] = (array)$form['#validate'] + array('ctuser_validate' => array());
    }
}

function ctuser_validate($form_id, $form_values){
    if ($form_id == 'user_login_block' || $form_id == 'user_login'){
    global $user;
    print_r($user);
}

Est-ce que c'est la bonne façon de faire ?

Tu t'en approches, te restes à coder la logique métier.
Je réécris ton code car je préfères cette écriture mais tu adapteras :

<?php
function ctuser_form_alter($form_id, &$form) {
  switch (
$form_id) {
    case
'user_login_block':
    case
'user_login':
     
$form['#validate']['ctuser_user_login_validate'] = array();
      break;
  }
}

function
ctuser_user_login_validate($form_id, $form_values) {
  if (!
ctuser_check_code($form_values['my_code'])) {
   
form_set_error('my_code', t('Sucker !'));
  }
}

function
ctuser_check_code($code) {
 
// Place ici ta logique métier et renvoie un booléen
  // (TRUE si code valide, FALSE dans le cas contraire)
 
return ($code == 'sesame ouvre toi');
}
?>

Je suis parti du principe que le "field_key" de ton champ "code" est "my_code".
Sur cet exemple, seul le code "sesame ouvre toi" valide l'inscription.

Merci encore webastien.

J'ai toujours le même problème, je mets un print_r($user) dans la fonction ctuser_user_login_validate et si l'utilisateur saisi des bons paramètres de connexion (email et mdp) alors la session est créée. Donc la fonction de validité du code ne sert à rien puisque l'utilisateur a sa session.

Voilà le code :

<?php
function ctuser_form_alter($form_id, &$form) {
  switch (
$form_id) {
    case
'user_login_block':
    case
'user_login':
     
$form['#validate']['ctuser_user_login_validate'] = array();
      break;
  }
}

function ctuser_user_login_validate($form_id, $form_values) {
  global
$user;
 
print_r($user);

  if (!ctuser_check_code($form_values['my_code'])) {
   
form_set_error('my_code', t('Sucker !'));
  }
}

function ctuser_check_code($code) {
 
// Place ici ta logique métier et renvoie un booléen
  // (TRUE si code valide, FALSE dans le cas contraire)
 
return ($code == 'sesame ouvre toi');
}
?>

Je ne comprend pas comment ça se fait que le hook_validate passe après.

Non, une session "authenticated user".

Je viens d'essayer et j'ai vu que si j'écris :
$form['#validate'] = array('ctuser_user_login_validate' => array());
au lieu de
$form['#validate']['ctuser_user_login_validate'] = array();
alors l'utilisateur a une session "authenticated user" donc ça marche.

Maintenant,j'essaie de récupérer l'uid de l'utilisateur avec la fonction user_authenticate(name, pass) qui marche mais elle créé la session. Y a t-il une autre fonction que le fasse ou faut-t-il le faire à la main ?

Bizarre ta méthode : Si tu remplaces le tableau $form['#validate'], alors toutes les autres validations sont perdues...
A ne pas faire car si ça marche aujourd'hui... pas sûr demain (notamment si tu ajoutes des modules).

A mais attends... j'ai pigé :

<?php
function user_login_validate($form_id, $form_values) {
  if (
$form_values['name']) {

    ...

    else if (
$form_values['pass']) {
     
$user = user_authenticate($form_values['name'], trim($form_values['pass']));

      ...
    }
  }
}
?>

L'authentification se fait dans la fonction de validation...
J'ai une méthode du coup :

<?php
function ctuser_form_alter($form_id, &$form) {
  switch (
$form_id) {
    case
'user_login_block':
    case
'user_login':
     
$form['#validate'] = array(
       
'ctuser_user_login_validate' => array(
         
$form['#validate']
        )
      );
      break;
  }
}

function
ctuser_user_login_validate($form_id, $form_values, $validations) {
  global
$user;
 
print_r($user);

  if (!
ctuser_check_code($form_values['my_code'])) {
   
form_set_error('my_code', t('Sucker !'));
  }
  elseif (
is_array($validations) && count($validations)) {
    foreach (
$validations as $function => $args) {
      if (
function_exists($function)) {
       
call_user_func_array($function, $args);
      }
    }
  }
}

function
ctuser_check_code($code) {
 
// Place ici ta logique métier et renvoie un booléen
  // (TRUE si code valide, FALSE dans le cas contraire)
 
return ($code == 'sesame ouvre toi');
}
?>

Vérifies que ce soient les bons arguments à passer mais l'idée est là.

A+

Merci encore webastien de m'aider.

Je n'ai peut-être pas très bien expliquer mon problème depuis le début mais le code de l'utilisateur n'est pas saisi lors de la connexion. Ce code est saisi au moment de l'inscription de l'utilisateur et a une validité d'un mois.

C'est-à-dire qu'à chaque connexion d'un utilisateur (il saisit son adresse e-mail et son mot de passe) je vérifie que son code est toujours valide. C'est pour cela que ma première méthode était de détruire la session dans le cas où le code n'était pas valide. Puisque la session était créée j'avais accès à l'uid et donc je pouvais faire ma vérification.

Maintenant que je fais ma vérification dans le hook_validate je n'ai plus accès à l'uid. Du coup je suis obligé de tester que l'utilisateur existe et je récupère l'uid. Mais ces tests sont déjà fait dans le hook_validate du module user donc ce n'est pas je pense une bonne méthode.

J'ai en plus un problème parce que dans la validation on ne peut pas faire de redirection. Dans mon cas je n'affiche pas un message d'erreur pour dire que le code n'est pas valide mais je dois rediriger vers une page avec un formulaire pour saisir un code valide.

J'espère que j'ai mieux expliqué mon problème.

Dans ce cas :
- créée un champ masqué avec le module profile.
- places y le timestamp de fin de validité de ton code
- dans un hook_cron, tu désactives les comptes dont ce timestamp est dépassé.

Pour ta redirection, dans le xxxx_form_submit, tu renvoies un path et c'est logiquement ok.

Merci, très bonne idée.

J'ai encore deux problèmes :

Je n'arrive pas à faire de redirection sans changer le $form['#action'] et je suis encore bloqué parce que si je désactive un utilisateur alors à la connexion j'ai un message d'erreur donc je ne peux pas aller jusqu'au hook_submit.

Le code suivant ne fonctionne pas :

<?php
function ctuser_form_alter($form_id, &$form){
    switch(
$form_id){
        case
'user_login_block': case 'user_login':
           
$form['#submit'] = (array)$form['#submit'] + array('ctuser_login_submit' => array());
            break;
    }
}

function ctuser_login_submit($form_id, $form_values){
   
drupal_goto('forum');
   
ou
   
return 'forum';
}
?>

J'ai vraiment beaucoup de mal à faire ce que je veux et je pense que je vais mettre encore du temps pour faire juste cette vérification.
Il ne serait pas plus judicieux si cela est possible de créer un rôle limité à la page de mise-à-jour du code ? Comme ça je fait le test dans le hook_validate et si le code n'est pas valide je change son rôle.

Sinon, pour le hook_cron, comment indique-t-on à Drupal la périodicité d'exécution du cron ? J'ai installer le module cronplus mais je ne suis pas sûr qu'il fonctionne correctement.

"Il ne serait pas plus judicieux si cela est possible de créer un rôle limité à la page de mise-à-jour du code ? Comme ça je fait le test dans le hook_validate et si le code n'est pas valide je change son rôle."

Excellente idée.

"comment indique-t-on à Drupal la périodicité d'exécution du cron ?"

En stoquant la dernière date d'exécution dans une variable bdd :
variable_get() / variable_set()
et en comparant cette date + xxx secondes avec time().

"J'ai installer le module cronplus mais je ne suis pas sûr qu'il fonctionne correctement."

Là dessus désolé : je n'ai jamais utilisé ce module, je ne saurai pas te dire ce qu'il vaut.

Merci webastien de continuer à m'aider.

Je sais qu'il faut créer le rôle dans l'administration et choisir ses droits d'accès.

Mais est-ce possible d'avoir le rôle "authenticated user", le nouveau rôle en même temps et d'avoir son accès limiter à une seule page ?

Ce que je voudrais faire, est lorsque l'utilisateur se connecte et je détecte que son code n'est plus valide (dans le hook_validate) alors je lui laisse le rôle "authenticated user" (comme ça il garde sa session) et je lui ajoute un nouveau rôle qui limite son accès à la page de mise à jour du code.
Une fois qu'il a mis à jour son code et qu'il est valide alors je lui enlève le nouveau rôle qui limitait son accès ainsi il est toujours connecté et il peut désormais parcourir le site normalement.

Sauf erreur de ma part le module logintoboggan permet seulement de donner un rôle spécifique aux nouveaux utilisateurs au moment de l'inscription.

Dans mon cas, c'est au moment de la connexion.

Merci quand même, je vais chercher de mon côté.

Le module Path Access à l'air d'être approprié à mon problème.

Maintenant il ne me reste plus qu'a savoir comment je vais finalement la validation du code.

Pour cela je voudrais savoir comment on peut changer le rôle d'un utilisateur connecté. J'ai essayé avec le code suivant mais ça ne fonctionne pas (DRUPAL_UPDATE_RID est défini en haut de mon fichier PHP).

global $user;

// This is done to unserialize the data member of $user
$user = drupal_unpack($user);
    
// Add roles element to $user
$user->roles = array();
$user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
$user->roles[DRUPAL_UPDATE_RID] = 'update user';

Au temps pour moi, je suis parti à des kilomètres de la solution.

J'ai enfin trouvé un début de solution qui semble être le plus approprié. J'en dirais plus une fois que j'aurais bien avancé.

Merci beaucoup webastien pour ton aide.

Voilà la solution que j'ai choisie.

J'ai utilisé la même technique implémentée par le module login_destination.

C'est-à-dire je change le $form['#action'] dans mon hook_form pour le formulaire de connexion. Lorsqu'un utilisateur se connecte il est redirigé sans le savoir vers une page qui s'occupe de détermine si l'utilisateur doit ou non être redirigé vers la page de mise-à-jour du code.

Maintenant lors de l'inscription l'utilisateur se voit affecter un rôle par défaut différent de "authenticated user". Ce rôle lui permet d'avoir accès à tout le contenu du site. Sinon, j'ai modifié le rôle "authenticated user" pour qu'il soit seulement limité à la mise-à-jour du code.

Dans le cas où l'utilisateur a un code plus valide alors je lui change son rôle (je le enlève le rôle par défaut pour lui laisser seulement le rôle "authenticated user" qui est limité) avec le code suivant :

<?php
   
global $user;
    unset(
$user->roles[6]);
   
user_save($user, array('roles' => $user->roles));
   
cache_clear_all($user->uid .':', 'cache_menu', TRUE);
   
cache_clear_all();
?>

Le code suivant permet de changer le rôle d'un utilisateur connecté comme cela il est redirigé vers la page de mise-à-jour du code avec des droits très limités. De plus, tant qu'il n'aura pas mis à jour son code dès qu'il se connectera il aura toujours ses droits limités.

Enfin, si l'utilisateur met à jour son code avec un code valide alors j'utilise la même technique pour lui remets le rôle par défaut sans limite d'accès.

<?php
   
global $user;
   
$user->roles[6] = 'actived user';
   
user_save($user, array('roles' => $user->roles));
   
cache_clear_all($user->uid .':', 'cache_menu', TRUE);
   
cache_clear_all();
?>

Voilà, maintenant cela fonctionne enfin !!!!!