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.