Formulaire login/mot de passe et gestion des utilisateurs pour une application liée à une base de données

Chrispi

Ce tutoriel propose une solution d'identification-authentification développée avec Lazarus pour une application fonctionnant sous Windows. L'application qui permet aussi le changement de mot de passe ainsi que la gestion des utilisateurs est liée à une base de données sans pour autant en être l'administratrice. Cependant, la base devient aussi le lieu de stockage des identifiants et des mots de passe.

Commentez

- Titre : Formulaire login/mot de passe et gestion des utilisateurs pour une application liée à une base de données

- Auteur : Chrispi

- Parution : 26 mai 2021

- Public visé :

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2021 Chrispi. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. La base de données

- III. Création des unités et des fiches

- IV. Composants et aménagement des formulaires et fiches

- IV-A. La fiche Data

- IV-B. Le formulaire de connexion

- IV-C. La fiche de l'application

- IV-D. Le formulaire de changement de mot de passe

- IV-E. Le formulaire de gestion des utilisateurs

- V. Le code

- V-A. Ordonnancement des fiches

- V-B. Le code de la fiche Data

- V-C. Le code du formulaire de connexion

- V-D. Le code de l'application

- V-E. Le code du formulaire de changement de mot de passe

- V-F. Le code du formulaire de gestion des utilisateurs

- V-F-1. Création d'un utilisateur

- V-F-2. Suppression d'un utilisateur

- VI. Conclusion

- VII. Remerciements

I - Introduction

Débutant : avant de passer à ce tutoriel, il est préférable d’étudier le tutoriel « Gestion d'une base de données MySQL » d’Alcatiz.

https://alcatiz.developpez.com/tutoriel/lazarus-mysql/

Ce tutoriel destiné initialement à élaborer un formulaire de connexion par mot de passe pour limiter et sécuriser l’accès à une application liée à une base de données, vous amènera à lui associer d'autres formulaires et à rendre l'accès à l'application encore plus sûr. Il vous est proposé de créer une application factice dont vous pourrez réutiliser les codes dans vos propres créations. Ici, ne seront pas reprises les premières étapes de création d’une application avec Lazarus.

Ce tutoriel est inspiré de celui pour Microsoft Access, « Comment concevoir un formulaire login/mot de passe ? » réalisé par Morsi, ainsi que de celui de Denis Hulo, « Créer une gestion des droits des utilisateurs avec Access ».

https://access.developpez.com/faq/?page=TAAdmin#login

https://denishulo.developpez.com/tutoriels/access/gestion-droits-utilisateurs/

Le choix s’est porté sur une base de données de type MariaDB administrée avec phpMyAdmin.

Pour que l’application fonctionne, il faut que le serveur et la base de données soient actifs.

Pour utiliser MariaDB, il est nécessaire de mettre le fichier libmysql.dll dans le dossier contenant l’application. Il faut aussi utiliser le composant TmySQLxxConnection adapté à la version de MariaDB ainsi que bien renseigner le port.

Comme stipulé dans la publication de Morsi, un formulaire login/mot de passe permet à un utilisateur de s'identifier et à la base de le reconnaître et de décider de lui donner ou non la permission d'accéder aux données.

Donc, nous créerons un formulaire de connexion « login/mot de passe » pour sécuriser l'accès à l'application. Ensuite, nous réaliserons aussi un formulaire permettant aux utilisateurs de changer leur mot de passe comme cela est souvent demandé de le faire lors d'une première connexion. Enfin, nous irons un peu plus loin en élaborant un formulaire de gestion des utilisateurs de l'application. Avec celui-ci, un administrateur pourra inscrire des utilisateurs ou bien en supprimer. Mais avant tout, commençons par nous occuper de la base de données.

II - La base de données

Au niveau de la base de données, il faut créer la table t_user avec les colonnes : id, trigramme, nom, prenom, groupe, paswd. Tous les champs sont de type « VARCHAR » sauf id qui est de type « INT ».

Exemple d’enregistrement dans cette table : « ‘1’,‘gdéon’,‘DEON’,‘Gérard’,‘Util’, ‘GlaCléde12!’ ».

Code SQL pour créer la table :

CREATE TABLE `t_user` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`trigramme` VARCHAR(20) NOT NULL,
`nom` VARCHAR(20) NOT NULL,
`prenom` VARCHAR(20) NOT NULL,
`groupe` VARCHAR(20) NOT NULL,
`paswd` VARCHAR(20) NOT NULL,
PRIMARY KEY (`id`)
)
COLLATE='latin1_general_cs';

III - Création des unités et des fiches

Après avoir enregistré le projet, nous passons à la création des unités et des fiches (formes) :

- Data et l'unité datamodule pour la fiche de liaison à la base de données ;

- FormLogin et l'unité login pour le formulaire de connexion ;

- FormMain et l'unité main pour la fiche support de l'application ;

- FormChgt et l'unité chgtmdp pour le formulaire de changement de mot de passe ;

- FormGutil et l'unité gutil pour le formulaire de gestion des utilisateurs.

Pour Data, il faut créer un datamodule et pas une fiche classique.

IV - Composants et aménagement des formulaires et fiches

Dans ce chapitre, nous constituons chaque formulaire et fiche avec les composants nécessaires. Nous procédons à leur disposition et nous paramétrons certaines de leurs propriétés.

IV-A - La fiche Data

Composants

Propriétés

TData

Name : Data

TMySQL56Connection

DatabaseName : Nom de la base
Name : SQLConnect
Password : Mot de passe
Port : (en fonction de MariaDB)
Transaction : SQLTransact
UserName : Nom d'utilisateur de la base
HostName : Nom d'hôte

TSQLTransaction

Database : SQLConnect
Name : SQLTransact

