Gestionnaire d'autorisation kbmMW explained

Depuis kbmMW v4.40, un nouveau gestionnaire d'autorisation très flexible a été introduit.

Son objectif est de fournir aux développeurs la possibilité de définir des droits sur des ressources pour les utilisateurs. Il s'agit d'une fonctionnalité optionnelle qui intègre le schéma驱动方案 d'autorisation existant dans kbmMW, facilitant ainsi les tâches de développement d'autorisation.

On dit qu'une image vaut mieux que mille mots... Vérifions si c'est vrai :

Le diagramme ci-dessus illustre comment un utilisateur typique interagit avec les fonctionnalités à l'intérieur du serveur d'application (représenté en vert). Il montre également comment le nouveau gestionnaire d'authentification s'intègre au serveur d'application et fonctionne en interne.

Malgré cette image, je vais utiliser plus de mille mots pour expliquer le fonctionnement.

Premièrement : qu'est-ce qui pourrait vous interesser ?

Si vous prévoyez ou avez déjà fourni des fonctionnalités centralisées pour un ou plusieurs clients, et que ces fonctionnalités dépendent de la position exacte du client et de son comportement, cette solution pourrait vous interesser.

C'est une question de sécurité. La sécurité est généralement implémentée comme un schéma de confiance côté client, ou comme une fonctionnalitéforcée dans la base de données.

Les schéma de sécurité basés sur la confiance client sont fondamentalement incohérents car il n'y a pas vraiment de sécurité. Le client pourrait être facilement remplacé par un client malveillant, ce qui compromettrait la sécurité du système, leading au vol ou à la destruction des données. En tant que développeur, c'est votre responsabilité de vous assurer que les clients se comportent correctement pour protéger vos données. De plus, il est difficile d'ajouter un autre type de client (comme une application mobile) qui puisse accéder et manipuler les données en toute sécurité. Le nouveau client devrait implémenter les mêmes couches de sécurité que le premier, ce qui nécessite la réutilisation du code et non pas du copier-coller qui pourrait créer des failles de sécurité.

La sécurité forcée par la base de données est beaucoup meilleure car il y a réellement une couche intermédiaire qui decide ce que le client peut faire sans manipulation directe des données. Cependant, si vos opérations vont au-delà de l'accès à des structures de données simples dans la base de données, vous devrez recourir à des langages de programmation spécifiques à la base de données comme TransactSQL, PL/SQL, etc., si la base de données contient son propre langage de programmation. Sinon, vous devrez déplacer les fonctionnalités vers le client, ce qui constitue une solution sous-optimale comme mentionné précédemment.

Une meilleure solution consiste à utiliser Delphi ou C++ comme langage de programmation, et à placer tout le code défini par les développeurs/entreprises (génération de rapports, importation/exportation de données, calculs, etc.) sur le serveur d'application du développeur. En d'autres termes, implémentez une architecture à trois couches : client, serveur d'application, serveur de base de données. Cela offre une flexibilité maximale et facilite le support d'autres types de clients tout en maintenant une couche capable d'appliquer les mesures de sécurité.

kbmMW a toujours eu des événements et des fonctions pour gérer l'autorisation des requêtes, mais les développeurs doivent écrire eux-mêmes le code pour effectuer ces tâches fastidieuses.

Maintenant, avec la publication de kbmMW v4.40, un nouveau composant appelé TkbmMWAuthorizationManager est disponible.

Les fonctionnalités de protection ultérieures permettent de définir plusieurs "modèles". Nous avons choisi d'utiliser une approche très flexible basée sur trois définitions :

  • Acteurs (Actors)
  • Rôles (Roles)
  • Ressources (Resources)

Un acteur représente généralement un utilisateur avec un nom de connexion et un mot de passe, ainsi qu'un rôle par défaut. Un acteur ne doit pas nécessairement être un utilisateur réel ; il peut également être utilisé par l'application elle-même, par exemple pour permettre à l'application d'agir comme un acteur spécifique dans des situations particulières.

Un acteur peut avoir différents "chapeaux"... exécuter des opérations basées sur différents rôles dans le système.

