Authentification

Les systèmes d’authentification constituent une partie courante de nombreuses applications Web. Avec CakePHP, il y a plusieurs systèmes pour authentifier un utilisateur, chacun fournissant des options différentes. Au coeur du composant d’authentification, on vérifie si l’utilisateur possède un compte sur le site. Si c’est la cas, le composant donne l’accès à tout le site à l’utilisateur. Ce composant peut être combiné avec le composant ACL (access control lists) pour créer des niveaux d’accès à l’intérieur du site. Cela permettra à un utilisateur de s’authentifier comme utilisateur générique tandis qu’un autre sera authentifié comme administrateur qui possède des droits pour accéder aux parties protégées du site.

Le composant AuthComponent peut servir à créer un tel système rapidement et facilement. Regardons comment construire un système d’authentification simple.

Comme n’importe quel composants, vous devrez l’ajouter à la liste des composants en ajoutant “Auth” dans votre contrôleur :

class FooController extends AppController {
    var $components = array('Auth');

Ou ajoutez-le à votre AppController pour le rendre disponible à l’ensemble des contrôleurs :

class AppController extends Controller {
    var $components = array('Auth');

Maintenant, il y a quelques conventions auxquelles penser quand on utilise le composant AuthComponent. Par défaut, AuthComponent s’attend à ce que la table des utilisateurs s’appelle “users” et qu’elle comporte les champs nommées “username” et “password”.

Le script SQL suivant met en place une telle table :

CREATE TABLE users (
    id integer auto_increment,
    username char(50),
    password char(50),
    PRIMARY KEY (id)
);

Pour une sécurité accrue, le composant AuthComponent hashe automatiquement les données soumises de n’importe quel formulaire dont le nom correspond au nom du champ que vous avez rattaché au champ password de AuthComponent. Par exemple, si vous avez créé un nouvel utilisateur, la valeur stockée dans $data[“User”][“password”] sera une valeur hashée plutôt que la chaîne de caractère saisie par l’utilisateur à l’origine.

Ce comportement peut créer quelques difficultés si vous chercher à vous assurer que que le nouvel utilisateur a choisi un mot de passe suffisamment complexe. Par exemple, vous pourriez vous assurer que tous les gens ont des mots de passe de 8 caractères au moins.

Vous pouvez contourner la difficulté en créant un champ dans votre formulaire, dont le nom ne correspond pas au nom du champ mot de passe dans le modèle. Vous pouvez alors valider l’entrée de l’utilisateur, hasher ce champ manuellement et le sauvegarder en base en utilisant le modèle User.

Ce procédé de hashage automatique de mot de passe doit être gardé à l’esprit quand on crée une table pour stocker l’authentification de l’utilisateur. Assurez-vous que le champ mot de passe est suffisamment long pour stocker le hash (40 caractères pour SHA1 par exemple).

Pour le paramétrage le plus basique, il vous faudra créer deux actions dans votre contrôleur :

class UsersController extends AppController {

    var $name = 'Users';

    function login() {
    }

    function logout() {
        $this->redirect($this->Auth->logout());
    }
}

Vous n’avez réellement besoin que de créer une vue, la page de login (située dans app/views/users/login.ctp). L’exemple ci-dessous présuppose que vous utilisez le Helper Form

<?php
    if  ($session->check('Message.auth')) $session->flash('auth');
    echo $form->create('User', array('action' => 'login'));
    echo $form->input('username');
    echo $form->input('password');
    echo $form->end('Login');
?>

Cette vue crée un formulaire d’authentification où on peut entrer son nom d’utilisateur et un mot de passe. Quand on soumet le formulaire, le composant AuthComponent prend en charge le reste pour vous. Le message de session flash affichera les messages généré par AuthComponent.

Si vous n’utilisez pas $form->create, assurez-vous que vous éléments input adressent bien les champs en tant que User.username et User.password.

Croyez le ou no, on en a terminé ! Voici comment écrire un système d’authentification fondé sur la base de données avec le composant AuthComponent. Cependant, il y a encore plus de choses qu’on peut faire. Intéressons-nous maintenant à quelques usages avancés du composant.

A chaque fois que vous voulez modifier une option par défaut du composant, vous pouvez le faire en créant une méthode beforeFilter() pour votre contrôleur et en appelant les diverses méthodes natives et les variables de paramétrage du composant.

class UsersController extends AppController {
    var $component = array('Auth');