TSQLQuery

Database : SQLConnect
Name : SQLreqMDP
Transaction : SQLTransact

TSQLQuery

Database : SQLConnect
Name : SQLreqNMDP
Transaction : SQLTransact

TSQLQuery

Database : SQLConnect
Name : SQLreqGutil
Transaction : SQLTransact

IV-B - Le formulaire de connexion

Composants

Propriétés

TForm

BorderStyle :bsToolWindow
Caption : Connexion
Color : clActiveCaption
Name : FormLogin
Position : poScreenCenter

TButton

Caption : Entrer
Name : BtnEntr

TButton

Caption : Annuler
Name : BtnAnnul1

TEdit

MaxLength : 20
Name : EdMdp1
Text : Effacer le contenu
PasswordChar : *

TLabel

Caption : Identifiant
Name : LbIdent

Tlabel

AutoSize : False
Caption : Effacer le contenu
Color : clWhite et Ajuster la position et la taille du composant
Name : LbUser
Transparent : False

TLabel

Caption : Mot de passe
Name : LbMdp

TLabel

Caption : 3 essais maximum
Name : LbEss

IV-C - La fiche de l'application

Composants

Propriétés

TForm

Caption : MDPtuto (à vous de choisir)
Name : FormMain
Position : poScreenCenter

TLabel

Caption : Changement mot de passe
Font → Style → fsUnderline : (True)
Name : LbChgt

TLabel

Caption : Gestion des utilisateurs
Font → Style → fsUnderline : (True)
Name : LbGest

IV-D - Le formulaire de changement de mot de passe

Composants

Propriétés

TForm

BorderStyle : bsToolWindow
Caption : Changement mot de passe
Color : clActiveCaption
Name : FormChgt
Position : poScreenCenter

TButton

Caption : Valider
Name : BtnValid

TButton

Caption : Annuler
Name : BtnAnnul2

TEdit

Name : EdNmdp
Text : Effacer le contenu
PasswordChar : *

TEdit

Name : EdConf
Text : Effacer le contenu
PasswordChar : *

TLabel

Caption : Nouveau mot de passe
Name : LbNmdp

TLabel

Caption : Confirmation mot de passe
Name : LbConf

IV-E - Le formulaire de gestion des utilisateurs

Composants

Propriétés

Tform

Caption : Gestion des utilisateurs
Color : clActiveCaption
Name : FormGutil
Position : poScreenCenter

TButton

Caption : Créer
Name : BtnCrea

TButton

Caption : Supprimer
Name : BtnSup

TButton

Caption : Annuler
Name : BtnAnnul3

TEdit

Name : EdIdentU
Text : Effacer le contenu

TEdit

Name : EdNomU
Text : Effacer le contenu

TEdit

Name : EdPrenU
Text : Effacer le contenu

TEdit

Name : EdMdpU
Text : Effacer le contenu

TComboBox

Item: Admin
Util
Name : ComboGpe
Style : csDropDownList
Text : Effacer le contenu

TLabel

Caption : Identifiant util
Name : LbIdentU

TLabel

Caption : Nom util *
Name : LbNomU

TLabel

Caption : Prénom util *
Name : LbPrenU

TLabel

Caption : Groupe util
Name : LbGpeU

TLabel

Caption : Mot de passe util *
Name : LbMdpU

TLabel

Caption : (* Non nécessaire pour supprimer)
Name : LbInfoSup

V - Le code

Les fiches prêtes avec leurs composants paramétrés, nous passons maintenant au code. Nous commençons par l'ordonnancement des fiches, puis nous nous attaquerons à l'élaboration du code de chacune d'elles.

Il faudra penser à mettre Dialogs dans les clauses uses des unités où sera utilisé ShowMessage et en plus LCLType où sera utilisé MessageBox.

V-A - Ordonnancement des fiches

Comme le veut une bonne pratique, nous ne chargeons pas toutes les fiches au démarrage de l'application. Donc, nous allons les ordonner en affichant dans Lazarus le code source en cliquant sur Projet et ensuite Voir le code source. Nous le modifions pour que ce soit le formulaire de connexion FormLogin qui apparaisse le premier au lancement de l'application. Les autres fiches ne seront pas chargées d'emblée, exceptée Data. Dans le respect de la logique de connexion préalable à la base de données, Data sera créée avant FormLogin.

program MDPtuto;

{$mode objfpc}{$H+}

uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, main, datamodule, login, chgtmdp, gutil, lazdbexport;

{$R *.res}

begin
RequireDerivedFormResource:= True;
Application.Title:= 'MDPtuto';
Application.Scaled:= True;
Application.Initialize;
Application.CreateForm(TData,Data);
FormLogin:= TFormLogin.Create(Application);
if FormLogin.ShowModal= 1 then
Application.Run
else
Formlogin.Close; //Libération pour éviter les fuites de mémoire
end.

V-B - Le code de la fiche Data

La fiche Data constitue une véritable « interface » entre l'application, ses formulaires et la base de données. Le code concernant cette fiche est créé dans l'unité datamodule. Nous allons préparer l'unité pour accueillir les procédures et fonctions au fur et à mesure de la construction des formulaires.

Nous déclarons les variables et les fonctions qui seront utilisées pour la connexion, le changement de mot de passe et la gestion des utilisateurs.

public
User: String;
gpe: String;
function Connexion(cMdp: String): boolean;
function Testmdp(cNmdp: String): boolean;
function Testgpu(cutil,cgpu: String): boolean;
function Testmdpu(cutil,cmdpu: String): boolean;
function Changement(cNmdp: String): boolean;
function Creatutil(cutil,cnomu,cprenu,cgpu,cmdpu: String): boolean;
function Suputil(cutil,cgpu: String): boolean;