Une ressource est l'élément fondamental que vous, en tant que développeur, souhaitez protéger. Par exemple, cela peut être un service kbmMW spécifique, ou une fonction particulière au sein d'un service kbmMW. Cependant, comme le développeur est responsable à 100% du choix du schéma de nommage des ressources, tout ce que le développeur peut imaginer peut devenir une ressource.

Basé sur ces trois définitions, les développeurs peuvent maintenant définir les autorisations. Une autorisation détermine si un utilisateur (acteur) appartenant à un rôle spécifique peut accéder à une ressource spécifique.

De plus, une autorisation peut être valide uniquement pendant une période donnée (par exemple de 9h à 15h, ou seulement le mercredi, ou seulement le 1er avril, etc.), et peut ne s'appliquer que lorsque la requête provient d'un client physique spécifique. Dans kbmMW, nous appelons ces limitations des contraintes. Une autorisation peut être marquée avec zéro ou plusieurs contraintes.

Nous avons beaucoup parlé des acteurs. Mais dans le monde réel, nous avons des utilisateurs qui doivent être authentifiés et identifiés comme l'un des rôles définis. Pour cela, nous avons le sous-composant final dans le système d'autorisation de kbmMW : la connexion.

Une connexion vérifie le nom d'utilisateur/mot de passe d'un acteur par rapport à une liste connue. Si la validation du nom d'utilisateur/mot de passe réussit, des vérifications supplémentaires sont effectuées selon toute contrainte de connexion définie, qui définit des situations spéciales où la connexion peut être refusée (par exemple selon l'heure ou la position du client).

Maintenant, ajoutons des fonctionnalités de sécurité à notre serveur d'application de démonstration.

Comme le montre l'image, faites glisser un TkbmMWAuthorizationManager sur le formulaire, puis définissez la propriété AuthorizationManager de kbmMWServer pourpointer vers le composant nouvellement ajouté. Maintenant, kbmMW sait quand demander à son gestionnaire d'autorisation de traiter l'autorisation.

Quand est-ce le moment approprié ? Lorsqu'un utilisateur non connecté au système tente d'accéder à quelque chose, ou lorsqu'un utilisateur connecté tente d'accéder à un service dont les indicateurs indiquent qu'une autorisation est requise pour l'exécution. Cela peut être défini lors de la création du service via l'assistant kbmMW, mais peut également être défini ultérieurement.

Examinons un service de requête de démonstration (Query Service), qui renvoie des données depuis une base de données SQLite vers le client.

unit Unit2;    

interface    

uses    
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  kbmMWServer, kbmMWQueryService, Db, kbmMemTable, kbmMWCustomConnectionPool,
  kbmMWCustomDataset, kbmMemCSVStreamFormat, kbmMWSecurity,
  kbmMWBinaryStreamFormat, kbmMWStreamFormat, kbmMWResolvers, kbmMWSQLite,
  kbmMWServiceUtils;
type    
   TTestQuery = class(TkbmMWQueryService)
      kbmMWBinaryStreamFormat1: TkbmMWBinaryStreamFormat;               
      ClientSideQuery: TkbmMWSQLiteQuery;
      ALL_EVENTS: TkbmMWSQLiteQuery;
      SELECTED_EVENT: TkbmMWSQLiteQuery; 
      kbmMWSQLiteResolver1: TkbmMWSQLiteResolver;  
      SELECTED_SPECIES: TkbmMWSQLiteQuery;  
      ALL_SPECIES: TkbmMWSQLiteQuery;  
  private 
     { Private declarations }  
  public  
     { Public declarations }  
    class function GetFlags:TkbmMWServiceFlags; override;  
  end;  

var  
   TestQuery: TTestQuery;  

implementation  

uses Unit1;  

{$R *.DFM}  

class function TTestQuery.GetFlags:TkbmMWServiceFlags;  
begin  
     Result:=[mwsfListed,mwsfRunRequireAuth];  
end;  

end. 

Il n'y a pas beaucoup de code, à part la fonction de classe GetFlags qui retourne un ensemble d'indicateurs de service. En assurant que mwsfRunRequireAuth est inclus dans l'ensemble des indicateurs de service, on indique au serveur kbmMW que seuls les utilisateurs autorisés peuvent appeler toutes les fonctions de ce service de requête.

