Article
· Fév 28 8m de lecture

Réserver une License

"Cela fait trente secondes que j'attends un service. C'est scandaleux ! Je m'en vais !"

"Je suis désolé d'entendre cela, monsieur. La prochaine fois, vous devriez peut-être réserver."

Si vous entendiez ce commentaire dans votre restaurant préféré, vous penseriez que la personne qui le fait est ridicule. Cependant, dans le contexte de votre API, ce commentaire est parfaitement logique. Tout comme votre restaurant préféré, votre API a des clients réguliers qui, comme vous le savez, viendront un jour ou l'autre. Il serait formidable de pouvoir faire une réservation permanente pour eux aussi.

Cela implique quelques principes fondamentaux d'IRIS. Tout d'abord, nous devons comprendre l'interface %SYSTEM.License. Il s'agit d'une interface permettant d'obtenir des informations sur l'utilisation de la licence pour une instance d'IRIS. Deuxièmement, nous devrons nous familiariser avec la classe %CSP.SessionEvents. Cette classe nous permet de remplacer diverses méthodes qui sont appelées tout au long du cycle de vie d'une session CSP.

Nous commencerons par l'interface %SYSTEM.License. Alors que certaines méthodes de cette classe peuvent être appelées à l'aide de $SYSTEM.License, d'autres doivent être appelées via ##class(%SYSTEM.License). Par souci de cohérence, nous utiliserons ##class(%SYSTEM.License) puisqu'il peut être utilisé pour toutes les méthodes. Pour notre cas d'utilisation, nous devrons être en mesure de vérifier le nombre de licences disponibles et celles utilisées par un utilisateur spécifique. 

Tout d'abord, nous vérifions le nombre d'unités de licence disponibles. Si au moins une licence est disponible, nous n'avons pas besoin de vérifier autre chose car nous avons déjà assez de place pour notre réservation. Nous devons également vérifier si l'utilisateur qui tente de se connecter est bien celui pour lequel nous détenons la réservation. Cela peut être réalisé par un simple appel à la méthode LUAvailable comme suit :

if (##class(%SYSTEM.License).LUAvailable()) || ($USERNAME = username) > 0{
    return $$$OK
}

Ensuite, nous devons vérifier si l'utilisateur réservé est actuellement connecté. S'il a déjà une table dans notre restaurant, nous n'avons pas besoin de vérifier s'il y a une place disponible pour lui. Si l'utilisateur se connecte toujours à partir d'une adresse spécifique, nous pouvons utiliser la méthode LicenseCount pour le vérifier. Elle prend l'identifiant de l'utilisateur comme argument et renvoie un nombre entier qui nous indique si l'utilisateur consomme ou non des licences en ce moment. Par exemple :

set licenseusage = ##class(%SYSTEM.License).LicenseCount("David@127.0.0.1")

Il nous dira si cet utilisateur utilise une licence. Si elle renvoie 0, l'utilisateur ne l'utilise pas, mais si elle renvoie 1, l'utilisateur exploite la licence. Sachez que si cette méthode renvoie un nombre supérieur à 1, cela signifie que l'utilisateur a dépassé son nombre maximal de connexions et a commencé à consommer une licence par connexion. Dans ce cas, vous avez un nouveau problème à résoudre !

Si nous ne savons pas de quelle adresse provient la connexion, nous devons aborder cette situation différemment et prendre quelques mesures supplémentaires. Nous devons utiliser la requête de la classe ConnectionList dans ce cas. Considérons le code suivant :

 

ClassMethod CheckReservation(username As %String) As %Status
{
    try{
        if (##class(%SYSTEM.License).LUAvailable() > 1) || ($USERNAME = username) {
            return $$$OK
        }
        set rs = ##class(%ResultSet).%New("%SYSTEM.License:ConnectionList")
        set sc = rs.%Execute()
        if $$$ISERR(sc) {$$$ThrowStatus(sc)}
        while rs.%Next(){
            if $P(rs.%GetData(1),"@",1) = username{
                return $$$OK
            }
        }
        $$$ThrowStatus($$$ERROR(5001,"Reserved User Not Logged In."))
    }
    catch ex{
        do ex.Log()
        return ex.AsStatus()
    }
}

Veuillez noter que cette méthode ne permet de réserver qu'une seule licence sur la base d'un nom d'utilisateur. Si votre cas d'utilisation nécessite la sécurisation de plus d'une licence, vous devrez consulter les résultats de la requête ConnectionList pour compter combien de clients se sont déjà connectés et, peut-être, se sont rendus au bar pour leur premier scotch dans l'intervalle. Ensuite, vous devrez vous assurer que la LUAvailable restante est suffisante pour accueillir toutes les réservations à venir, et pas seulement celles qui sont supérieures à un.

Cette méthode accède généralement à la requête LicenseCount de la classe %SYSTEM.License à l'aide d'un objet %ResultSet. Bien qu'il soit généralement préférable d'utiliser %SQL.Statement, la méthode %PrepareClassQuery échoue toujours avec cette requête. C'est pourquoi nous ne pouvons pas l'utiliser ici. Cette requête renvoie une ligne pour chaque connexion, puis nous itérons à travers le jeu de résultats. Pour chaque ligne, la première colonne contient l'identifiant de la licence, qui ressemble au nom d'utilisateur et à l'adresse IP séparés par un @. Comme nous ne recherchons que le nom d'utilisateur (et non le nom d'utilisateur et l'adresse IP combinés), nous voulons comparer la première partie de cette colonne avec le nom d'utilisateur fourni, et s'ils correspondent, nous renverrons un statut de réussite. Cependant, si nous parcourons l'ensemble des résultats sans trouver le nom d'utilisateur, nous renverrons une erreur.