Pour pouvoir procéder par la suite à l'identification-authentification de l'utilisateur, nous devons préalablement récupérer son identifiant de session.

procedure TData.DataModuleCreate(Sender: TObject);
begin
User:= Application.EnvironmentVariable['USERNAME']; //Récupération de l'identifiant utilisateur
end;

V-C - Le code du formulaire de connexion

La fiche FormLogin constitue le formulaire qui permet de se connecter à l'application via une identification-authentification par identifiant et mot de passe en lien avec la table t_user. Pour plus de sécurité, à la troisième tentative de connexion erronée l'application se ferme. Le mot de passe pouvant être saisi dans le champ dédié doit être composé de 20 caractères maximum. Le code concernant ce formulaire est créé dans l'unité login.

Cette fiche doit être liée à la base de données grâce à Data et à FormMain pour l'application. Donc, nous déclarons les clauses uses comme suit :

implementation

{$R *.lfm}

uses
datamodule, main;

Pour procéder à l'identification-authentification, la connexion nécessite l'identifiant de session de l'utilisateur du PC. Sa récupération étant faite au niveau du datamodule, son affichage se fait avec le composant LbUser dans le formulaire dès sa création.

procedure TFormLogin.FormCreate(Sender: TObject); //Récupération des éléments d'identification-authentification
begin
LbUser.Caption:= Data.User; //Recueil de l'identifiant utilisateur
end;

Pour compter les tentatives de connexion nous devons élaborer la procédure Tentative que nous déclarons comme suit :

TFormLogin = class(TForm)
BtnEntr: TButton;
BtnAnnul1: TButton;
EdMdp: TEdit;
LbEss: TLabel;
LbMdp: TLabel;
LbUser: TLabel;
LbIdent: TLabel;
procedure BtnAnnul1Click(Sender: TObject);
procedure BtnEntrClick(Sender: TObject);
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure Tentative(tentatives: word);

Puis nous déclarons la variable tentatives qui sera utilisée dans la procédure de comptage et de qualification des tentatives de connexion et Mdp dans la fonction de connexion.

private
tentatives: word;
Mdp: String;

Nous pouvons maintenant construire la procédure Tentative qui comptera et qualifiera les tentatives de connexion. Cette procédure impose qu'après la troisième tentative erronée, le formulaire de connexion se ferme ainsi que l'application.

procedure TFormLogin.Tentative(tentatives: word); //Procédure de comptage des tentatives de connexion
begin
case tentatives of
0 : ShowMessage('Erreur de mot de passe. Deuxième essai.'); //Info 2e essai
1 : ShowMessage('Erreur de mot de passe. Attention ! Dernier essai.'); //Info dernier essai
2 : FormLogin.Close; //Ferme le formulaire de connexion
end;
end;

L'identification-authentification se fait par un clic sur le bouton Entrer qui appelle la fonction de connexion que nous élaborerons dans l'unité datamodule. C'est également à ce niveau que le mot de passe de l'utilisateur est récupéré. Le clic sur le bouton appelle aussi l'incrémentation des tentatives de connexion quand il y a une erreur.

procedure TFormLogin.BtnEntrClick(Sender: TObject); //Connexion
begin
Mdp:= EdMdp.Text; //Récupération mot de passe
if Data.Connexion(Mdp)= True then //Appel fonction de connexion
begin
FormMain:= TFormMain.Create(Application); //Création de la forme de l'application
FormMain.Show; //Montre la forme de l'application
FormLogin.Hide; //Cache le formulaire de connexion
end
else
FormLogin.Tentative(tentatives); //Appel procédure comptage des tentatives de connexion
inc(tentatives); //Incrémentation des tentatives de connexion
end;

Quand il y a une erreur de mot de passe, un message apparaît. Quand l'utilisateur clique sur OK ou ferme le message, il revient au formulaire de connexion pour modifier la saisie du mot de passe et essayer à nouveau de se connecter. Voici les messages :

Le bouton Annuler annule et ferme le formulaire de connexion. La fermeture du formulaire par le bouton Annuler ou par la croix X provoque la fermeture de l'application.

procedure TFormLogin.BtnAnnul1Click(Sender: TObject); //Annulation de la connexion
begin
Close;
end;

À la fermeture du formulaire, nous libérons la mémoire.

procedure TFormLogin.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
CloseAction:= caFree; //Libère la mémoire
end;

Au niveau de l'unité datamodule, nous construisons la fonction Connexion qui interroge la base de données pour y rechercher l'utilisateur et lui ouvrir l'application. Nous profitons de la connexion pour récupérer le groupe d'appartenance de l'utilisateur lié au mot de passe. Ceci sera déterminant pour donner l'accès ou non au formulaire de gestion des utilisateurs. Si son statut d'utilisateur est administrateur « Admin », il y aura accès contrairement à un simple utilisateur « Util ».

function TData.Connexion(cMdp: String): boolean; //Fonction de connexion
begin
//Test de présence de l'utilisateur dans la table t_user selon son mot de passe
result:= False;
SQLreqMDP.Close;
SQLreqMDP.SQL.Text:='SELECT trigramme,paswd,groupe FROM t_user WHERE trigramme =:t AND paswd =:m;';
SQLreqMDP.ParamByName('t').asString:= User;
SQLreqMDP.ParamByName('m').asString:= cMdp;
SQLreqMDP.Open;
if SQLreqMDP.FieldBYName('groupe').asString <> '' then
result:= True; //Utilisateur présent
gpe:= SQLreqMDP.FieldBYName('groupe').asString; //Récupération du statut de l'utilisateur
end;

V-D - Le code de l'application

La fiche FormMain constitue le support de l'application. L'application est appelée depuis le formulaire de connexion après identification-authentification. Le code concernant l'application est créé dans l'unité main.