    function beforeFilter() {
        ....
    }
}

La touche finale est de faire en sorte que votre layout (gabarit) récupère les messages d’erreur en provenance du composant. Voici ce que ça donne si on les regroupe avec les messages flash ordinaires :

<?php
    if ($session->check('Message.flash')) {
        $session->flash();
    }
    if ($session->check('Message.auth')) {
        $session->flash('auth');
    }
?>

Attardons nous maintenant sur les méthodes du composant AuthComponent à notre disposition.

Configurer les variables du composant Auth

Chaque fois que vous voulez modifier une option par défaut du composant Auth, vous devez le faire en créant une méthode beforeFilter() dans votre contrôleur, puis en appelant les différentes méthodes pré-existantes ou en configurant les variables du composant.

Par exemple, pour changer le nom du champ utilisé pour le mot de passe de “password” à “mot_secret”, vous devez faire ceci :

class UsersController extends AppController {
    var $components = array('Auth');

    function beforeFilter() {
        $this->Auth->fields = array(
            'username' => 'username',
            'password' => 'mot_secret'
            );
    }
}

Dans cette situation particulière, vous devrez aussi penser à changer le nom du champ dans la vue correspondante !

Une autre utilisation commune des variables du composant Auth est d’autoriser l’accès à certaines méthodes sans que l’utilisateur ne soit identifié (par défaut, Auth interdit l’accès à toutes les actions sauf aux méthodes login et logout).

Par exemple, si nous voulions autoriser tous les utilisateurs à accéder aux méthodes index et voir (mais à aucune autre), nous ferions comme çà :

function beforeFilter() {
        $this->Auth->allow('index','voir');
}

Afficher les messages d’erreur du composant Auth

Pour afficher les messages d’erreur que Auth renvoie, vous devez ajouter le code suivant à votre vue. Dans ce cas, le message apparaitra à la suite des messages flash normaux :

<?php
    $session->flash();
    $session->flash('auth');
?>

Diagnostic des problèmes avec Auth

Il peut être parfois un peu difficile de diagnostiquer les problèmes quand ça ne marche pas comme prévu, voici donc quelques points à se rappeler.

Hâchage du mot de passe

Quand vous postez des informations à une action via un formulaire, le composant Auth hâche (crypte) automatiquement le contenu de votre champ mot de passe, si vous avez également une donnée dans le champ “username”. Donc, si vous essayez de créer une page d’inscription quelconque, assurez-vous que l’utilisateur ait rempli le champ “confirmation du mot de passe” pour comparer les deux. Voici un exemple de code :

<?php
function enregistrer() {
    if ($this->data) {
        if ($this->data['User']['password'] == $this->Auth->password($this->data['User']['password_confirm'])) {
            $this->User->create();
            $this->User->save($this->data);
        }
    }
}
?>

Changer la fonction de hâchage

Le composant Auth utilise la classe Security pour hacher un mot de passe. La classe Security utilise le procédé SHA1 par défaut. Pour changer la fonction de hash utilisée par le composant Auth, servez-vous de la méthode setHash en lui passant md5, sha1 ou sha256 comme premier et unique paramètre.

Security::setHash('md5'); // ou sha1 ou sha256.

La classe Security utilise une valeur salt (définie dans /app/config/core.php) pour hacher le mot de passe.

Si vous voulez utiliser une logique de hachage du mot de passe différente, autre que md5/sha1 ajouté au salt de l’application, vous devrez surcharger le mécanisme standard de hashPassword. Vous aurez besoin de faire cela si vous avez, par exemple, une base de données existante, qui utilisait précédemment un procédé de hachage sans salt. Pour faire cela, créez la méthode hashPasswords dans la classe à laquelle vous souhaitez confier le hachage de vos mots de passe (habituellement le modèle User) et définissez authenticate par l’objet sur lequel vous réalisez l’authentification (habituellement, c’est User), comme ceci :

function beforeFilter() {
   $this->Auth->authenticate = ClassRegistry::init('User');
   ...
   parent::beforeFilter();
}

Avec le code ci-dessus, la méthode hashPasswords() du modèle User sera appelée chaque fois que Cake appelle AuthComponent::hashPasswords().

Les Méthodes du composant Auth

action

action (string $action = ':controller/:action')

Si vous utilisez les ACOs dans le cadre de votre structure ACL, vous pouvez obtenir le chemin jusqu’au nœud ACO relié à un couple contrôleur/action particulier :

$acoNode = $this->Auth->action('users/delete');

Si vous ne passez pas de valeur, le couple contrôleur/action courant est utilisé.

allow

Si vous avez des actions dans votre contrôleur que vous n’avez pas besoin d’authentifier (comme une action d’enregistrement d’un utilisateur), vous pouvez ajouter des méthodes que le composant Auth devrait ignorer. L’exemple suivant montre comment autoriser une action intitulée “enregistrer”.

function beforeFilter() {
    ...
    $this->Auth->allow('enregistrer');
}

Si vous souhaitez autoriser plusieurs actions qui échapperont à l’authentification, passez-les en paramètres à la méthode allow() :

function beforeFilter() {
    ...
    $this->Auth->allow('foo', 'bar', 'baz');
}

Raccourci : vous pouvez aussi autoriser toutes les actions d’un contrôleur en utilisant “*”.

function beforeFilter() {
    ...
    $this->Auth->allow('*');
}

Si vous utilisez requestAction dans votre layout ou vos éléments, vous devriez autoriser ces actions de façon à être capable d’ouvrir la page de login proprement.

Le composant Auth suppose que les noms de vos actions respectent les conventions et qu’elles sont « underscorées ».

deny

Il peut arriver que vous vouliez retirer des actions de la liste des actions autorisées (déclarée en utilisant $this->Auth->allow()). Voici un exemple :

function beforeFilter() {
    $this->Auth->authorize = 'controller';
    $this->Auth->allow('delete');
}

function isAuthorized() {
    if ($this->Auth->user('role') != 'admin') {
        $this->Auth->deny('delete');
    }

    ...
}

hashPasswords

hashPasswords ($data)

Cette méthode vérifie si $data contient les champs username et password, comme spécifié par la variable $fields, elle même indexée par le nom du modèle, comme spécifié dans $userModel. Si le tableau $data contient à la fois username et password, la méthode encode le champ password du tableau et retourne le tableau $data dans le même format. Cette fonction devrait être utilisée en priorité pour les requêtes d’insertion ou de mise à jour de l’utilisateur, quand le champ password est affecté.

$data['User']['username'] = '[email protected]';
$data['User']['password'] = 'changemoi';
$hashedPasswords = $this->Auth->hashPasswords($data);
print_r($hashedPasswords);
/* retourne :
Array
(
    [User] => Array
    (
        [username] => [email protected]
        [password] => 8ed3b7e8ced419a679a7df93eff22fae
    )
)

*/

Le champ $hashedPasswords[“User”][“password”] sera maintenant encodé en utilisant la fonction password du composant.

Si votre contrôleur utilise le composant Auth et que les données postées contiennent les champs mentionnés ci-dessus, il encodera automatiquement le mot de passe en utilisant cette fonction.

mapActions

Si vous utilisez les Acl en mode CRUD, vous aimeriez peut-être assigner certaines actions non-standards à chaque partie du CRUD.

$this->Auth->mapActions(
    array(
        'create' => array('uneAction'),
        'read' => array('uneAction', 'uneAction2'),
        'update' => array('uneAction'),
        'delete' => array('uneAction')
    )
);

login

login($data = null)

Si vous souhaitez une authentification depuis un composant Ajax, vous pouvez utiliser cette méthode pour authentifier manuellement un utilisateur dans le système. Si vous ne passez aucune valeur pour $data, les données reçues en POST seront alors automatiquement passées au controlleur.

logout

Cette méthode fournit une manière rapide de désauthentifier quelqu’un et de le rediriger là où il a besoin d’aller.

Cette méthode est également pratique si vous voulez proposer un lien “Déconnexion” dans la partie membres de votre application.

Exemple :

$this->redirect($this->Auth->logout());

password

password (string $password)

Passez une chaîne à cette méthode et vous pourrez voir à quoi ressemblera le mot de passe crypté. C’est une fonctionnalité essentielle si vous créez un écran d’inscription où les utilisateurs doivent entrer deux fois leur mot de passe pour le confirmer.

if ($this->data['User']['password'] ==
    $this->Auth->password($this->data['User']['password2'])) {

    // Les mots de passe correspondent, on continue
    ...
} else {
    $this->flash('Les mots de passe saisis ne correspondent pas', 'users/register');
}

Le composant Auth encryptera automatiquement le champ password si le champ username est aussi présent dans les données envoyées.

Cake ajoute un « grain de sécurité » (Security.salt) à votre chaîne de mot de passe et crypte le tout ensuite. La fonction de cryptage utilisée dépend de celle définie dans la classe utilitaire Security de CakePHP (sha1 par défaut). Vous pouvez utiliser la fonction Security::setHash pour changer la méthode de cryptage. Le « grain de sécurité » est configuré dans le fichier core.php de votre application.

user

user(string $key = null)

Cette méthode fournit des informations sur l’utilisateur connecté. Ces informations sont issues de la session. Par exemple :

if ($this->Auth->user('role') == 'admin') {
    $this->flash('Vous avez un accès administrateur');
}

Elle peut aussi être utilisée pour obtenir des informations complètes sur la session de l’utilisateur, de cette façon :

$data['User'] = $this->Auth->user();

Si cette méthode renvoie null, l’utilisateur n’est pas connecté.

Dans les vues, vous pouvez utiliser l’assistant Session, pour retrouver les informations sur l’utilisateur actuellement connecté :

$session->read('Auth.User'); // renvoie l'ensemble des informations sur l'utilisateur
$session->read('Auth.User.first_name') // renvoie la valeur d'un champ en particulier

La clé de session peut être différente en fonction du modèle configuré pour utiliser Auth. Par exemple, si vous utilisez le modèle Compte au lieu de User, alors la clé de session sera Auth.Compte.

Variables du composant Auth

Désormais, il y a plusieurs variables liées à Auth que vous pouvez utiliser. Habituellement, vous ajoutez ces configurations dans la méthode beforeFilter() de votre contrôleur. Ou bien, si vous devez appliquer ces règles dans tout le site, vous aurez envie de les ajouter au beforeFilter() du contrôleur App.

userModel

Vous ne voulez pas utiliser un modèle Utilisateur pour vous authentifier ? Pas de problème, modifiez ce comportement en configurant cette variable avec le nom du modèle que vous voulez utiliser.

<?php
    $this->Auth->userModel = 'Membre';
?>

fields

Pour outrepasser les champs utilisateur et mot de passe utilisés par défaut pour l’authentification.

<?php
    $this->Auth->fields = array('username' => 'email', 'password' => 'motdepasse');
?>

userScope

Utilisez cette propriété pour ajouter des contraintes supplémentaires afin que l’authentification réussisse.

<?php
    $this->Auth->userScope = array('Utilisateur.actif' => true);
?>

loginAction

Vous pouvez changer l’adresse de connexion par défaut /users/login par toute action de votre choix.

<?php
    $this->Auth->loginAction = array('admin' => false, 'controller' => 'membres', 'action' => 'login');
?>

loginRedirect

Le Composant Auth mémorise quelle paire contrôleur/action vous essayiez d’obtenir avant que l’on vous demande de vous authentifier, en stockant cette valeur dans la Session, sous la clé Auth.redirect. Cependant, si cette valeur de session n’est pas définie (par exemple, si vous arrivez à la page d’identification depuis un lien externe), alors l’utilisateur sera redirigé à l’URL spécifiée dans loginRedirect.

Exemple :

<?php
    $this->Auth->loginRedirect = array('controller' => 'membres', 'action' => 'accueil');
?>

logoutRedirect

Vous pouvez également spécifier où vous voulez que l’utilisateur soit redirigé après sa déconnexion, ayant pour action par défaut l’action de login.

<?php
    $this->Auth->logoutRedirect = array(Configure::read('Routing.admin') => false, 'controller' => 'membres', 'action' => 'logout');
?>

loginError

Change le message d’erreur par défaut affiché lorsque quelqu’un ne s’authentifie pas correctement.

<?php
    $this->Auth->loginError = "Non, vous vous êtes trompé! Ce n'est pas le bon mot de passe!";
?>

authError

Change le message d’erreur par défaut affiché lorsque quelqu’un essaye d’accéder à une ressource ou une action qu’il n’est pas autorisé à accéder.

<?php
    $this->Auth->authError = "Désolé, vous n'avez pas les droits suffisants.";
?>

autoRedirect

Normalement, le Composant Auth vous redirige automatiquement dès lors qu’il vous authentifie. Parfois, vous souhaitez faire d’autres vérifications avant de rediriger les utilisateurs :

<?php
    function beforeFilter() {
        ...
        $this->Auth->autoRedirect = false;
    }