Si nous souhaitons modifier cette méthode pour vérifier un rôle à la place (disons que nous voulons toujours autoriser au moins un utilisateur ayant le rôle %All à se connecter pour permettre à un superutilisateur d'accéder au système à tout moment), nous pouvons le faire en passant à l'espace de noms %SYS et en vérifiant les rôles de chaque utilisateur au fur et à mesure que nous itérons dans la boucle. Nous pourrions utiliser ##class(Security.Users).Get($P(rs.%GetData(1),"@",1),.props) et ensuite vérifier si notre rôle a été inclus dans props("Roles") en utilisant props("Roles") ["%All". Nous devons nous rappeler de revenir à l'espace de noms d'origine, afin de nous connecter à l'espace de noms de l'application correcte. Cela devrait ressembler au code suivant :

ClassMethod CheckReservation(role As %String) As %Status
{
    try{
        set returnns = $NAMESPACE
        zn "%SYS"
        set sc = ##class(Security.Users).Get($USERNAME,.props)
        if (##class(%SYSTEM.License).LUAvailable() > 1) || (props("Roles") [ role) {
            zn returnns
            return $$$OK
        }
        set rs = ##class(%ResultSet).%New("%SYSTEM.License:ConnectionList")
        set sc = rs.%Execute()
        if $$$ISERR(sc) {$$$ThrowStatus(sc)}
        while rs.%Next(){
            set sc = ##class(Security.Users).Get($P(rs.%GetData(1),"@",1),.props)
            if props("Roles") [ role{
                zn returnns
                return $$$OK
            }
        }
        zn returnns
        $$$ThrowStatus($$$ERROR(5001,"Reserved User Not Logged In."))
    }
    catch ex{
        do ex.Log()
        return ex.AsStatus()
    }
}

There are some other practical class queries within the License API that you may want to use as well. If you wish to verify a login to a specific application,

ConnectionAppList serait plus approprié. Si vous souhaitez inspecter le type de licence : User, CSP, Mixed ou Grace - utilisez la requête de classe UserList. Si vous utilisez la requête ProcessList, vous pouvez aller encore plus loin dans les détails des processus exécutés par l'utilisateur. Gardez à l'esprit qu'à chaque fois que vous vous emportez un peu trop, il vous suffit de ralentir le processus de connexion.

Maintenant que nous avons ce morceau de code, nous devons trouver où le placer. Pour ce faire, nous devons apprendre quelques notions de base sur la classe %CSP.SessionEvents. Cette classe contient plusieurs méthodes que vous pouvez remplacer pour contrôler ce qui se passe lorsque les sessions CSP commencent, se terminent ou obtiennent un délai d'attente, ou lorsqu'un utilisateur se connecte ou se déconnecte. Ici, nous sommes particulièrement intéressés par la méthode OnStartSession() qui s'exécute avant l'attribution d'une licence à la session. Nous pouvons exécuter notre méthode à cet endroit et, lorsqu'elle renvoie une erreur, il nous suffit de donner la valeur 1 à %session.EndSession. L'utilisateur recevra le message standard de fin de licence et se verra interdire de se connecter.

Nous allons créer une classe appelée User.SessionEvents avec l'extension %CSP.SessionEvents et notre méthode de classe définie précédemment. Ensuite, nous ne remplacerons qu'une seule méthode de cette classe afin de nous assurer que nous conservons notre espace disponible pour d'autres réservations. Cette méthode ressemblera à l'exemple suivant :

ClassMethod OnStartSession()
{
    if $$$ISERR(##class(User.SessionEvents).CheckReservation("David")){ 
        set %session.EndSession=1
    }
    Quit $$$OK
}

Si vous avez plusieurs espaces de noms, n'oubliez pas de créer cette classe dans chacun d'entre eux avec une application CSP qui l'utilisera.

La dernière étape consistera à configurer notre application pour lui permettre d'utiliser ces événements de session. Nous pouvons le faire dans le portail de gestion en allant dans Administration du système, Sécurité, Applications, et Applications Web. Nous devons cliquer sur les applications dans lesquelles nous souhaitons utiliser la classe d'événements de session personnalisés. Ensuite, dans la case Classe d'événement, nous devons saisir User.SessionEvents. À partir de là, chaque fois que nous nous connecterons, notre méthode personnalisée OnStartSession sera appelée et les autres utilisateurs ne pourront pas se connecter s'il n'y a pas de place pour notre réservation.

Il y a un détail critique de mise en œuvre à prendre en compte ici. Voulons-nous que ces événements de session s'appliquent au portail de gestion du système ou non ? Dans l'affirmative, nous pourrions nous retrouver dans une situation où un problème surviendrait, mais où nous ne serions pas en mesure de nous connecter et de le résoudre. Si nous ne le faisons pas, il est possible que quelqu'un se connectant prenne l'unité de licence que nous essayions de réserver pour nous-mêmes. Peut-être devrions-nous modifier le code et voir si l'utilisateur a un certain rôle qui lui permet de contourner la vérification de la réservation. Cependant, cette partie dépend de vous.

Désormais, chaque fois que votre client régulier arrivera, vous pourrez lui garantir une place assise !

Discussion (0)1
Connectez-vous ou inscrivez-vous pour continuer