Depuis la fiche FormMain sont appelés les formulaires de changement de mot de passe et de gestion des utilisateurs. Cette fiche doit être liée à ces formulaires, mais aussi avec Data. Donc nous déclarons les clauses uses comme suit :

implementation

{$R *.lfm}

uses
datamodule, chgtmdp, gutil;

L'appel du formulaire de changement de mot de passe FormChgt se fait par un clic sur le composant LbChgt (Changement mot de passe) qui ouvre une boîte message énonçant les règles de composition d'un mot de passe. Elle donne le choix d'accéder au formulaire de changement de mot de passe (clic sur OK) ou de retourner à l'application (clic sur Cancel).

procedure TFormMain.LbChgtClick(Sender: TObject); //Montre le message de composition des mots de passe
var
reply: integer;
boxstyle: integer;
begin
with application do
begin
boxstyle:= MB_ICONINFORMATION + MB_OKCANCEL;
reply:= MessageBox('Pour être valide, le nouveau mot de passe doit avoir une longueur comprise entre 8 et 20 caractères. Il doit obligatoirement contenir une majuscule, une minuscule, un chiffre et un caractère spécial.', 'Règles de composition du mot de passe', boxstyle);
if reply = IDOK then
begin
FormChgt:= TFormChgt.Create(Application); //Création du formulaire de changement de mot de passe
FormChgt.ShowModal; //Montre le formulaire
end
else
FormMain.Show; //Retourne à l'application
end;
end;

Voici le message préalable à l'accès au formulaire de changement de mot de passe :

L'appel du formulaire de gestion des utilisateurs FormGutil se fait par un clic sur le composant LbGest (Gestion des utilisateurs). L'ouverture du formulaire est conditionnée par l'appartenance de l'utilisateur au groupe administrateur « Admin ». Cette condition est recherchée au niveau de la fonction Connexion dans l'unité datamodule. C'est lors de l'utilisation de celle-ci qu'est récupéré le groupe (statut) dans lequel ce dernier est inscrit.

procedure TFormMain.LbGestClick(Sender: TObject); //Ouvre le formulaire de gestion des utilisateurs
begin
if Data.gpe <> 'Admin' then
ShowMessage('Vous ne possédez pas les droits pour cette fonctionnalité.')
else
begin
FormGutil:= TFormGutil.Create(Application); //Création du formulaire de gestion des utilisateurs
FormGutil.ShowModal; //Montre le formulaire
end;
end;

Si l'utilisateur n'a pas les droits d'administrateur pour accéder au formulaire de gestion des utilisateurs, un message l'avertit. À la fermeture du message, l'utilisateur revient à l'application.

Pour fermer l'application, en cliquant sur la croix X, nous utilisons le code classique suivant :

procedure TFormMain.FormClose(Sender: TObject; var CloseAction: TCloseAction); //Fermeture de l'application
begin
Application.Terminate;
end;

V-E - Le code du formulaire de changement de mot de passe

La fiche FormChgt constitue le formulaire qui permet à un utilisateur de changer son mot de passe. Ce formulaire est appelé depuis l'application. Dans celui-ci, l'utilisateur doit saisir son nouveau mot de passe en respectant des règles strictes de saisie et le confirmer. Ce mot de passe doit être composé de 8 à 20 caractères, dont au moins une majuscule, une minuscule, un chiffre et un caractère spécial. Le code concernant ce formulaire est créé dans l'unité chgtmdp.

Nous déclarons la variable Nmdp qui sera utilisée dans la fonction de changement de mot de passe ainsi que les fonctions Testchamp et Testchar qui vérifieront la conformité de celui-ci. Nous déclarons aussi la procédure Procrazchgt que nous utiliserons pour effacer les champs de saisie du formulaire.

private
Nmdp: String;
function Testchamp(cNmdp: String): boolean;
function Testchar(cNmdp: String): boolean;
procedure Procrazchgt(Form: TForm);

Nous déclarons les clauses uses qui permettront de lier le formulaire de changement de mot de passe à celui de connexion et à Data ainsi qu'à RegExpr qui permet la vérification de la composition du nouveau mot de passe en termes de types de caractères.

implementation

{$R *.lfm}

uses
login, datamodule, RegExpr;

Nous élaborons la procédure d'effacement des champs de saisie du formulaire constitués par les composants EdNmdp et EdConf.

procedure TFormChgt.Procrazchgt(Form: TForm); //Procédure d'effacement du contenu des champs du formulaire
var
I: Integer;
begin
for I:= 0 to Form.ComponentCount - 1 do
if Form.Components[I] is TEdit then
TEdit(Form.Components[I]).Text:= '';
end;

Nous créons la fonction Testchamp qui vérifiera la complétude des champs de saisie du nouveau mot de passe et de sa confirmation selon les critères de présence, de taille, mais aussi de différence avec celui jusqu'alors employé par l'utilisateur. Cette fonction sera utilisée dans la procédure de validation du changement de mot de passe.

function TFormChgt.Testchamp(cNmdp: String): boolean; //Fonction de vérification du contenu des champs
begin
result:= False;
if cNmdp = '' then
ShowMessage('Absence de nouveau mot de passe.')
else
if EdConf.Text = '' then
ShowMessage('Absence de confirmation du mot de passe.')
else
if cNmdp <> EdConf.Text then
ShowMessage('Mot de passe et confirmation différents.')
else
if Length(cNmdp) < 8 then
ShowMessage('Longueur minimale du mot de passe non conforme.')
else
if Length(cNmdp) > 20 then
ShowMessage('Longueur maximale du mot de passe non conforme.')
else
if cNmdp = FormLogin.EdMdp.Text then
ShowMessage('Mot de passe identique à celui utilisé précédemment.')
else
result:= True;
end;