    ...

    function login() {
    //-- le code de cette fonction ne s'exécute que lorsque autoRedirect est défini à false (i.e. dans un beforeFilter).
        if ($this->Auth->user()) {
            if (!empty($this->data['Utilisateur']['se_souvenir_de_moi'])) {
                $cookie = array();
                $cookie['nom'] = $this->data['Utilisateur']['nom'];
                $cookie['motdepasse'] = $this->data['Utilisateur']['motdepasse'];
                $this->Cookie->write('Auth.Utilisateur', $cookie, true, '+2 weeks');
                unset($this->data['Utilisateur']['se_souvenir_de_moi']);
            }
            $this->redirect($this->Auth->redirect());
        }
        if (empty($this->data)) {
            $cookie = $this->Cookie->read('Auth.Utilisateur');
            if (!is_null($cookie)) {
                if ($this->Auth->login($cookie)) {
                    //  Efface le message auth, seulement si nous l'utilisons
                    $this->Session->del('Message.auth');
                    $this->redirect($this->Auth->redirect());
                }
            }
        }
    }
?>

Le code de la fonction login ne s’exécutera pas sauf si vous définissez $autoRedirect à false dans un beforeFilter. Le code présent dans la fonction de login ne s’exécutera qu’après un essai d’authentification. C’est le meilleur endroit pour déterminer si oui ou non une connexion réussie a été effectuée par le Composant Auth (vous aurez peut-être envie d’enregistrer la dernière date d’authentification réussie, etc.).

authorize

Normalement, le Composant Auth essaiera de vérifier que les critères de login que vous avez saisis sont exacts, en les comparant à ce qui a été stocké dans votre modèle utilisateur. Cependant, vous voudrez peut-être certaines fois effectuer du traitement additionnel, en déterminant vos propres critères. En assignant à cette variable l’une des nombreuses valeurs possibles, vous pouvez faire différentes choses. En voici quelques-unes, parmi les plus communes, que vous souhaiterez peut-être utiliser.

<?php
    $this->Auth->authorize = 'controller';
?>

Lorsque authorize est défini à “controller”, vous aurez besoin d’ajouter une méthode appelée isAuthorized() à votre contrôleur. Cette méthode vous permet de faire plus de vérifications d’authentification et de retourner ensuite soit true, soit false.

<?php
    function isAuthorized() {
        if ($this->action == 'delete') {
            if ($this->Auth->user('role') == 'admin') {
                return true;
            } else {
                return false;
            }
        }

        return true;
    }
?>

Souvenez-vous que cette méthode sera inspectée, après que vous ayez déjà passé la vérification d’authentification simple du modèle utilisateur.

<?php
    $this->Auth->authorize = 'model';
?>

Vous ne souhaitez rien ajouter à votre contrôleur et peut-être utiliser les ACO’s ? Vous pouvez demander au Composant Auth d’appeler une méthode, nommée isAuthorized(), dans votre modèle utilisateur, pour faire le même genre de choses :

<?php
    class Utilisateur extends AppModel {
        ...