Maintenant, nous devons définir comment notre schéma d'autorisation devrait être structuré.

Les acteurs peuvent être définis directement dans le serveur d'application, ou être déterminés dynamiquement à partir d'une source de données externe (comme les utilisateurs définis dans la base de données) lors de la demande d'autorisation.

Les rôles sont généralement définis directement dans le serveur d'application, de même que les ressources et les autorisations. Cependant, les rôles peuvent également être保存 et chargés via un fichier XML, permettant une définition dynamique externe.

Pour la simplicité, nous choisissons que ce serveur d'application connaisse Gertrude et Franz comme acteurs, Administrator et User comme deux rôles. Nous les définissons dans l'événement OnCreate de MainForm, bien que cela puisse être fait ailleurs, à condition de le faire avant que quiconque ne soit autorisé.

procedure TForm1.FormCreate(Sender: TObject);  
var  
   sd:TkbmMWCustomServiceDefinition;  
begin  
     sd:=kbmMWServer1.RegisterServiceByName('KBMMW_QUERY',TTestQuery,false);
     kbmMWAuthorizationManager1.AddRole('Administrator',0);
     kbmMWAuthorizationManager1.AddRole('User',0);
     kbmMWAuthorizationManager1.AddActor('Gertrude','GertrudesPassword','User');
     kbmMWAuthorizationManager1.AddActor('Franz','FranzPassword','Administrator');
end;

Nous ajoutons deux rôles et deux acteurs. Chaque définition d'acteur possède un rôle par défaut. Tout acteur peut demander l'accès via d'autres rôles en plus de son rôle par défaut. Si l'acteur est autorisé à accéder à la ressource via ce rôle dépend des autorisations que nous devons définir bientôt.

Ensuite, nous devons déterminer quelles ressources de notre serveur d'application doivent être protégées. Bien que nous ayons défini que les fonctions du service TTestQuery nécessitent une autorisation, cela n'est pas spécifique.

Si nous laissons kbmMW gérer automatiquement l'autorisation, kbmMW définit les ressources identifiées par une chaîne contenant le nom du service demandé, un point, et le nom de la fonction demandée, comme : "KBMMW_QUERY.QUERY".

Dans notre exemple, le service de requête est enregistré via RegisterServiceByName sous le nom "KBMMW_QUERY", et ce identifiant sera utilisé lorsque le client demande le service de requête. Le client peut utiliser les fonctions supportées par le service de requête : "QUERY", "DEFINITIONS", "EXECUTE", "RESOLVE", "INVENTORY" ou "METADATA".

Cela signifie que lorsqu'un client exécute une requête, au moins la fonction "QUERY" sera exécutée sur le service.

Si vous utilisez votre propre service personnalisé, vous pouvez définir de nombreuses fonctions personnalisées côté serveur qui seront utilisées par vos clients.

Dans le mode d'autorisation par défaut, les noms de ressources construits sont "KBMMW_QUERY.QUERY", "KBMMW_QUERY.DEFINITIONS", "KBMMW_QUERY.EXECUTE", etc.

Les ressources sont définies via la méthode AddResource du gestionnaire d'autorisation, en fournissant le nom de la ressource et une référence optionnelle à une ressource parente. Nous pouvons utiliser la fonctionnalité de définition d'arborescence de ressources pour minimiser le code lors de la définition des autorisations pour les utilisateurs.

var
   adminResources, userResources:TkbmMWAuthorizationResource;  
...
   adminResources:=kbmMWAuthorizationManager1.AddResource('AdminResources',nil);
   userResources:=kbmMWAuthorizationManager1.AddResource('UserResources',adminResources);
   kbmMWAuthorizationManager1.AddResource('KBMMW_QUERY.QUERY',userResources);
   kbmMWAuthorizationManager1.AddResource('KBMMW_QUERY.DEFINITIONS',userResources);
   kbmMWAuthorizationManager1.AddResource('KBMMW_QUERY.EXECUTE',userResources);
   kbmMWAuthorizationManager1.AddResource('KBMMW_QUERY.INVENTORY',userResources);
   kbmMWAuthorizationManager1.AddResource('KBMMW_QUERY.METADATA',userResources);
   kbmMWAuthorizationManager1.AddResource('KBMMW_QUERY.RESOLVE',adminResources);