Nous construisons ensuite la fonction Testchar qui permettra de s'assurer de la composition en termes de majuscule, de minuscule, de chiffre et de caractère spécial du nouveau mot de passe saisi par l'utilisateur. Comme la fonction précédente, elle sera également utilisée dans la procédure de validation du changement de mot de passe.

function TFormChgt.Testchar(cNmdp: String): boolean; //Fonction de vérification des caractères du nouveau mot de passe
var
Expr: TRegExpr;
const spchar = ',;:!§£€$~#{|`^@{}_&é"èçà<>%ù²'; //Caractères spéciaux
const escspchar = '\.\?\=\[\]\+\-\*\(\)'; //Autres caractères spéciaux
begin
result:= False;
Expr:=TRegExpr.Create;
Expr.Expression := '[A-Z]+';
if not Expr.Exec(cNmdp) then //Absence de majuscule
begin
ShowMessage('Absence de majuscule.');
exit;
end
else
Expr.Expression := '[a-z]+';
if not Expr.Exec(cNmdp) then //Absence de minuscule
begin
ShowMessage('Absence de minuscule.');
exit;
end
else
Expr.Expression := '\d+';
if not Expr.Exec(cNmdp) then //Absence de chiffre
begin
ShowMessage('Absence de chiffre.');
exit;
end
else
Expr.Expression := '['+spchar+escspchar+']+';
if not Expr.Exec(cNmdp) then //Absence de caractère spécial
begin
ShowMessage('Absence de caractère spécial.');
exit;
end
else
result:= True; //Conformité de types de caractères dans le nouveau mot de passe
end;

La fonction de changement de mot de passe est appelée par le bouton Valider. La validation passe préalablement par la série de contrôles définis dans les fonctions Testchamp, Testchar et Testmdp. Ces tests passés avec succès, le changement peut être lancé. Le test de mot de passe non identique à celui d'un autre compte de l'utilisateur fait l'objet de la fonction Testmdp que nous élaborerons dans l'unité datamodule.

procedure TFormChgt.BtnValidClick(Sender: TObject); //Appel changement de mot de passe
begin
Nmdp:= EdNmdp.Text;
if (Testchamp(Nmdp)= False) or (Testchar(Nmdp)= False) then
exit;
if Data.Testmdp(Nmdp)= False then //Teste si le mot de passe est non identique à celui d'un autre compte de l'utilisateur
ShowMessage('Mot de passe déjà utilisé dans un de vos profils.')
else
if Data.Changement(Nmdp)= True then //Appel conditionnel de la fonction de changement de mot de passe
begin
ShowMessage('Changement de mot de passe réussi.');
FormChgt.Close; //Ferme le formulaire quand changement réussi
end;
end;

Pour chaque problème de conformité de complétude des champs, un message informe l'utilisateur. Le message correspond à la première erreur rencontrée à chaque tentative de validation. À la fermeture, l'utilisateur se retrouve de nouveau sur le formulaire pour corriger ou renoncer à son changement de mot de passe. Exemple de message d'erreur :

De même, pour chaque problème de conformité de la composition du nouveau mot de passe, un message informe l'utilisateur. Le message correspond à la première erreur rencontrée à chaque tentative de validation. À la fermeture, l'utilisateur se retrouve de nouveau sur le formulaire pour corriger ou renoncer à son changement de mot de passe. Exemple de message d'erreur :

Si le nouveau mot de passe est identique à celui d'un autre de ses profils, un message prévient l'utilisateur. À la fermeture, il est ramené au formulaire pour modifier le mot de passe. Voici le message :

Quand le changement de mot de passe est réussi, un message en informe l'utilisateur. À la fermeture du message, le formulaire de changement de mot de passe est fermé et l'utilisateur se retrouve de nouveau sur l'application.

Si l'utilisateur désire abandonner son changement de mot de passe, un clic sur le bouton Annuler ferme le formulaire et il se retrouve de nouveau sur l'application. La fermeture du formulaire par la croix X ramène aussi à l'application.

procedure TFormChgt.BtnAnnul2Click(Sender: TObject); //Abandon changement mot de passe
begin
Close;
end;

Dans le cas où l'utilisateur souhaite à nouveau changer de mot de passe après avoir interrompu sa démarche précédente, la procédure d'effacement des champs de saisie est appelée à la réapparition du formulaire.

procedure TFormChgt.FormShow(Sender: TObject); //Effacement des champs du formulaire de Changement de mot de passe
begin
Procrazchgt(Self);
end;

À la fermeture du formulaire, nous libérons la mémoire.

procedure TFormChgt.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
CloseAction:= caFree; //Libère la mémoire
end;

Nous avons vu précédemment que nous devons créer la fonction Testmdp. Nous nous rendons dans l'unité datamodule pour le faire.

function TData.Testmdp(cNmdp: String): boolean; //Fonction de test de mot de passe non identique à celui d'un autre compte de l'utilisateur
begin
//Vérifie si le mot de passe n'est pas employé par l'utilisateur pour un de ses autres profils
result:= False;
SQLreqNMDP.Close;
SQLreqNMDP.SQL.Text:='SELECT trigramme,paswd,groupe FROM t_user WHERE trigramme =:t AND paswd =:m;';
SQLreqNMDP.ParamByName('t').asString:= User;
SQLreqNMDP.ParamByName('m').asString:= cNmdp;
SQLreqNMDP.Open;
if SQLreqNMDP.FieldBYName('paswd').isNull then
result:= True; //Mot de passe non identique à celui d'un autre compte de l'utilisateur
end;

