badge Dotclear 2.36

Les jetons d'accés des utilisateurs à des services tiers doivent pouvoir être gérés par utilisateur mais également par service, les tables existantes de Dotclear ne proposaient pas de solution adéquate. Une nouvelle table credential et sa classe App::credential() sont donc disponibles depuis Dotclear 2.36.

Préambule

  • La classe de gestion de jeton est très classique de ce qui se fait dans Dotclear.
  • La classe est toujours disponible directement dans Dotclear.
  • Si un utilisateur est supprimé, ses jetons seront également supprimés (cascade)
  • Si un blog est supprimé les jetons qui lui sont liés seront également supprimés (cascade)

Attention : Les données sont cryptées avant d'être enregistrées dans la base de données, si vous perdez la "Master Key" de votre installation, toutes ces données seront illisibles et donc perdues.

Schéma de la table


$structure->credential
            ->field('user_id', 'varchar', 32, false)
            ->field('blog_id', 'varchar', 32, true, null)
            ->field('credential_dt', 'timestamp', 0, false, 'now()')
            ->field('credential_type', 'varchar', 32, false)
            ->field('credential_value', 'varchar', 255, false, "''")
            ->field('credential_data', 'text', 0, true, null)

            ->unique('uk_credential', 'credential_type', 'user_id', 'blog_id', 'credential_value')


$structure->credential
            ->index('idx_credential_user_id', 'btree', 'user_id');
$structure->credential
            ->index('idx_credential_blog_id', 'btree', 'blog_id');
$structure->credential
            ->reference('fk_credential_user', 'user_id', 'user', 'user_id', 'cascade', 'cascade')
$structure->credential
            ->reference('fk_credential_blog', 'blog_id', 'blog', 'blog_id', 'cascade', 'cascade');

Définition de son interface


namespace Dotclear\Interface\Core;

use ArrayObject;
use Dotclear\Database\Cursor;
use Dotclear\Database\MetaRecord;

/**
 * @brief   User credentials handler interface.
 *
 * Use this class to store user credential
 * for third party applications.
 *
 * @author  Jean-Christian Paul Denis
 * @since   2.36
 */
interface CredentialInterface
{
    /**
     * Credential table name.
     *
     * @var    string  CREDENTIAL_TABLE_NAME
     */
    public const CREDENTIAL_TABLE_NAME = 'credential';

    /**
     * Open a credential database table cursor.
     *
     * @return  Cursor  The credential database table cursor
     */
    public function openCredentialCursor(): Cursor;

    /**
     * Get credentials.
     *
     * @param      array<string, mixed>|ArrayObject<string, mixed>  $params      The parameters
     *
     * @return     MetaRecord  The users.
     */
    public function getCredentials(array|ArrayObject $params = []): MetaRecord;

    /**
     * Set user credential.
     *
     * @param     string     $user_id     The user ID
     * @param     Cursor     $cur         The credential Cursor
     */
    public function setCredential(string $user_id, Cursor $cur): void;

    /**
     * Delete credentials.
     *
     * If <var>$global</var> is set to False, current blog is selected.
     *
     * @param   string          $credential_type    The credential type
     * @param   string          $credential_value   The credential value
     * @param   null|string     $user_id            The user_id (or null for all users)
     * @param   bool            $global             True for global or false for current blog
     */
    public function delCredentials(string $credential_type, string $credential_value = '', ?string $user_id = null, bool $global = true): void;

    /**
     * Encrypt data.
     *
     * This is used to protect sensible data in database.
     * Data encryption is linked to Dotclear's master key.
     *
     * @param   ArrayObject<string, mixed>|array<string, mixed>     $data   The credential data to encode
     *
     * @return  string  The encoded credential data
     */
    public function encryptData(array|ArrayObject $data): string;

    /**
     * Decrypt data.
     *
     * Decode data encoded with self::encreyptData()
     *
     * @param   string  $data   The encoded credential data
     *
     * @return  array<string, mixed> The decoded credential data
     */
    public function decryptData(string $data): array;
}

Recherche de jetons

Les paramètres de recherche de la méthode getCredentials() sont :

  • user_id : optionnel : l'utilisateur du jeton
  • blog_id : optionnel : la blog courant (si ommit ce sera global)
  • credential_type : optionnel : généralement le nom du service associé au jeton (si ommit ce sera "webauthn")
  • credential_value : optionnel : une valeur mise en avant du jeton
  • order : optionnel : parametre de trie
  • limit : optionnel : limite de nombre de résultats

Les données du champs credential_data sont cryptées lors de l'enregistrement d'un champs, pour les décrypter il faut utiliser la méthode $rs->getAllData(); de l'enregistrement retourné.

$rs = App::credential()->getCredentials([
    'credential_type' => 'plop',
    'user_id' => App::auth()->userID(),
]);
if (!$rs->isEmpty()) {
    // tableau complet du jeton
    $data = $rs->getAllData();
    // ou
    $un_morceau_de_jeton = $rs->getData('un_morceau_de_jeton');
}

Ajout de jeton

L'ajout du jeton se fait à la manière de Dotclear, en ouvrant un curseur et en y incluant les informations qu'on désire.

$cur = App::credential()->openCredentialCursor();
$cur->setField('blog_id', App::blog()->id());
$cur->setField('credential_type', 'mon_service');
$cur->setField('credential_value', 'XYZEFFRE1234');
$cur->setField('credential_data', new ArrayObject($mon_tableau_du_jeton));
App::credential()->setCredential((string) App::auth()->userID(), $cur);

On remarque que pour le champs credential_data on crée un objet, cela permet de le manipuler dans la classe credential et de pouvoir le crypter lors de l'enregistrement dans la base de données.

Supression de jeton

Le suppression de jetons se fait en empilant les critères suivant :

  • credential_type : obligatoire.
  • credential_value : obligatoire, si ommit on cherche une valuer vide
  • user_id : optionnel
  • global : si vrai on recherche une valeur null de blog, si faux on recherche le blog courant
App::credential()->delCredentials(
    'mon_service',
    'une_clé',
    null,
    false
);

Dans cet exemple on supprimer le jeton de valeur 'une_clé' de type 'mon_service' sur le blog courant et peu importe l'utilisateur.

Conclusion

Rien d'extraordinaire sur cette classe, il suffit juste de réspecter les quelques recommandations présenteés ici.