Documentation
Plugin API
Table des matières ▼
Présentation
Temma peut facilement être utilisé pour créer des API web (aussi appelés webservices) de type REST.
Fondamentalement, les API web sont gérées par des contrôleurs, qui reçoivent des données envoyées en paramètres GET ou POST, et qui retournent des données au format JSON.
Ce plugin sert à :
- gérer l'authentification en utilisant des paires de clés publique/privée (voir plus bas) ;
- définir la vue JSON (pour ne pas avoir besoin de la définir dans chaque action) ;
- gérer différentes versions de l'API, qui peuvent éventuellement être utilisable simultanément.
Authentification
Certaines API peuvent être librement accessibles, sans vérification des droits d'accès. Mais la plupart du temps, l'utilisateur qui se connecte à l'API doit s'authentifier, pour vérifier qu'il a bien le droit d'accéder aux fonctionnalités.
Le pluggin API utilise une authentification HTTP Basic, qui repose sur deux clés, une clé "publique" et une clé "privée", qui sont envoyées respectivement comme identifiant et mot de passe à chaque requête sur l'API.
Il est important que le site soit sécurisé par SSL, pour que les clés ne soient pas visibles par quelqu'un qui écouterait les échanges réseau. Les certificats SSL sont aujourd'hui faciles à mettre en place (et gratuits) grâce à la solution Let's Encrypt.
Le choix a été fait de ne pas se baser sur des jetons d'authentification (tels que JWT) pour deux raisons :
-
L'authentification HTTP Basic est la plus simple à implémenter (autant côté client que serveur). Si elle a une mauvaise
réputation du point de vue de la sécurité des échanges, cela date de l'époque où la plupart des communications web n'étaient
pas sécurisées par SSL. Avec des sites en HTTPS, il n'y a pas de souci de sécurité.
Les jetons, quant à eux, nécessitent au minimum une connexion supplémentaire pour la génération du token, lors de laquelle les identifiants (clé publique/privée ou login/mot de passe) sont envoyés au serveur. Cette cinématique n'est pas plus sécurisée, car les identifiants circulent quand même sur le réseau : un hacker qui écouterait les verrait passer (directement ou sous une forme "digest" qui resterait exploitable), même s'ils ne circulent pas à chaque requête. De plus, cela impose pour le client une gestion complexe de la durée d'expiration des jetons, avec des mécanismes de réessais. - Les jetons sont utilisés pour ne pas avoir besoin de revérifier les droits d'accès de l'utilisateur à chaque requête, limitant ainsi les accès à la base de données. Si cela semblait initialement être une bonne idée, de nombreuses applications nécessitent une gestion précise des droits d'accès ; lorsqu'un utilisateur se voit retiré un accès, il n'est pas acceptable qu'il puisse continuer à utiliser l'API jusqu'à ce que son jeton expire. Face à ce genre de situation, la durée de vie des jetons est souvent réduite jusqu'à ce que l'authentification soit revérifiée quasiment à chaque requête. On se retrouve ainsi avec le pire de chaque monde : des accès fréquent à la base de données, mais aussi une cinématique complexe de connexion.
URLs et contrôleurs
Ce plugin est prévu pour gérer des URLs du type /v[version]/[contrôleur]/[action].
Exemples :
- /v1/ : appellera l'action par défaut du contrôleur par défaut
- /v1/articles : appellera l'action par défaut du contrôleur "\v1\Articles"
- /v2/user/list : appellera l'action "list()" du contrôleur "\v2\User"
- /v3/user/remove/123 : appellera l'action "remove()" du contrôleur "\v3\User", en lui fournissant "123" en paramètre
Les contrôleurs doivent donc être placés dans le namespace correspondant au numéro de version de l'API.
Base de données
Ce helper nécessite deux tables en base de données, l'une nommée User (contenant les informations sur les utilisateurs), l'autre nommée ApiKey (contenant les couples de clés publique/privée).
La table User doit contenir les champs suivants :
- id (string) : Clé primaire.
- date_creation (datetime) : Date de création de l'utilisateur.
- date_last_login (datetime) : Date de dernière authentification de l'utilisateur.
- date_last_access (datetime) : Date de dernier accès de l'utilisateur.
- email (string) : Adresse mail de l'utilisateur.
- name (string) : Nom de l'utilisateur (champ obligatoire, même si vous ne l'utilisez pas).
- roles (set) : Rôles assignés à l'utilisateur.
- services (set) : Services auxquels l'utilisateur a accès.
La table ApiKey doit contenir les champs suivants :
- public_key (string) : Clé publique de l'utilisateur.
- private_key (string) : Empreinte numérique (algorithme SHA-256) de la clé privée de l'utilisateur.
- name (string) : Nom de la paire de clés (éventuellement défini par l'utilisateur).
- user_id (int) : Clé étrangère vers l'utilisateur.
Voici un exemple de requête de création de ces tables, dont vous devez personnaliser les champs roles et services de la table User :
CREATE TABLE User (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
date_creation DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
date_last_login DATETIME,
date_last_access DATETIME,
email TINYTEXT CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
name TINYTEXT,
roles SET('admin', 'writer', 'reviewer'), -- à personnaliser
services SET('articles', 'news', 'images'), -- à personnaliser
PRIMARY KEY (id),
UNIQUE INDEX email (email(255))
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE TABLE ApiKey (
public_key CHAR(32) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
private_key CHAR(64) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
name TINYTEXT NOT NULL DEFAULT ('Default'),
user_id INT UNSIGNED NOT NULL,
PRIMARY KEY (public_key),
FOREIGN KEY (user_id) REFERENCES User (id) ON DELETE CASCADE
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
Configuration
Ce plugin doit être l'un des tous premiers pré-plugins dans la chaîne d'exécution.
Il faudra donc ajouter une directive dans le fichier temma.json (voir la documentation de la configuration) :
{
"plugins": {
// liste des pré-plugins
"_pre": [
"\\Temma\\Plugins\\Api"
]
}
}
Configuration de la base de données
Par défaut, les noms des tables et des champs sont ceux indiqué plus haut, et ces tables doivent être placées dans la base ouverte par la connexion nommée db dans la directive de configuration dataSources. Vous avez la possibilité d'indiquer le nom de la base, les nom des tables, ainsi que les noms des champs, s'ils sont différents des noms par défaut :
{
"x-security": {
"auth": {
"userData": {
"base": "auth_app",
"table": "tUser",
"id": "user_id",
"email": "user_mail",
"name": "user_name",
"roles": "user_roles",
"services": "user_services"
},
"apiKeyData": {
"base": "auth_app",
"table": "tKeys",
"public_key": "public_string",
"private_key": "secret_string",
"user_id": "identifier_user"
}
}
}
}
-
Lignes 4 à 12 : Configuration de la table des utilisateurs.
- Ligne 5 : Nom de la base de données qui contient la table.
- Ligne 6 : Nom de la table.
- Ligne 7 : Nom du champ contenant la clé primaire.
- Ligne 8 : Nom du champ contenant l'adresse mail de l'utilisateur.
- Ligne 9 : Nom du champ contenant le nom de l'utilisateur.
- Ligne 10 : Nom du champ contenant les rôles de l'utilisateur.
- Ligne 11 : Nom du champ contenant les services auxquels l'utilisateur a accès.
-
Lignes 13 à 19 : Configuration de la table contenant les clés publique/privée.
- Ligne 14 : Nom de la base de données qui contient la table.
- Ligne 15 : Nom de la table.
- Ligne 16 : Nom du champ contenant l'empreinte de la clé publique.
- Ligne 17 : Nom du champ contenant la clé privée.
- Ligne 18 : Nom du champ contenant l'identifiant de l'utilisateur.
Si la table des utilisateurs contient d'autres champs que vous souhaitez récupérer, vous pouvez les ajouter à la liste. Si les champs ne doivent pas être renommés, mettez le même nom en clé et en valeur :
{
"x-security": {
"auth": {
"userData": {
"email": "user_mail",
"name": "user_name",
"org": "user_organization",
"birthday": "birthday"
}
}
}
}
- Ligne 5 : Définition du nom du champ (user_mail) qui contient l'adresse mail de l'utilisateur.
- Ligne 6 : Définition du nom du champ (user_name) qui contient le nom de l'utilisateur.
- Ligne 7 : Ajout d'un champ organization, renommé en org.
- Ligne 8 : Ajout d'un champ birthday, qui n'est pas renommé.
Configuration de la base de données par DAO
Plutôt que d'utiliser le fichier de configuration (temma.json) pour définir les paramètres concernant la base de données, il est possible d'utiliser des DAO personnalisées.
Exemple de configuration :
{
"x-security": {
"auth": {
"userDao": "\\MyApp\\UserDao",
"apiKeyDao": "\\MyApp\\ApiKeyDao"
}
}
}
Exemple de DAO :
namespace MyApp;
class UserDao extends \Temma\Dao\Dao {
protected $_tableName = 'tUser';
protected $_idField = 'use_i_id';
protected $_fields = [
'user_id' => 'id',
'user_mail' => 'email',
'user_roles' => 'roles',
'user_services' => 'services',
'user_name' => 'name',
'organization',
'birthday',
];
}
Attribut Auth
Temma fournit l'attribut Auth, qui permet de spécifier si un contrôleur ou une action ne peut être accédé que par un utilisateur authentifié (ou non). Cet attribut se basant que la présence d'une variable de template currentUser (ainsi que sur ses clés roles et services), il est pleinement compatible avec ce plugin.
Par exemple, il est possible d'indiquer qu'un contrôleur d'API n'est accessible qu'aux utilisateurs authentifiés :
namespace v1;
use \Temma\Attributes\Auth as TµAuth;
#[TµAuth]
class User extends \Temma\Web\Controller {
// ...
}
Il est possible d'indiquer que certaines actions d'un contrôleur sont en accès libre, alors que d'autres nécessitent d'être authentifié, ou de ne pas être authentifié :
namespace v1;
use \Temma\Attributes\Auth as TµAuth;
class Media extends \Temma\Web\Controller {
// action accessible à tous
public function list() { }
// action accessible uniquement aux
// utilisateurs authentifiés
#[TµAuth]
public function get() { }
// action accessible uniquement aux utilisateurs
// non authentifiés
#[TµAuth(authenticated: false)}
public function remove() { }
}
Il est aussi possible de faire en sorte que des contrôleurs ou des actions ne soient accessibles qu'aux utilisateurs ayant un rôle spécifique ou ayant accès à un service particulier :
namespace v2;
use \Temma\Attributes\Auth as TµAuth;
class Article extends \Temma\Web\Controller {
// accès autorisé uniquement aux utilisateurs
// ayant le rôle "manager"
#[TµAuth('manager')]
public function remove($articleId) { }
// autorisé uniquement aux utilisateurs
// ayant accès au service "images" ou au
// service "text"
#[TµAuth(service: ['images', 'text'])]
public function list() { }
}
Référez-vous à la documentation de l'attribut pour voir toutes ses capacités.
Génération de clés
Vous devez créer les clés publiques et privées de vos utilisateurs pour qu'ils puissent se connecter à votre API. Habituellement, cela se fait via une interface web par laquelle les utilisateurs peuvent demander la génération d'un nouveau couple de clés, ainsi que révoquer les anciennes clés.
Le plugin offre la méthode statique generateKeys(), qui retourne un tableau associatif contenant les clés public et private. La clé publique est une chaîne de 32 caractères de long encodée en base 71 (voir le helper BaseConvert), alors que la clé privée est une chaîne de 64 caractères de long (elle aussi encodée en base 71).
Vous pouvez transmettre ces deux chaînes à l'utilisateur. En base de données, vous devez enregistrer la clé publique
en clair ; par contre, c'est une empreinte numérique (utilisant l'algorithme SHA-256) de la clé privée qui doit
être enregistrée.
Cela implique que l'utilisateur doit copier la clé privée au moment où elle lui est affichée, et qu'il ne pourra
plus la récupérer par la suite (il faut donc lui donner la possibilité de regénérer de nouvelles clés si
nécessaire).
Exemple de code :
// génération des clés
$keys = \Temma\Plugins\Api::generateKeys();
// récupération des clés qui seront affichées à l'utilisateur
$publicKey = $keys['public'];
$privateKey = $keys['private'];
// calcul de l'empreinte numérique de la clé privée
$hashedPrivateKey = hash('sha256', $privateKey);
// enregistrement des clés en base de données
// en utilisant une DAO préalablement créée
$apiKeyDao->create([
'public_key' => $publicKey,
'private_key' => $hashedPrivateKey,
'user_id' => $userId,
]);
Il est possible de donner un nom à chaque paire de clés, pour permettre aux utilisateurs de les gérer
(par exemple générer des clés pour différents usages, et avoir la possiblité d'effacer certaines
clés spécifiques). Si aucun nom n'est fourni, le nom par défaut sera utilisé.
Pour nommer la paire de clé, il suffit d'ajouter le nom au moment où la paire est enregistrée
en base de données :
// enregistrement de la paire de clés, avec un nom associé
$apiKeyDao->create([
'public_key' => $publicKey,
'private_key' => $hashedPrivateKey,
'user_id' => $userId,
'name' => $keyName,
]);
Précédent : | Helper contrôleur Auth |
Suivant : | Helper plugin Cache |
Table des matières
- Migration : Comment passer de Temma 1.x à la version 2
- Installation : Télécharger Temma et l'installer pour démarrer votre projet Web
- Configuration : Toutes les directives de configuration du fichier etc/temma.json et les variables d'environnement utilisables en option
- Bibliothèques externes : Comment utiliser des bibliothèques de fonctions externes
- Routage : Le système de routage par défaut de Temma, et le routage avancé
- Log : Utilisation du système de log, gestion par niveaux de criticité
- Contrôleurs : Pièces essentiels de votre application Web
- Vues : Templates Smarty ou exports JSON/CSV/RSS/iCal/INI
- Injection de dépendances : La colonne vertébrale de vos développements applicatifs
- Sessions : Extension des sessions utilisateurs gérées par PHP
- Sources de données : Pour gérer l'accès aux données de manière unifiée
- Modèle : Comment utiliser les DAO pour accéder aux bases de données
- Flux d'exécution : Comment gérer le flux d'exécution, entre les plugins et le contrôleur
- Plugins : Comment utiliser les plugins, et écrire les vôtres pour modulariser votre code
- Attributs : Comment filtrer l'accès aux contrôleurs et aux actions
- Tests : Pour écrire des tests d'intégration automatisés.
- Interface en ligne de commande : Pour créer des scripts exécutables en ligne de commande, initialisés automatiquement par Temma
-
Helpers :
Objets proposés par Temma pour vous aider dans plusieurs circonstances
- Scripts en ligne de commande
-
Contrôleur + plugin
- Auth : Contrôleur et plugin servant à gérer l'authentification des utilisateurs
- Plugins
- Attributs
-
Plugins Smarty
- urlize : Modificateur transformant un texte en URL
- filenamize : Modificateur transformant un texte en nom de fichier
- nbsp : Modificateur transformant des espaces en espaces non sécables
-
Objets utilitaires
- ANSI : Pour mettre en forme les textes écrits sur la sortie standard
- BaseConvert : Pour faire des conversions de bases numériques
- DataFilter : Pour filtrer et valider des données
- Email : Pour envoyer des emails
- HTMLCleaner : Pour nettoyer un flux HTML provenant d'un éditeur WYSIWYG
- IniExport : Pour exporter des données au format INI
- Json : Pour lire des flux JSON pouvant contenir des commentaires
- Lock : Pour verrouiller l'accès à un fichier, ou l'exécution du script PHP courant
- Registry : Pour stocker proprement des variables globales
- Smarty : Pour traiter des templates Smarty en dehors de la vue
- Term : Gestion des terminaux (TTY)
- Text : Différents traitements sur les chaînes de caractères
- Timer : Pour gérer des chronomètres