La fonction de changement de mot de passe est aussi créée au sein de l'unité datamodule. Nous préférons ici une fonction qui assure le changement et retourne grâce au « bloc try » un résultat booléen permettant de le confirmer ou de gérer une éventuelle erreur pouvant se produire lors d'une utilisation « multiposte » Commentez L'utilisation d'une procédure aurait été adaptée seulement dans le cadre d'une utilisation « monoposte ».

function TData.Changement(cNmdp: String): boolean; //Fonction de changement de mot de passe
begin
//Change le mot de passe en fonction du groupe d'appartenance de l'utilisateur
result:= False;
gpe:= SQLreqMDP.FieldBYName('groupe').asString; //Récupération du statut de l'utilisateur
SQLreqNMDP.SQL.Text:='UPDATE t_user SET paswd =:p WHERE trigramme =:t AND groupe =:g;';
SQLreqNMDP.ParamByName('t').asString:= User;
SQLreqNMDP.ParamByName('p').asString:= cNmdp;
SQLreqNMDP.ParamByName('g').asString:= gpe; //Discrimination par le statut de l'utilisateur
try
SQLreqNMDP.ExecSQL;
result:= True;
except
result:= False;
end;
end;

V-F - Le code du formulaire de gestion des utilisateurs

La fiche FormGutil constitue le formulaire qui permet à un administrateur de l'application d'inscrire ou de supprimer des utilisateurs. Ce formulaire est appelé depuis l'application. Il est seulement accessible aux administrateurs. Le code concernant ce formulaire est créé dans l'unité gutil.

Nous déclarons les variables et les fonctions de création et de suppression des utilisateurs dans lesquelles elles seront utilisées. Nous déclarons aussi la procédure Procrazgdu qui permettra d'effacer les champs du formulaire de gestion des utilisateurs après chaque création ou suppression d'un utilisateur.

private
util: String;
nomu: String;
prenu: String;
mdpu: String;
gpu: String;
test: String;
function Testsuppu(cutil,cgpu: String): boolean;
function Testcreau(cutil,cnomu,cprenu,cgpu,cmdpu: String): boolean;
procedure Procrazgdu(Form: TForm);

Nous déclarons l'unité datamodule dans les clauses uses au niveau de l'implémentation pour lier le formulaire de la gestion des utilisateurs à la base de données.

implementation

{$R *.lfm}

uses
datamodule;

Nous élaborons la procédure Procrazgdu qui effacera les champs de saisie du formulaire suite à une création ou suppression d'utilisateur. Cette procédure permet ainsi de libérer les champs pour pouvoir faire plusieurs créations ou suppressions d'utilisateurs les unes après les autres sans avoir à effacer manuellement les champs. Les composants de type TEdit peuvent être effacés de « manière groupée » alors qu'une ligne de code est destinée au composant ComboGpe.

procedure TFormGutil.Procrazgdu(Form: TForm); //Procédure d'effacement du contenu des champs du formulaire
var
I: Integer;
begin
for I:= 0 to Form.ComponentCount - 1 do
if Form.Components[I] is TEdit then
TEdit(Form.Components[I]).Text:= '';
ComboGpe.Text:= '';
end;

Si l'administrateur désire abandonner la création ou la suppression d'un utilisateur, un clic sur le bouton Annuler ferme le formulaire et il se retrouve de nouveau sur l'application. La fermeture du formulaire par la croix X ramène aussi à l'application.

procedure TFormGutil.Annul3Click(Sender: TObject); //Abandon gestion des utilisateurs
begin
Close;
end;

À la fermeture du formulaire, la mémoire est libérée.

procedure TFormGutil.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
CloseAction:= caFree; //Libère la mémoire
end;

V-F-1 - Création d'un utilisateur

Dans ce paragraphe, nous verrons qu'un utilisateur ne peut posséder qu'un profil ou compte par groupe et les mots de passe pour chacun devront être différents. Pour des raisons pratiques, les mots de passe saisis dans le formulaire n'ont pas à répondre aux règles que nous avons mises en place dans la conception du formulaire de changement de mot de passe. Ici, le but est de donner au nouvel utilisateur un mot de passe simple qu'il devra changer suite à sa première connexion. Par exemple : util01 pour un simple utilisateur ou admin01 pour un administrateur.

Pour créer un utilisateur, il faut s'assurer préalablement de la complétude des champs requis dans le formulaire de gestion des utilisateurs. Pour le faire, nous élaborons la fonction Testcreau.

function TFormGutil.Testcreau(cutil,cnomu,cprenu,cgpu,cmdpu: String): boolean;
var
ListeDesCas: TStrings;
begin
result:= False;
ListeDesCas:= TstringList.create;
try
ListeDesCas.Add(cutil);
ListeDesCas.Add(cnomu);
ListeDesCas.Add(cprenu);
ListeDesCas.Add(cgpu);
ListeDesCas.Add(cmdpu);
Case ListeDesCas.IndexOf(test) of
0 : cutil:= '';
1 : cnomu:= '';
2 : cprenu:= '';
3 : cgpu:= '';
4 : cmdpu:= ''
else
result:= True;
end;
Case ListeDesCas.IndexOf(test) of
0 : ShowMessage('Échec de création. Identifiant utilisateur non renseigné.');
1 : ShowMessage('Échec de création. Nom utilisateur non renseigné.');
2 : ShowMessage('Échec de création. Prénom utilisateur non renseigné.');
3 : ShowMessage('Échec de création. Groupe utilisateur non renseigné.');
4 : ShowMessage('Échec de création. Mot de passe utilisateur non renseigné.');
end;
finally ListeDesCas.Free;
end;
end;