Nous avons défini deux ressources virtuelles. L'une appelée AdminResources sans ressource parente, et l'autre appelée UserResources ayant AdminResources comme ressource parente. En d'autres termes, UserResources est un sous-ensemble de AdminResources, donc les acteurs/rôles ayant accès à AdminResources peuvent également accéder à UserResources.

Maintenant, définissons les autorisations avec le code suivant :

kbmMWAuthorizationManager1.Grant('','User','UserResources',[mwapExecute]);
kbmMWAuthorizationManager1.Grant('','Administrator','AdminResources',[mwapExecute]);

La première instruction accorde l'autorisation à tout acteur occupant le rôle 'User' et demandant une ressource dans le groupe 'UserResources', avec le droit mwapExecute (exécution de la requête).

Comme vous pouvez spécifier explicitement le nom de l'acteur au lieu d'une chaîne vide, vous pouvez créer des autorisations très précises pour des personnes spécifiques. Cependant, dans des circonstances normales, autoriser simplement les rôles sur les ressources est plus clair.

En plus de Grant, vous pouvez utiliser la méthode Deny avec la même syntaxe pour refuser les autorisations. Cependant, cela devrait rarement être nécessaire. Si c'est le cas, cela pourrait indiquer que vous devriez revisar la structure de l'arborescence des ressources et la définition des rôles pour voir si elles peuvent être réorganisées différemment pour mieux répondre à vos besoins en matière d'autorisation.

Si un client tente de se connecter au serveur d'application et de demander des données depuis le service de requête, l'accès sera toujours refusé. Pourquoi ? Nous n'avons toujours pas de client connecté.

Le diagramme ci-dessous illustre le processus de connexion :

La méthode la plus simple est de laisser le serveur d'application kbmMW gérer complètement automatiquement le processus de connexion. Nous pouvons y parvenir en définissant la propriété Options pour inclure mwaoAutoLogin.

L'étape suivante consiste à faire en sorte que le client fournisse le nom d'utilisateur et le mot de passe apppropriés.

Il y a plusieurs façons de le faire, dont l'une consiste à utiliser le composant TkbmMWSimpleClient. En fournissant son instance avec le nom d'utilisateur et le mot de passe, tous les composants TkbmMWClientQuery utilisent ensuite le composant TkbmMWSimpleClient via la propriété Client, remplaçant l'instance Client utilisée en interne par TkbmMWClientQuery.

Si TkbmMWClientQuery est utilisé dans un environnement multithread, une meilleure approche consiste à définir TkbmMWClientQuery.ClientAsTemplate sur true et à s'assurer qu'une requête est exécutée via SimpleClient avant que les actions de requête ne se produisent (cela peut être une fonction serveur virtuelle "LOGON"). Cette fonction n'a pas besoin d'exécuter quoi que ce soit ; elle sert simplement à déclencher le processus de connexion kbmMW avant le début des requêtes, pour obtenir le jeton de connexion du serveur.

Cet article couvre uniquement les concepts de base de ce qui peut être réalisé avec le gestionnaire d'autorisation en matière d'autorisation. Nous n'avons pas encore discuté de l'ajout d'autorisations plus granulaires sur les tables de données réelles, de la gestion des délais d'expiration après connexion sans utilisation, de la sauvegarde et du chargement des paramètres d'autorisation XML complets, de l'ajout de contraintes aux connexions et autorisations, de l'autorisation d'accès anonyme/utilisateur inconnu à des fonctions spécifiques, ni de la définition dynamique des rôles basée sur des ressources externes comme la base de données.

Mais tout cela est pris en charge par le nouveau mécanisme de gestion d'autorisation. J'espère que cela vous permettra de mieux comprendre vos besoins.

Étiquettes: Delphi kbmmw authorization security

Publié le 16 juin à 22h16