        function isAuthorized($utilisateur, $controleur, $action) {

            switch ($action) {
                case 'default':
                    return false;
                    break;
                case 'delete':
                    if ($utilisateur['Utilisateur']['role'] == 'admin') {
                        return true;
                    }
                    break;
            }
        }
    }
?>

Enfin, vous pouvez utiliser authorize avec les actions, comme montré ci-dessous :

<?php
    $this->Auth->authorize = 'actions';
?>

En utilisant actions, Auth utilisera l’ACL et vérifiera avec AclComponent::check(). Une fonction isAuthorized n’est pas nésessaire.

<?php
    $this->Auth->authorize = 'crud';
?>

En utilisant crud, Auth utilisera l’ACL et vérifiera avec AclComponent::check(). Les actions devraient correspondre aux CRUD (voir mapActions).

sessionKey

Nom de la clé du tableau de session où l’enregistrement de l’utilisateur actuellement authentifié est stocké.

Par défaut, vaut « Auth », donc si non spécifié, l’enregistrement est stocké dans « Auth.{nom $modeleUtilisateur} ».

<?php
    $this->Auth->sessionKey = 'Autorise';
?>

ajaxLogin

Si vous faites des requêtes Ajax ou Javascript qui nécessitent des sessions authentifiées, donnez à cette variable le nom d’un élément de vue que vous souhaiteriez rendre et retourner quand vous avez une session invalide ou expirée.

Comme dans toute partie de CakePHP, soyez certains d’avoir jeté une œil à la classe AuthComponent dans l’API, pour avoir une vision plus approfondie du composant Auth.

authenticate

Cette variable contient une référence à l’objet responsable du hashage des mots de passe, s’il est nécessaire de changer/surcharger le mécanisme de hashage des mots de passe par défaut. Voyez Changer le type de cryptage pour plus d’info.

actionPath

Si vous utilisez le contrôle d’accès basé sur les actions, ceci définit la façon dont sont déterminés les chemins vers les nœuds ACO de l’action. Si, par exemple, tous les nœuds de contrôleur sont imbriqués sous un nœud ACO nommé “Controllers”, $actionPath devrait être défini à “Controllers/”.