Le bouton Créer appelle la fonction Creatutil que nous créerons dans l'unité datamodule. Elle s'exécute après avoir répondu aux conditions de complétude des champs du formulaire. Parmi ces conditions, seront aussi sollicitées les fonctions Testgpu et Testmdpu que nous élaborerons également dans datamodule.

procedure TFormGutil.BtnCreaClick(Sender: TObject); //Appel création d'un utilisateur
var
reply: integer;
boxstyle: integer;
begin
util:= EdIdentU.Text;
nomu:= EdNomU.Text;
prenu:= EdPrenU.Text;
gpu:= ComboGpe.Text;
mdpu:= EdMdpU.Text;
//Teste la complétude des champs
if Testcreau(util,nomu,prenu,gpu,mdpu)= False then
exit;
//Teste l'existence dans le groupe d'utilisateurs
if Data.Testgpu(util,gpu)= True then
ShowMessage('Échec de création. Utilisateur déjà existant dans ce groupe.')
else
//Teste si le mot de passe est identique à celui d'un autre compte de l'utilisateur
if Data.Testmdpu(util,mdpu)= True then
ShowMessage('Échec de création. Mot de passe identique à celui d''un autre compte de cet utilisateur.')
else
//Appel conditionnel de la fonction de création
if Data.Creatutil(util,nomu,prenu,gpu,mdpu)= True then
begin
Procrazgdu(Self); //Efface les champs du formulaire
with application do
begin
boxstyle:= MB_ICONQUESTION + MB_YESNO;
reply:= MessageBox('Création réussie. Voulez-vous créer un autre utilisateur ?', 'Création utilisateur', boxstyle);
if reply = IDYES then
FormGutil.Show //Montre le formulaire de gestion des utilisateurs
else
FormGutil.Close; //Ferme le formulaire
end;
end;
end;

Lors de la création d'un utilisateur, chaque manquement dans les éléments à saisir renvoie un message d'information comme celui ci-après invitant l'administrateur à compléter le champ omis. À la fermeture du message l'administrateur revient sur le formulaire pour apporter les corrections ou abandonner la création. Exemple de message :

En passant par la fonction Testgpu, la procédure interdit la création d'un deuxième compte utilisateur pour un utilisateur déjà présent dans le groupe sélectionné. Une tentative de création dans ces conditions ouvre un message qui une fois fermé remet l'administrateur sur le formulaire pour apporter les corrections ou abandonner la création.

En passant par la fonction Testmdpu, la procédure interdit la création d'un compte utilisateur pour un utilisateur ayant le même mot de passe pour un autre de ses comptes. Ainsi, pour un même utilisateur ses comptes d'administrateur et de simple utilisateur ne peuvent être utilisés avec un même mot de passe. Une tentative de création dans ces conditions renvoie un message qui une fois fermé remet l'administrateur sur le formulaire pour apporter les corrections ou abandonner la création.

Quand la création aboutit, un message informe l'administrateur et l'invite soit à revenir sur le formulaire en cliquant sur Yes pour créer un nouvel utilisateur, ou bien abandonner la création en cliquant sur No et revenir sur l'application.

Nous avons vu précédemment que pour créer un utilisateur, il faut utiliser les fonctions Testgpu et Testmdpu ainsi que la procédure Creatutil. Pour concevoir celles-ci, nous nous rendons dans l'unité datamodule.

Commençons par la fonction Testgpu.

function TData.Testgpu(cutil,cgpu: String): boolean; //Fonction test de présence de l'utilisateur dans la table t_user selon son groupe d'appartenance
begin
result:= False;
SQLreqGutil.Close;
SQLreqGutil.SQL.Text:='SELECT trigramme,groupe,paswd FROM t_user WHERE trigramme =:t AND groupe =:g;';
SQLreqGutil.ParamByName('t').asString:= cutil;
SQLreqGutil.ParamByName('g').asString:= cgpu;
SQLreqGutil.Open;
if SQLreqGutil.FieldBYName('groupe').asString <> '' then
result:= True; //Utilisateur présent
end;

Ensuite, passons à la fonction Testmdpu.

function TData.Testmdpu(cutil,cmdpu: String): boolean; //Fonction test de présence de l'utilisateur dans la table t_user selon son mot de passe
begin
result:= False;
SQLreqGutil.Close;
SQLreqGutil.SQL.Text:='SELECT trigramme,groupe,paswd FROM t_user WHERE trigramme =:t AND paswd =:m;';
SQLreqGutil.ParamByName('t').asString:= cutil;
SQLreqGutil.ParamByName('m').asString:= cmdpu;
SQLreqGutil.Open;
if SQLreqGutil.FieldBYName('paswd').asString <> '' then
result:= True; //Utilisateur présent
end;

Enfin, pour terminer la partie création d'un utilisateur, nous créons la fonction Creatutil. La raison d'opter pour une fonction plutôt que pour une procédure est la même que celle pour le changement de mot de passe.

function TData.Creatutil(cutil,cnomu,cprenu,cgpu,cmdpu: String): boolean; //Fonction de création d'un utilisateur
begin
result:= False;
SQLreqGutil.SQL.Text:= 'INSERT INTO t_user (trigramme, nom, prenom, groupe, paswd) VALUES (:t, :n, :p, :g, :m);';
SQLreqGutil.ParamByName('t').asString:= cutil;
SQLreqGutil.ParamByName('n').asString:= cnomu;
SQLreqGutil.ParamByName('p').asString:= cprenu;
SQLreqGutil.ParamByName('g').asString:= cgpu;
SQLreqGutil.ParamByName('m').asString:= cmdpu;
try
SQLreqGutil.ExecSQL;
result:= True;
except
result:= False;
end;
end;

V-F-2 - Suppression d'un utilisateur

Nous revenons maintenant dans l'unité gutil pour réaliser la partie suppression d'un utilisateur.

Dans ce paragraphe, nous verrons que la suppression d'un utilisateur est sélective. Elle ne concerne que la suppression de l'utilisateur dans un groupe défini, préservant ainsi le compte ou profil que ce même utilisateur pourrait avoir dans un autre groupe.

Pour supprimer un utilisateur, il est nécessaire que les champs Identifiant util et Groupe util soient complétés. Pour le vérifier, nous créons la fonction Testsuppu.

function TFormGutil.Testsuppu(cutil,cgpu: String): boolean;
begin
result:= False;
if cutil = '' then
ShowMessage ('Échec de suppression. Identifiant utilisateur non renseigné.')
else
if cgpu = '' then
ShowMessage ('Échec de suppression. Groupe utilisateur non renseigné.')
else
result:= True;
end;

Le bouton Supprimer lance la fonction de suppression d'un utilisateur Suputil que nous élaborerons dans l'unité datamodule. Comme vu précédemment, seule la complétude des champs Identifiant util et Groupe util est requise pour procéder. La suppression sera alors soumise à la condition d'appartenance au groupe sélectionné dans ComboGpe et testée avec la fonction Testgpu déjà utilisée précédemment dans la partie création d'un utilisateur.

procedure TFormGutil.BtnSupClick(Sender: TObject); //Appel suppression d'un utilisateur
var
reply: integer;
boxstyle: integer;
begin
util:= EdIdentU.Text;
gpu:= ComboGpe.Text;
//Teste la complétude des champs
if Testsuppu(util,gpu)= False then
exit;
//Teste l'existence dans le groupe d'utilisateurs
if Data.Testgpu(util,gpu)= False then
ShowMessage('Échec de suppression. Utilisateur absent ou mal renseigné.')
else
//Appel conditionnel de la fonction de suppression
if Data.Suputil(util,gpu)= True then
begin
Procrazgdu(Self); //Efface le contenu du formulaire
with application do
begin
boxstyle:= MB_ICONQUESTION + MB_YESNO;
reply:= MessageBox('Suppression réussie. Voulez-vous supprimer un autre utilisateur ?', 'Suppression utilisateur', boxstyle);
if reply = IDYES then
FormGutil.Show //Montre le formulaire de gestion des utilisateurs
else
FormGutil.Close; //Ferme le formulaire
end;
end;
end;

Lors de la suppression d'un utilisateur, chaque manquement dans les éléments à saisir renvoie un message d'information comme celui ci-après invitant l'administrateur à compléter le champ omis. À la fermeture du message, l'administrateur revient sur le formulaire pour corriger ou abandonner la suppression.

En passant par la fonction Testgpu, la procédure permet d'autoriser ou non la suppression d'un utilisateur selon le groupe sélectionné. Une tentative de suppression dans le cas d'une inadéquation entre utilisateur et groupe ouvre un message qui à sa fermeture remet l'administrateur sur le formulaire pour apporter les corrections ou abandonner la suppression.

Quand la suppression aboutit, un message informe l'administrateur et l'invite soit à revenir sur le formulaire en cliquant sur Yes pour supprimer un nouvel utilisateur, ou bien abandonner la suppression en cliquant sur No et revenir sur l'application.

Pour finir, nous nous rendons maintenant dans l'unité datamodule où nous devons élaborer la fonction Suputil. Ici encore, la raison d'opter pour une fonction plutôt que pour une procédure est la même que celle pour le changement de mot de passe.

function TData.Suputil(cutil,cgpu: String): boolean; //Fonction de suppression d'un utilisateur
begin
result:= False;
SQLreqGutil.SQL.Text:= 'DELETE FROM t_user WHERE trigramme =:t AND groupe =:g;';
SQLreqGutil.ParamByName('t').asString:= cutil;
SQLreqGutil.ParamByName('g').asString:= cgpu;
try
SQLreqGutil.ExecSQL;
result:= True;
except
result:= False;
end;
end;

VI - Conclusion

Nous voilà parvenus au terme de ce tutoriel. Pour que tout fonctionne, il faut que la base de données soit démarrée. Il est aussi nécessaire qu'un premier administrateur ait été créé préalablement dans la table t_user en utilisant bien son identifiant de session comme identifiant de connexion à l'application. Celui-ci pourra ensuite créer les autres utilisateurs.

Nous avons vu comment profiter de la base de données pour mettre en place un formulaire de connexion avec identifiant et mot de passe. Nous y avons associé un formulaire de changement de mot de passe ainsi qu'un formulaire destiné aux administrateurs de l’application pour gérer les utilisateurs. Nous avons abordé la sécurisation des mots de passe lors de leur création. Cependant, la bonne pratique voudrait que les mots de passe ne soient pas lisibles en clair dans la table t_user. Pour faire cela, il faudrait les crypter ou les chiffrer, lors de leur inscription dans la table. Nous vous laissons le choix de la méthode…

Il est possible d'améliorer encore ce que nous avons élaboré ensemble. Maintenant, à vous d'être créatif pour mettre à votre goût et apporter les améliorations qui vous paraîtront nécessaires à ce que nous avons créé en cheminant dans ce tutoriel.

VII - Remerciements

Je remercie gvasseur58 pour ses encouragements pour me lancer et pour qui je me devais de finaliser ce tutoriel.

Je remercie SergioMaster, mon maître du code, qui m'a parfois poussé dans mes retranchements et dont la lecture technique m'a accompagné tout au long de ce laborieux cheminement. Il m'a donné beaucoup de son temps, de sa patience et de son savoir pour l'élaboration ce tutoriel.

Je remercie aussi ClaudeLELOUP. Sa grande qualité de relecture, nous permet de sortir la tête du code et d'écrire correctement ce vers quoi nous voulons amener nos lecteurs.