Sous-sections de Hermes
Hermes
Cette section contient une brève présentation d’Hermes et de ses fonctionnalités, puis définit ses concepts clés et détaille le fonctionnement de ses principaux processus.
Sous-sections de Hermes
Présentation
Qu’est-ce qu’Hermes ?
Hermes est un outil de capture des changements de données (CDC) depuis n’importe quelles sources vers n’importe quelles cibles.
Flux de fonctionnement simplifié
Le serveur Hermes interrogera régulièrement les sources de données et générera un différentiel entre le nouvel ensemble de données et le précédent issu du cache. Chaque différence sera convertie en un message d’événement et envoyée à un bus de messages (e.g. Kafka, RabbitMQ…).
Les clients recevront et traiteront chaque message d’événement pour propager les données vers leurs cibles respectives.
flowchart LR
subgraph Datasources["Sources de données"]
direction LR
RefOracle
RefPostgreSQL
RefLDAP
RefEtc
end
subgraph Hermes-server
direction LR
hermes-server
end
subgraph External_dependencies["Dépendences externes"]
direction LR
MessageBus
end
subgraph Hermes-clients
direction LR
hermes-client-ldap
hermes-client-aspypsrp-ad
hermes-client-webservice
hermes-client-etc["..."]
end
subgraph Targets["Cibles"]
direction LR
LDAP
ActiveDirectory
webservice
etc
end
RefOracle[(Oracle)]-->|Données|hermes-server
RefPostgreSQL[(PostgreSQL)]-->|Données|hermes-server
RefLDAP[(LDAP)]-->|Données|hermes-server
RefEtc[(...)]-->|Données|hermes-server
hermes-server-->|Événements|MessageBus(("Bus de message"))
MessageBus-->|Événements|hermes-client-ldap
MessageBus-->|Événements|hermes-client-aspypsrp-ad
MessageBus-->|Événements|hermes-client-webservice
MessageBus-->|Événements|hermes-client-etc
hermes-client-ldap-->|Met à jour|LDAP[(LDAP)]
hermes-client-aspypsrp-ad-->|Met à jour|ActiveDirectory[(Active Directory)]
hermes-client-webservice-->|Met à jour|webservice[(Web service <i>nom</i>)]
hermes-client-etc-->|Met à jour|etc[("...")]
classDef external fill:#fafafa,stroke-dasharray: 5 5
class Datasources,External_dependencies,Targets external
Fonctionnalités
- Ne nécessite aucune modification du ou des modèles de données sources (e.g. pas besoin d’ajouter une colonne
last_updated) - Multi-source, avec possibilité de fusionner ou d’agréger des données, et éventuellement de définir des contraintes de fusion/agrégation
- Capable de gérer plusieurs types de données, avec des liens (clés étrangères) entre elles, et d’appliquer des contraintes d’intégrité
- Capable de transformer des données avec des filtres Jinja dans les fichiers de configuration : pas besoin d’éditer du code Python
- Gestion des erreurs propre, pour éviter les problèmes de synchronisation, et un mécanisme optionnel d’auto-remédiation des erreurs
- Propose une corbeille sur les clients pour les données supprimées
- Insensible à l’indisponibilité et aux erreurs sur chaque lien (source, bus de messages, cible)
- Facile à étendre par conception. Tous les éléments suivants sont implémentés en tant que plugins (liste des plugins existants) :
- Sources de données
- Filtres d’attributs (filtres de données)
- Clients (cibles)
- Bus de messages
- Les modifications apportées au modèle de données sont faciles et sûres à intégrer et à propager, que ce soit sur le serveur ou sur les clients
Concepts clé
Source de données
Une source depuis laquelle le serveur va collecter des données. Cela peut être tout ce qui contient des données : base de données, annuaire LDAP, service Web, fichier plat…
Plugin source de données
Un plugin Hermes chargé de collecter des données à partir d’un type de source de données spécifique et de les fournir au serveur.
Serveur
application hermes-server : interroge les sources de données à intervalles réguliers et convertit tous les changements entre les données récentes et les données précédentes en événements qui seront envoyés sur un bus de messages par un plugin producteur de bus de messages.
Bus de messages
Service externe comme Apache Kafka ou RabbitMQ qui collecte les événements du serveur et les fournit aux clients dans le même ordre que celui où ils ont été émis.
Plugin producteur de bus de messages
Un plugin Hermes exécuté par le serveur, chargé d’émettre des événements sur un type de bus de messages spécifique.
Plugin consommateur de bus de messages
Un plugin Hermes exécuté par les clients, chargé de consommer des événements depuis un type de bus de messages spécifique.
Client
application hermes-client : consomme les événements du bus de messages via le plugin consommateur de bus de messages et appelle les méthodes appropriées implémentées sur le plugin client pour propager les changements de données sur la cible.
Corbeille
Si activée dans la configuration, le client ne supprimera pas immédiatement les données mais les stockera dans la corbeille pendant le nombre de jours configuré. Si les données sont ajoutées à nouveau durant ce délai, le client les restaurera depuis la corbeille. Sinon, une fois la limite de rétention de la corbeille atteinte, les données seront supprimées.
Selon l’implémentation choisie sur le plugin client, cela peut permettre de nombreux scénarios, e.g. désactiver un compte ou le garder actif pendant une période de grâce.
File d’erreurs
Lorsqu’une exception est levée lors du traitement d’un événement sur le plugin client, l’événement est stocké dans une file d’erreurs. Tous les événements suivants concernant les mêmes objets de données ne seront pas traités mais stockés dans la file d’erreurs jusqu’à ce que le premier soit traité avec succès. Le traitement des événements dans la file d’attente d’erreurs est relancé périodiquement.
Auto remédiation
Parfois, un événement peut être stocké dans la file d’erreurs en raison d’un problème de données (e.g. un nom de groupe avec un point terminal génèrera une erreur sur Active Directory). Si le point terminal est ensuite supprimé du nom de groupe sur la source de données, l’événement modified sera stocké dans la file d’erreurs et ne sera pas traité tant que le précédent n’aura pas été traité, ce qui ne pourra jamais se produire sans procéder à une opération risquée et non-souhaitable : l’édition manuelle du fichier cache du client.
L’auto remédiation résout ce type de problèmes en fusionnant les événements d’un même objet dans la file d’erreurs. Elle n’est pas activée par défaut, car elle peut perturber l’ordre de traitement normal des événements.
Plugin client
Un plugin Hermes exécuté par le client, chargé d’implémenter des méthodes simples de traitement d’événements visant à propager les changements de données vers un type de cible spécifique.
Plugin d’attribut
Un plugin Hermes exécuté par le serveur ou le client qui sera utilisable comme un nouveau filtre Jinja, permettant de modifier des données.
Initsync
Un client ne peut pas commencer à traiter de nouveaux événements en toute sécurité sans disposer de l’ensemble du jeu de données complet préalable. Ainsi, le serveur est capable d’envoyer une séquence d’événements spécifique appelée initsync qui contiendra le modèle de données serveur et l’ensemble de données. Un client déjà initialisé l’ignorera silencieusement, mais un client non initialisé le traitera pour initialiser sa cible en ajoutant toutes les entrées fournies par la séquence initsync, puis commencera ensuite à traiter normalement les événements suivants.
Modèle de données (datamodel)
Comme il existe des différences entre eux, veuillez consulter modèle de données serveur et modèle de données client.
Type de données
Également nommé “type d’objet”. Un type de données avec son mapping d’attributs qui sera géré par Hermes.
Clé primaire
L’attribut type de données qui permet de distinguer une entrée d’une autre. Il doit évidemment être unique.
Modèle de données serveur
Configuration des types de données que le serveur doit gérer, avec leurs mapping d’attributs respectifs. Le nom de l’attribut distant est le nom de l’attribut utilisé sur la source de données.
Le modèle de données du serveur est construit en spécifiant les éléments suivants :
- Chaque type de données avec :
- sa clé primaire
- ses clés étrangères
- ses contraintes d’intégrité
- sa politique de conflit de fusion
- les noms et opérations de chacune de ses sources de données avec :
- son mapping d’attributs
- sa liste d’attributs spéciaux : attributs locaux, attributs secrets et attributs à ne mettre qu’en cache
- ses contraintes de fusion
Politique de conflit de fusion
Définit le comportement si un même attribut obtient des valeurs différentes sur différentes sources de données.
Contraintes de fusion
Permet de déclarer des contraintes pour garantir la cohérence des données lors de la fusion des données, lorsque le serveur interroge plusieurs sources de données.
Clés étrangères
Permet de déclarer des clés étrangères dans un type de données, que les clients utiliseront pour appliquer leur politique de clés étrangères. Voir Clés étrangères pour plus de détails.
Contraintes d’intégrité
Permet de déclarer des contraintes entre plusieurs types de données pour assurer la cohérence des données.
Attributs à ne mettre qu’en cache
Attributs du modèle de données qui seront uniquement stockés dans le cache, mais ne seront ni envoyés dans les événements, ni utilisés lors de la comparaison avec le cache.
Attributs secrets
Attributs du modèle de données qui contiendront des données sensibles, comme des mots de passe, et ne doivent jamais être stockés dans le cache ni affichés dans les journaux. Ils seront tout de même envoyés aux clients, à moins qu’ils ne soient également définis comme attributs locaux.
Comme ces attributs ne sont pas mis en cache, ils seront considérés comme ajoutés CHAQUE fois que le serveur interrogera les sources de données.
Attributs locaux
Attributs du modèle de données qui ne seront pas envoyés dans les événements, mis en cache ni utilisés lors de la comparaison avec le cache, mais qui pourront être utilisés dans les templates Jinja.
Modèle de données client
Configuration des types de données que le client doit gérer, avec leur mapping d’attributs. Le nom de l’attribut distant est le nom de l’attribut utilisé sur le modèle de données du serveur.
Si vous vous demandez pourquoi ce mapping est nécessaire, voici pourquoi :
- il permet la transformation des données locales via des filtres Jinja et les plugins d’attribut sur le client.
- il permet de réutiliser (et de partager) les plugins client sans nécessiter de modification du modèle de données serveur ni du code du plugin, mais simplement en modifiant le fichier de configuration client.
Le modèle de données client est construit en spécifiant les éléments suivants :
- Chaque type de données avec :
- le nom de son type de données distant correspondant, appelé
hermesType - son mapping d’attributs
- le nom de son type de données distant correspondant, appelé
Mapping d’attributs
Également appelé “attrsmapping”. Mapping (clé/valeur) qui associe le nom de l’attribut interne (en tant que clé) à celui distant (en tant que valeur). Le distant peut être un modèle Jinja pour transformer des données avec des filtres Jinja et des plugins d’attribut.
Fonctionnement
Explications sur le fonctionnement ou la structure de certains composants clés.
Sous-sections de Fonctionnement
hermes-server
Explications sur le fonctionnement ou la structure de certains composants clés d’hermes-server.
Sous-sections de hermes-server
Workflow
hermes-server
- 1. charge son cache local
- 2. vérifie si son schéma de données a changé depuis la dernière exécution, et émet les événements supprimés résultants (le cas échéant), ainsi que le nouveau schéma de données
- 3. récupère toutes les données requises par son modèle de données depuis la ou les sources de données
- 3.1. applique les contraintes de fusion
- 3.2. fusionne les données
- 3.3. remplace les incohérences et les conflits de fusion par les valeurs en cache
- 3.4. applique les contraintes d’intégrité
- 4. génère un différentiel entre son cache et les données distantes récupérées
- 5. boucle sur chaque type de différence : ajout, modification, suppression
- 5.1. pour chaque type de différence, boucle sur chaque type de données selon leur ordre de déclaration dans le modèle de données, à l’exception du type de différence supprimé, pour lequel il s’agit de l’ordre de déclaration inverse
- 5.1.1. boucle sur chaque différence du type de données actuel
- 5.1.1.1. génère l’événement correspondant
- 5.1.1.2. émet l’événement sur le bus de messages
- 5.1.1.3. si l’événement a été émis avec succès :
- 5.1.1.3.1. exécute l’action
commit_onedu modèle de données le cas échéant - 5.1.1.3.2. met à jour le cache pour refléter la nouvelle valeur de l’élément affecté par l’événement
- 5.1.1.3.1. exécute l’action
- 5.1.1. boucle sur chaque différence du type de données actuel
- 5.1. pour chaque type de différence, boucle sur chaque type de données selon leur ordre de déclaration dans le modèle de données, à l’exception du type de différence supprimé, pour lequel il s’agit de l’ordre de déclaration inverse
- 6. une fois que tous les événements ont été émis
- 6.1. exécute l’action
commit_alldu modèle de données le cas échéant - 6.2. enregistre le cache sur le disque
- 6.1. exécute l’action
- 7. attend
updateIntervalet recommence à partir de l’étape3.si l’application n’a pas été invitée à s’arrêter
Si une exception est levée à l’étape 2., cette étape est recommencée jusqu’à ce qu’elle réussisse.
Si une exception est levée aux étapes 3. à 7., le cache est enregistré sur le disque et le serveur recommence à partir de l’étape 3..
Contraintes d'intégrité
Hermes-server peut gérer plusieurs types de données, avec des liens (clés étrangères) entre eux, et peut leur appliquer des contraintes d’intégrité.
Illustrons cela avec un cas d’utilisation typique d’utilisateurs / groupes / membres de groupes.
classDiagram
direction BT
GroupsMembers <-- Users
GroupsMembers <-- Groups
class Users{
user_id
...
}
class Groups{
group_id
...
}
class GroupsMembers{
user_id
group_id
integrity() _SELF.user_id in Users_pkeys and _SELF.group_id in Groups_pkeys
}
Dans ce scénario, les entrées dans GroupsMembers qui ont un user_id qui n’existe pas dans Users, ou un group_id qui n’existe pas dans Groups seront ignorées silencieusement.
Pour plus de détails, veuillez consulter integrity_constraints dans la configuration d’hermes-server.
Agrégation de données multi-sources
Dans un scénario multi-sources, Hermes peut aggréger les entrées provenant de plusieurs sources comme si elles provenaient d’une seule, et éventuellement appliquer des contraintes d’agrégation pour garantir la cohérence des données.
Prenons un cas d’utilisation, avec un ensemble de données universitaires utilisées par Hermes pour gérer les comptes utilisateurs. Les données du personnel et des étudiants sont stockées sur deux sources de données distinctes. Hermes pourra fusionner les deux sources de données dans un seul type Users virtuel, tout en s’assurant qu’il n’y ait pas de collision entre les clés primaires.
Ici, nous avons deux sources de données distinctes pour un même type de données.
classDiagram
direction BT
Users <|-- Users_employee
Users <|-- Users_students
class Users{
user_id
login
mail
merge_constraints() s.user_id mustNotExist in e.user_id
}
class Users_students{
s.user_id
s.login
s.mail
}
class Users_employee{
e.user_id
e.login
e.mail
}
Dans ce scénario, les entrées dans Users_students qui ont un user_id qui existe dans Users_employee seront ignorées silencieusement.
Mais les entrées dans Users_employee qui ont un user_id qui existe dans Users_students seront toujours traitées.
Pour plus de détails, veuillez consulter pkey_merge_constraint et merge_constraints dans la configuration d’hermes-server.
Fusion de données multi-sources
Dans un scénario multi-sources, Hermes peut recomposer des entrées provenant de plusieurs sources en fusionnant leurs données et en définissant éventuellement des contraintes de fusion pour assurer la cohérence des données.
Prenons un cas d’utilisation, où Hermes gère des comptes utilisateurs. Les données principales et le nom du profil wifi sont stockés sur deux sources de données distinctes. Hermes pourra agréger les deux sources de données dans un seul objet Users virtuel, tout en s’assurant que les clés primaires de la seconde source existent dans la première.
Ici, nous avons deux sources de données distinctes pour une même entrée.
classDiagram
direction BT
Users <|-- Users_main
Users <|-- Users_auxiliary
class Users{
user_id
login
mail
wifi_profile
merge_constraints() a.user_id mustAlreadyExist in m.user_id
}
class Users_auxiliary{
a.user_id
a.wifi_profile
}
class Users_main{
m.user_id
m.login
m.mail
}
Dans ce scénario, les entrées dans Users_auxiliary qui ont un user_id qui n’existe pas dans Users_main seront ignorées silencieusement.
Mais les entrées dans Users_main qui ont un user_id qui n’existe pas dans Users_auxiliary seront traitées, et donc l’entrée Users résultante n’aura pas de valeur wifi_profile.
Pour plus de détails, veuillez consulter pkey_merge_constraint et merge_constraints dans la configuration d’hermes-server.
Événements émis
Catégories d’événements
Un événement appartient toujours à l’une de ces catégories :
-
base: événement standard, peut être de type :dataschema: propage le nouveau schéma de données aux clients, après une mise à jour du modèle de données du serveuradded: une nouvelle entrée a été ajoutée au type de données spécifié, avec des attributs et des valeurs spécifiésremoved: l’entrée de la clé primaire spécifiée a été supprimée du type de données spécifiémodified: l’entrée de la clé primaire spécifiée a été modifiée. Contient uniquement les attributs ajoutés, modifiés et supprimés avec leurs nouvelles valeurs
-
initsync: indique que l’événement fait partie d’une séquence initsync, peut être de type :init-start: début d’une séquence initsync, contient également le schéma de données actueladded: une nouvelle entrée a été ajoutée au type de données spécifié, avec les attributs et les valeurs spécifiés. Lorsque le serveur envoie le contenu de son cache pour initialiser les clients, les entrées ne peuvent qu’être ajoutéesinit-stop: fin d’une séquence initsync
Fichiers cache
_hermes-server.json
Contient l’état du serveur :
-
lastUpdate: datetime.datetime | NoneDate et heure de la dernière mise à jour.
-
errors: dict[str, dict[str, dict[str, Any]]]Dictionnaire contenant les erreurs actuelles, pour pouvoir notifier de tout changement.
-
exception: str | NoneChaîne contenant la dernière trace d’exception.
_dataschema.json
Contient le schéma de données, construit depuis le modèle de données. Ce fichier cache permet au serveur de traiter l’étape 2. de Workflow.
DataType.json
Un fichier par type de données déclaré dans Datamodel, contenant le cache des données de ce type de données, sous forme de liste de dictionnaires. Chaque dictionnaire de la liste représente une entrée.
hermes-client
Explications sur le fonctionnement ou la structure de certains composants clés d’hermes-client.
Sous-sections de hermes-client
Workflow
hermes-client
- 1. charge son modèle de données à partir du fichier de configuration
- 2. s’il existe, charge le modèle de données précédent depuis le cache
- 3. le cas échéant, notifie les avertissements du modèle de données : type distant et attributs distants présents dans le modèle de données, mais pas dans le schéma de données
- 4. si un schéma distant existe dans le cache, charge la file d’erreurs depuis le cache
- 5. si le client n’a pas encore été initialisé (aucune séquence initSync complète n’a encore été traitée) :
- 5.1. traite la séquence initSync, si une séquence initSync complète est disponible sur le bus de messages
- 5.2. redémarre à partir de l’étape
5.
- 6. si le client a déjà été initialisé (une séquence initSync complète a déjà été traitée) :
- 6.1. s’il s’agit de la première itération de la boucle (l’étape
7.n’a jamais été atteinte) :- 6.1.1. si le modèle de données dans la configuration diffère de celui mis en cache, traite la mise à jour du modèle de données :
- 6.1.1.1 génère les événements de suppression pour toutes les entrées des types de données supprimés, les traite et purge les fichiers de cache de ces types de données
- 6.1.1.2 génère un différentiel entre les données mises en cache construites sur le modèle de données précédent et les mêmes données converties au nouveau modèle de données, puis génère les événements correspondants et les traite
- 6.1.1. si le modèle de données dans la configuration diffère de celui mis en cache, traite la mise à jour du modèle de données :
- 6.2. si
errorQueue_retryIntervalest écoulé depuis la dernière tentative, réessaye de traiter les événements présents dans la file d’erreurs - 6.3. si
trashbin_purgeIntervalest écoulé depuis la dernière tentative, purge les objets expirés de la corbeille - 6.4. boucle sur tous les événements disponibles sur le bus de messages et traite chacun d’eux pour appeler sa méthode de gestion des événements correspondante lorsqu’elle existe dans le plugin client
- 6.1. s’il s’agit de la première itération de la boucle (l’étape
- 7. lorsqu’au moins un événement a été traité ou si l’application a été invitée à s’arrêter :
- 7.1. enregistre les fichiers cache de la file d’erreurs, de l’application, des données
- 7.2. appelle la méthode de gestion de l’événement spécial
onSavelorsqu’elle existe dans le plugin client - 7.3. notifie tout changement dans la file d’erreurs
- 8. redémarre à partir de l’étape
5.si l’application n’a pas été invitée à s’arrêter
Si une exception est levée à l’étape 6.1.1, elle sera considérée comme une erreur fatale, notifiée et le client s’arrêtera.
Si une exception est levée aux étapes 5. à 6., elle est notifiée, son événement est ajouté à la file d’erreurs et le client redémarre à partir de l’étape 7..
Traitement des événements
Comme le modèle de données sur le serveur diffère de celui sur le client, les clients doivent convertir les événements distants reçus sur le bus de messages en événements locaux. Si l’événement local résultant est vide (le type de données ou les attributs modifiés dans l’événement distant ne sont pas définis sur le modèle de données client), l’événement est ignoré.
Lors de la mise à jour du modèle de données client, le client peut générer des événements locaux qui n’ont pas d’événement distant correspondant, i.e. pour mettre à jour une valeur d’attribut calculée avec un modèle Jinja qui vient d’être modifié.
flowchart TB
subgraph Hermes-client
direction TB
datamodelUpdate[["mise à jour du modèle de données"]]
remoteevent["Événement distant"]
localevent["Événement local"]
eventHandler(["Méthode de gestion de l'événement du plugin client"])
end
datamodelUpdate-->|génère|localevent
MessageBus["Bus de message"]-->|fournit|remoteevent
remoteevent-->|convertit en|localevent
localevent-->|passe à la bonne|eventHandler
eventHandler-->|traite|Target["Cible"]
classDef external fill:#fafafa,stroke-dasharray: 5 5
class MessageBus,Target external
Clés étrangères
Il arrive que les objets soient liés entre eux par des clés étrangères. Lorsqu’une erreur survient sur un objet dont la clé primaire fait référence à celle d’un ou plusieurs autres objets “parents”, il peut être souhaitable d’interrompre le traitement de tout ou partie des événements de ces objets parents jusqu’à ce que ce premier événement ait été correctement traité. Cela peut être réalisé en ajoutant les événements des objets parents à la file d’attente des erreurs au lieu de tenter de les traiter.
La première chose à faire est de déclarer les clés étrangères via hermes-server.datamodel.data-type-name.foreignkeys dans la configuration d’hermes-server. Le serveur ne fera rien d’autre avec ces clés étrangères que de les propager aux clients.
Ensuite, il faut définir la politique à appliquer aux clients via hermes-client.foreignkeys_policy dans chaque configuration hermes-client. Il y en a trois possibles :
disabled: Aucun événement, la politique est désactivée. Probablement pas pertinent dans la plupart des cas, mais pourrait peut-être être utile à quelqu’un un jour.on_remove_event: Uniquement sur les événements removed. Devrait suffire dans la plupart des cas.on_every_event: Sur tous les types d’événements (added, modified, removed). Pour assurer une cohérence parfaite quoi qu’il arrive.
Auto remédiation
Parfois, un événement peut être stocké dans la file d’erreurs en raison d’un problème de données (e.g. un nom de groupe avec un point terminal génère une erreur sur Active Directory). Si le point terminal est ensuite supprimé du nom de groupe sur la source de données, l’événement modified sera stocké dans la file d’erreurs et ne sera pas traité tant que le précédent n’aura pas été traité, ce qui ne pourra jamais se produire sans procéder à une opération risquée et non-souhaitable : l’édition manuelle du fichier cache du client.
L’auto remédiation résout ce type de problèmes en fusionnant les événements d’un même objet dans la file d’erreurs. Elle n’est pas activée par défaut, car elle peut perturber l’ordre de traitement normal des événements.
Exemple
Prenons comme exemple un groupe créé avec un nom invalide. Comme son nom est invalide, le traitement de sa création échouera et l’événement sera stocké dans la file d’erreurs comme ceci :
flowchart TB
subgraph errorqueue [File d'erreurs]
direction TB
ev1
end
ev1["`**événement 1**
*eventtype*: added
*objType*: ADGroup
*objpkey*: 42
*objattrs*: {
grp_pkey: 42
name: 'NomInvalide.'
desc: 'Demo group'
}`"]
classDef leftalign text-align:left
class ev1 leftalign
Comme l’erreur a été notifiée, quelqu’un corrige le nom du groupe dans la source de données. Cette modification entraîne un événement modified correspondant. Cet événement modified ne sera pas traité, mais ajouté à la file d’erreurs puisque son objet a déjà un événement dans la file d’erreurs.
- sans auto-remédiation, tant que le premier événement n’aura pas été traité avec succès, le deuxième ne sera même pas tenté. La correction est bloquée.
- avec l’auto-remédiation, la file d’erreurs fusionne les deux événements, puis lors du prochain traitement de la file d’erreurs, l’événement résultant sera traité avec succès.
flowchart TB
subgraph errorqueuebis [avec auto remédiation]
direction TB
ev1bis
end
subgraph errorqueue [Sans auto remédiation]
direction TB
ev1
ev2
end
ev1["`**événement 1**
*eventtype*: added
*objType*: ADGroup
*objpkey*: 42
*objattrs*: {
grp_pkey: 42
name: 'NomInvalide.'
desc: 'Demo group'
}`"]
ev2["`**événement 2**
*eventtype*: modified
*objType*: ADGroup
*objpkey*: 42
*objattrs*: {
modified: {
name: 'NomValide'
}
}`"]
ev1bis["`**événement 1**
*eventtype*: added
*objType*: ADGroup
*objpkey*: 42
*objattrs*: {
grp_pkey: 42
name: 'NomValide'
desc: 'Demo group'
}`"]
classDef leftalign text-align:left
class ev1,ev2,ev1bis leftalign
Fichiers cache
_hermes-client-name.json
Contient l’état du client :
-
queueErrors: dict[str, str]Dictionnaire contenant l’ensemble des messages d’erreurs des objets en file d’erreurs, pour pouvoir notifier de tout changement.
-
datamodelWarnings: dict[str, dict[str, dict[str, Any]]]Dictionnaire contenant les avertissements actuels du modèle de données, pour pouvoir notifier de tout changement.
-
exception: str | NoneChaîne contenant la dernière trace d’exception.
-
initstartoffset: Any | NoneContient l’offset du premier message de la séquence initSync sur le bus de messages.
-
initstopoffset: Any | NoneContient l’offset du dernier message de la séquence initSync sur le bus de messages.
-
nextoffset: Any | NoneContient l’offset du prochain message à traiter sur le bus de messages.
_hermesconfig.json
Cache de la configuration précédente, utilisé pour pouvoir construire le modèle de données précédent et pour générer les expressions Jinja avec les plugins d’attributs.
_dataschema.json
Cache du dernier schéma de données, reçu d’hermes-server.
_errorqueue.json
Cache de la file d’erreurs.
RemoteDataType.json
Un fichier par type de données distantes, contenant toutes les entrées distantes, telles qu’elles ont été traitées par le client sans générer d’erreur.
Lorsque la file d’erreurs est vide, doit avoir le même contenu que RemoteDataType_complete__.json
RemoteDataType_complete__.json
Un fichier par type de données distantes, contenant toutes les entrées distantes, telles qu’elles sont sur le serveur.
Lorsque la file d’erreurs est vide, doit avoir le même contenu que RemoteDataType.json
trashbin_RemoteDataType.json
Uniquement si la corbeille est activée. Un fichier par type de données distant, contenant toutes les entrées distantes qui se trouvent dans la corbeille, telles qu’elles ont été traitées par le client sans générer d’erreur.
Lorsque la file d’erreurs est vide, doit avoir le même contenu que trashbin_RemoteDataType_complete__.json
trashbin_RemoteDataType_complete__.json
Uniquement si la corbeille est activée. Un fichier par type de données distant, contenant toutes les entrées distantes qui se trouvent dans la corbeille, telles qu’elles sont sur le serveur.
Lorsque la file d’erreurs est vide, doit avoir le même contenu que trashbin_RemoteDataType.json
__LocalDataType.json
Un fichier par type de données local, contenant toutes les entrées locales, telles qu’elles ont été traitées par le client sans générer d’erreur.
Lorsque la file d’erreurs est vide, doit avoir le même contenu que __LocalDataType_complete__.json
__LocalDataType_complete__.json
Un fichier par type de données local, contenant toutes les entrées locales, telles qu’elles sont sur le serveur.
Lorsque la file d’erreurs est vide, doit avoir le même contenu que __LocalDataType.json
__trashbin_LocalDataType.json
Uniquement si la corbeille est activée. Un fichier par type de données local, contenant toutes les entrées locales qui se trouvent dans la corbeille, telles qu’elles ont été traitées par le client sans générer d’erreur.
Lorsque la file d’erreurs est vide, doit avoir le même contenu que __trashbin_LocalDataType_complete__.json
__trashbin_LocalDataType_complete__.json
Uniquement si la corbeille est activée. Un fichier par type de données local, contenant toutes les entrées locales qui se trouvent dans la corbeille, telles qu’elles sont sur le serveur.
Lorsque la file d’erreurs est vide, doit avoir le même contenu que __trashbin_LocalDataType.json
Mise en service
Cette section contient tout ce dont vous avez besoin pour installer, configurer et exécuter Hermes.
Sous-sections de Mise en service
Mise en route
-
Identifiez vos prérequis :
- la ou les sources de données à utiliser et leur type, ainsi que les données que vous souhaitez capturer sur chacune d’elles. Une fois cela fait, identifiez si le ou les plugins de source de données correspondants existent
- choisissez (et éventuellement installez) le bus de messages que vous utiliserez
- identifiez le ou les plugins hermes-client dont vous aurez besoin
-
Installez Hermes en suivant la section Installation
-
Configurez hermes-server en suivant les sections suivantes :
- hermes pour les paramètres globaux
- configurez votre plugin producteur de bus de messages
- configurez votre ou vos plugins de source de données
- configurez vos plugins d’attribut, le cas échéant
- hermes-server pour les paramètres hermes-server et configurer votre modèle de données de serveur
-
Exécutez hermes-server en suivant la section Exécution et une fois qu’il a effectué avec succès sa première collecte de données, générez une séquence initsync à l’aide de la CLI hermes-server, comme expliqué dans la section Exécution
-
Configurez un premier client hermes en suivant les sections suivantes :
- hermes pour les paramètres globaux
- configurez votre plugin consommateur de bus de messages
- configurez vos plugins d’attribut, le cas échéant
- configurez votre plugin hermes-client
- hermes-client pour les paramètres génériques hermes-client et configurez votre modèle de données client
-
Exécutez le client hermes approprié en suivant la section Exécution
Installation
Configuration requise
- Python 3.10, 3.11, 3.12 ou 3.13 avec pip
- Exécution sous Linux (requis pour la CLI qui utilise un socket Unix)
- Un serveur de bus de messages, e.g. Apache Kafka - recommandé mais une implémentation sqlite est fournie
- direnv - uniquement si vous souhaitez utiliser le script d’aide
reset_venv
Guide d’installation
-
Télécharger et extraire l’archive de la dernière version d’hermes
-
(Facultatif) Si vous souhaitez réduire l’empreinte de l’installation, vous pouvez supprimer le répertoire
tests, le fichiertox.iniet tous les plugins inutiles en supprimant leurs répertoires dans :plugins/attributes/plugins/clients/plugins/datasources/plugins/messagebus_consumers/plugins/messagebus_producers/
Si votre installation est destinée à exécuter hermes-server uniquement (sans clients), vous pouvez supprimer les répertoires suivants :
clientsplugins/clients/plugins/messagebus_consumers
Si votre installation est destinée à exécuter un ou plusieurs hermes-client uniquement (sans serveur), vous pouvez supprimer les répertoires suivants :
serverplugins/datasourcesplugins/messagebus_producers
-
Configurer un venv et installer tous les pré-requis
- Automatiquement avec le script fourni
./reset_venv - Manuellement. Vous pouvez générer et installer les pré-requis Python avec les commandes suivantes :
cat "requirements.txt" "plugins/"*/*"/requirements.txt" > all_requirements.txt 2>/dev/null pip3 install -r all_requirements.txt - Automatiquement avec le script fourni
Configuration
Une application hermes recherchera son fichier de configuration YAML dans le répertoire de travail courant.
Le fichier de configuration doit être nommé APPNAME-config.yml, e.g. :
hermes-server-config.ymlpour server et server-clihermes-client-usersgroups_null-config.ymlpour client-usersgroups_null et client-usersgroups_null-cli
Les paramètres sont séparés en plusieurs sections YAML :
- hermes : paramètres partagés par le serveur et tous les clients
- hermes-server : paramètres du serveur
- hermes-client : paramètres partagés par tous les clients
- hermes-client-clientName : paramètres spécifiques à un plugin client
- hermes
- plugins
- attributes : paramètres des plugins d’attribut
- datasources : paramètres des plugins de source de données
- messagebus : paramètres des plugins producteur de bus de messages
- messagebus : paramètres des plugins consommateur de bus de messages
- plugins
Pour des raisons de sécurité, il peut être souhaitable d’autoriser certains utilisateurs à utiliser la CLI sans leur accorder un accès en lecture au fichier de configuration. Pour ce faire, il suffit simplement de créer un fichier de configuration CLI facultatif nommé APPNAME-cli-config.yml, e.g. :
hermes-server-cli-config.ymlpour server-clihermes-client-usersgroups_null-cli-config.ymlpour client-usersgroups_null-cli
Ce fichier ne doit contenir que les directives suivantes :
hermes:
cli_socket:
path: /path/to/cli/sockfile.sockSous-sections de Configuration
hermes
Paramètres partagés par le serveur et tous les clients.
Sous-sections principales :
hermes.umask
- Description : Définit le umask par défaut pour chaque fichier ou répertoire créé par l’application : répertoires cache, fichiers cache et fichiers journaux. Attention comme c’est une valeur octale, elle doit être préfixée par un
0. - Obligatoire : Non
- Type : integer
- Valeurs valides : 0000 - 0777
- Valeur par défaut : 0027
hermes.cache
Section obligatoire pour définir les paramètres du cache.
hermes.cache.dirpath
- Description : Chemin d’un répertoire existant où les fichiers de cache seront stockés.
- Obligatoire : Oui
- Type : string
hermes.cache.enable_compression
- Description : Si
true, tous les fichiers cache seront compressés avec gzip. - Obligatoire : Non
- Type : boolean
- Valeur par défaut :
true
hermes.cache.backup_count
- Description : À chaque sauvegarde, si le contenu du fichier a changé, Hermes conservera le contenu du cache précédent jusqu’au backup_count spécifié.
- Obligatoire : Non
- Type : integer
- Valeurs valides : 0 - 999999
- Valeur par défaut : 1
hermes.cli_socket
Active le socket CLI qui permet la communication entre l’application et sa CLI.
hermes.cli_socket.path
- Description : Chemin d’accès au fichier de socket CLI à créer/utiliser. Si ce chemin n’est pas spécifié, la CLI sera désactivée.
- Obligatoire : Non
- Type : string
hermes.cli_socket.owner
- Description : Nom de l’utilisateur qui sera propriétaire du fichier socket lors de sa création, tel qu’il serait transmis à chown.
Si ce nom n’est pas spécifié, le nom de l’utilisateur qui exécute le serveur hermes sera utilisé. - Obligatoire : Non
- Type : string
- Ignoré lorsque : dont_manage_sockfile vaut
true
hermes.cli_socket.group
- Description : Nom du groupe qui sera propriétaire du fichier socket lors de sa création, tel qu’il serait transmis à chown.
Si ce nom n’est pas spécifié, le nom de du groupe qui exécute le serveur hermes sera utilisé. - Obligatoire : Non
- Type : string
- Ignoré lorsque : dont_manage_sockfile vaut
true
hermes.cli_socket.mode
- Description : Les autorisations à appliquer au fichier socket lors de sa création, telles qu’elles seraient transmises à chmod.
Pour les personnes habituées à /usr/bin/chmod, rappelez-vous que les modes sont des nombres octaux et doivent être préfixés par un0.
Si le mode n’est pas spécifié et que le fichier socket n’existe pas, le umask par défaut du système sera utilisé lors de la définition du mode pour le fichier socket nouvellement créé.
Si le mode n’est pas spécifié et que le fichier socket existe, le mode du fichier socket existant sera utilisé. - Obligatoire : Non
- Type : integer
- Valeur par défaut : 00600
- Valeurs valides : 0 - 07777
- Ignoré lorsque : dont_manage_sockfile vaut
true
hermes.cli_socket.dont_manage_sockfile
- Description : Indique qu’Hermes ne doit pas gérer la création du fichier socket et utiliser le descripteur de fichier socket fourni par son processus parent (généralement SystemD).
Le socket créé doit être un socket stream AF_UNIX d’écoute.
Un et un seul socket doit être fourni : Hermes s’en assurera en vérifiant que la variable d’environnement SystemDLISTEN_FDSest définie sur1, et échouera dans le cas contraire. - Obligatoire : Non
- Type : boolean
- Valeur par défaut :
false
hermes.logs
Section obligatoire pour définir les paramètres des fichiers log.
hermes.logs.logfile
- Description : Chemin d’un répertoire existant où les fichiers journaux seront stockés. Si ce chemin n’est pas spécifié, aucun fichier journal ne sera stocké sur le disque.
- Obligatoire : Oui
- Type : string
hermes.logs.backup_count
- Description : Hermes assurera une rotation de son fichier log tous les jours à minuit et conservera les backup_count versions précédentes des fichiers journaux.
- Obligatoire : Non
- Type : integer
- Valeur par défaut : 7
- Valeurs valides : 0 - 999999
hermes.logs.verbosity
- Description : Verbosité de la journalisation.
- Obligatoire : Non
- Type : string
- Valeur par défaut : warning
- Valeurs valides :
- critical
- error
- warning
- info
- debug
hermes.logs.long_string_limit
- Description : Définit la limite (taille maximale) du contenu des attributs de chaîne à afficher dans les journaux.
Si le contenu d’un attribut de chaîne est supérieur à cette limite, il sera tronqué à cette limite et marqué comme LONG_STRING dans les journaux.
Peut être défini ànullpour désactiver cette fonctionnalité et toujours afficher l’intégralité de la chaîne dans les journaux. - Obligatoire : Non
- Type : integer
- Valeur par défaut : 512
- Valeurs valides : [1 - 999999] ou
null
hermes.mail
Section obligatoire pour définir les paramètres de messagerie (e-mail) permettant à Hermes de notifier les erreurs aux administrateurs.
Les e-mails contiendront 3 pièces jointes lorsque cela est possible : previous.txt, current.txt et diff.txt, contenant l’état précédent, l’état actuel et la différence entre les états précédent et actuel.
hermes.mail.server
- Description : Nom DNS ou adresse IP du relais SMTP.
- Obligatoire : Oui
- Type : string
hermes.mail.from
- Description : Adresse e-mail qui sera définie comme adresse d’expéditeur, avec la syntaxe de messagerie
Nom d'utilisateur <nom@example.com> - Obligatoire : Oui
- Type : string
hermes.mail.to
- Description : Adresse ou liste d’adresses de destination.
- Obligatoire : Oui
- Type : string | string[]
hermes.mail.compress_attachments
- Description : Indique si les pièces jointes doivent être compressées ou envoyées brutes.
- Obligatoire : Non
- Type : boolean
- Valeur par défaut :
true
hermes.mail.mailtext_maxsize
- Description : Taille maximale en octets pour le contenu de l’e-mail. Si la taille du contenu est supérieure à mailtext_maxsize, un message de substitution par défaut sera envoyé à la place.
- Obligatoire : Non
- Type : integer
- Valeur par défaut : 1048576 (1 Mo)
- Valeurs valides : >= 0
hermes.mail.attachment_maxsize
- Description : Taille maximale en octets pour une seule pièce jointe de l’e-mail. Si la taille de la pièce jointe est supérieure à attachment_maxsize, elle ne sera pas jointe à l’e-mail et un message l’indiquant sera ajouté au contenu de l’e-mail.
- Obligatoire : Non
- Type : integer
- Valeur par défaut : 5242880 (5 Mo)
- Valeurs valides : >= 0
hermes.plugins
Section obligatoire pour déclarer quels plugins doivent être chargés, avec leurs paramètres.
Elle est divisée en sous-sections par type de plugin.
hermes.plugins.attributes
Section facultative pour déclarer les plugins d’attributs à charger, ainsi que leurs paramètres.
Elle doit contenir une sous-section nommée avec le nom du plugin contenant elle-même une sous-section facultative settings avec les paramètres du plugin, à renseigner selon la documentation du plugin.
Exemple avec le plugin ldapPasswordHash :
hermes:
# (...)
plugins:
attributes:
ldapPasswordHash:
settings:
default_hash_types:
- SMD5
- SSHA
- SSHA256
- SSHA512
# (...)hermes.plugins.datasources
Section obligatoire pour hermes-server pour déclarer la ou les sources de données et leurs paramètres. Si elle est définie sur un hermes-client, elle sera ignorée silencieusement.
Un même plugin de source de données peut être utilisé pour interroger plusieurs sources de données, donc pour chaque source de données souhaitée, vous devez déclarer une sous-section avec le nom souhaité de la source de données (qui sera utilisé dans le modèle de données), contenant deux entrées obligatoires :
type(string) : le plugin de source de données à utiliser pour cette source de données.settings(subsection) : les paramètres du plugin de source de données pour cette source de données selon la documentation du plugin.
Exemple :
hermes:
# (...)
plugins:
datasources:
my_oracle1_datasource:
type: oracle
settings:
login: HERMES_DUMMY
password: "DuMmY_p4s5w0rD"
port: 1234
server: dummy.example.com
sid: DUMMY
my_oracle2_datasource:
type: oracle
settings:
login: HERMES_DUMMY2
password: "DuMmY2_p4s5w0rD"
port: 1234
server: dummy.example.com
sid: DUMMY2
my_ldap_datasource:
type: ldap
settings:
uri: ldaps://dummy.example.com:636
binddn: cn=binddn,dc=example,dc=com
bindpassword: DuMmY_p4s5w0rD
basedn: dc=example,dc=com
# (...)hermes.plugins.messagebus
Section obligatoire pour déclarer le plugin de bus de messages à charger, ainsi que ses paramètres. Évidemment, vous devez configurer exactement un plugin de bus de messages.
- Sur
hermes-server, il recherchera le plugin producteur de bus de messages dans le répertoireplugins/messagebus_producers/. - Sur
hermes-client, il recherchera le plugin consommateur de bus de messages dans le répertoireplugins/messagebus_consumers/.
Elle doit contenir une sous-section nommée avec le nom du plugin contenant une sous-section facultative settings avec les paramètres du plugin à définir selon la documentation du plugin producteur de bus de messages ou consommateur de bus de messages.
Exemple avec le plugin producteur sqlite :
hermes:
# (...)
plugins:
messagebus:
sqlite:
settings:
uri: /path/to/hermes/sqlite/message/bus.sqlite
retention_in_days: 30
# (...)hermes-server
Paramètres du serveur.
Sous-sections principales :
- hermes-server
hermes-server
hermes-server.updateInterval
- Description : Intervalle entre deux actualisations des données, en secondes.
- Obligatoire : Oui
- Type : integer
- Valeurs valides : >= 0
hermes-server.datamodel
Sous-section obligatoire utilisée pour configurer le modèle de données serveur.
Pour chaque type de données requis, une sous-section avec le nom souhaité du type de données doit être créée et configurée. Le nom du type de données DOIT commencer par un caractère alphanumérique.
Bien évidemment, au moins un type de données doit être configuré.
L’ordre de déclaration des types de données est important pour garantir l’intégrité des données :
- les événements d’ajout/modification seront traités dans l’ordre de déclaration
- les événements de suppression seront traités dans l’ordre de déclaration inversé
Vous devez donc d’abord déclarer les types de données qui ne dépendent d’aucun autre type, puis les types qui ont des dépendances (clés étrangères) à ceux déclarés au dessus.
hermes-server.datamodel.data-type-name.primarykeyattr
- Description : Le nom de l’attribut du modèle de données utilisé comme clé primaire. Si la clé primaire est un tuple, vous pouvez déclarer une liste de noms.
- Obligatoire : Oui
- Type : string | string[]
hermes-server.datamodel.data-type-name.toString
- Description : Template Jinja permettant de définir la représentation d’une entrée dans les fichiers journaux.
- Obligatoire : Non
- Type : string
hermes-server.datamodel.data-type-name.on_merge_conflict
- Description : Définit le comportement si un même attribut a une valeur différente sur plusieurs sources.
- Obligatoire : Non
- Type : string
- Valeur par défaut : use_cached_entry
- Valeurs valides :
keep_first_value: utilise la première valeur rencontrée dans l’ordre de déclaration des sources.use_cached_entry: ignore les données récupérées et continue à utiliser l’entrée en cache jusqu’à ce que le conflit soit résolu.
hermes-server.datamodel.data-type-name.foreignkeys
-
Description : Permet de déclarer des clés étrangères dans un type de données, que les clients utiliseront pour appliquer leur politique de clés étrangères. Voir Clés étrangères pour plus de détails.
Le paramètre est un dictionnaire avec la clé primaire du type de données actuel comme clé, un dictionnaire avec deux entrées comme valeur, faisant référence au type de données parentfrom_objtypeet à sa clé primairefrom_attr.
Bien que cela puisse sembler intuitif, la déclaration de clés étrangères ne créera aucune règle de contrainte d’intégrité automatiquement.AvertissementQue ce soit pour le type de données courant ou pour le parent, les attributs doivent être des clés primaires de leurs types respectifs.
De plus, la clé primaire du parent ne peut pas être multivaluée (un tuple).Ces contraintes pourraient éventuellement être assouplies un jour, mais pour l’instant aucun cas d’utilisation pertinent n’en justifie le besoin.
Exemple :
foreignkeys: group_id: from_objtype: SRVGroups from_attr: gid user_id: from_objtype: SRVUsers from_attr: uid -
Obligatoire : Non
-
Type : dict[string, dict[string, string]]
-
Valeur par défaut : {}
hermes-server.datamodel.data-type-name.integrity_constraints
-
Description : Contraintes d’intégrité entre les types de données, en Jinja.
ATTENTION : cela peut être affreusement lent, vous devriez donc essayer de faire au plus simple et idéalement vous limiter aux clés primaires.Les variables Jinja disponibles sont :
- _SELF : l’objet actuel
- data-type-name_pkeys : un ensemble (set) contenant chaque clé primaire du type de données spécifié.
- data-type-name : une liste de dictionnaires contenant chaque entrée du type de données spécifié.
Exemple :
integrity_constraints: - "{{ _SELF.pkey_attr in OTHERDataType_pkeys }}" -
Obligatoire : Non
-
Type : string[]
-
Valeur par défaut : []
hermes-server.datamodel.data-type-name.sources
Sous-section obligatoire listant les sources de données à utiliser pour récupérer les données du type de données courant.
Pour chaque source de données utilisée, une sous-section portant son nom doit être définie et configurée.
Évidemment, au moins une source de données doit être configurée.
L’ordre de déclaration des sources de données est important pour la fusion des données si hermes-server.datamodel.data-type-name.on_merge_conflict est défini sur keep_first_value, ou si hermes-server.datamodel.data-type-name.sources.datasource-name.pkey_merge_constraint est utilisé.
hermes-server.datamodel.data-type-name.sources.datasource-name.fetch
Sous-section obligatoire pour configurer la requête utilisée pour récupérer les données.
Selon le plugin de source de données utilisé, query et vars peuvent être facultatifs : configurez-les selon la documentation du plugin de source de données.
hermes-server.datamodel.data-type-name.sources.datasource-name.fetch.type
- Description : Indique au plugin de source de données le type de requête à exécuter. Il s’agit généralement de
fetch. - Obligatoire : Oui
- Type : string
- Valeurs valides :
fetch: Indique que le plugin doit récupérer des données, sans modifier le jeu de données.add: Indique que le plugin doit ajouter des données au jeu de données.delete: Indique que le plugin doit supprimer des données du jeu de données.modify: Indique que le plugin doit modifier des données dans le jeu de données.
hermes-server.datamodel.data-type-name.sources.datasource-name.fetch.query
-
Description : La requête à envoyer à la source de données. Peut être un template Jinja.
Les variables Jinja disponibles sont :
- REMOTE_ATTRIBUTES : la liste des noms d’attributs distants utilisés dans
attrsmapping. Peut être utile pour générer des requêtes SQL avec les données requises sans utiliser de wildcards ni saisir manuellement la liste d’attributs. - CACHED_VALUES : le cache de la requête précédente. Une liste de dictionnaires dans laquelle chaque dictionnaire est une entrée avec le nom d’attribut comme clé et sa valeur comme valeur. Peut être utile pour filtrer la requête à l’aide d’une valeur mise en cache.
- data-type-name_pkeys : un ensemble (set) contenant chaque clé primaire du type de données spécifié. Le type de données de la variable doit être déclaré avant celui actuel dans le modèle de données, sinon le contenu de la variable sera toujours vide car son contenu sera récupéré après celui du type de données actuel.
- data-type-name : une liste de dictionnaires contenant chaque entrée du type de données spécifié. Le type de données de la variable doit être déclaré avant celui courant dans le modèle de données, sinon le contenu de la variable sera toujours vide car son contenu sera récupéré après celui du type de données courant.
- REMOTE_ATTRIBUTES : la liste des noms d’attributs distants utilisés dans
-
Obligatoire : Non
-
Type : string
hermes-server.datamodel.data-type-name.sources.datasource-name.fetch.vars
Sous-section facultative contenant certaines variables à transmettre au plugin de source de données.
Le nom de la variable comme clé et sa valeur comme valeur. Chaque valeur peut être un template Jinja.
Les variables Jinja disponibles sont :
- REMOTE_ATTRIBUTES : la liste des noms d’attributs distants utilisés dans
attrsmapping. Peut être utile pour générer des requêtes SQL avec les données requises sans utiliser de wildcards ni saisir manuellement la liste d’attributs. - CACHED_VALUES : le cache de la requête précédente. Une liste de dictionnaires dans laquelle chaque dictionnaire est une entrée avec le nom d’attribut comme clé et sa valeur comme valeur.
- data-type-name_pkeys : un ensemble (set) contenant chaque clé primaire du type de données spécifié. Le type de données de la variable doit être déclaré avant celui actuel dans le modèle de données, sinon le contenu de la variable sera toujours vide car son contenu sera récupéré après celui du type de données actuel.
- data-type-name : une liste de dictionnaires contenant chaque entrée du type de données spécifié. Le type de données de la variable doit être déclaré avant celui courant dans le modèle de données, sinon le contenu de la variable sera toujours vide car son contenu sera récupéré après celui du type de données courant.
hermes-server.datamodel.data-type-name.sources.datasource-name.commit_one
Sous-section facultative permettant de configurer une requête à exécuter à chaque fois qu’une entrée a été traitée sans erreur.
Selon le plugin de source de données utilisé, query et vars peuvent être facultatifs : configurez-les selon la documentation du plugin de source de données.
commit_one et commit_all s’excluent mutuellement : vous pouvez définir aucun ou l’un d’entre eux, mais pas les deux en même temps.
hermes-server.datamodel.data-type-name.sources.datasource-name.commit_one.type
- Description : Indique au plugin de source de données le type de requête à exécuter.
- Obligatoire : Oui
- Type : string
- Valeurs valides :
fetch: Indique que le plugin doit récupérer des données, sans modifier le jeu de données.add: Indique que le plugin doit ajouter des données au jeu de données.delete: Indique que le plugin doit supprimer des données du jeu de données.modify: Indique que le plugin doit modifier des données dans le jeu de données.
hermes-server.datamodel.data-type-name.sources.datasource-name.commit_one.query
-
Description : La requête à envoyer à la source de données. Peut être un template Jinja.
Les variables Jinja disponibles sont :
- REMOTE_ATTRIBUTES : la liste des noms d’attributs distants utilisés dans
attrsmapping. Peut être utile pour générer des requêtes SQL avec les données requises sans utiliser de wildcards ni saisir manuellement la liste d’attributs. - ITEM_CACHED_VALUES : la valeur de l’entrée courante en cache. Un dictionnaire avec le nom d’attribut comme clé et sa valeur comme valeur.
- ITEM_FETCHED_VALUES : la valeur de l’entrée courante fraîchement récupérée. Un dictionnaire avec le nom d’attribut comme clé et sa valeur comme valeur.
- REMOTE_ATTRIBUTES : la liste des noms d’attributs distants utilisés dans
-
Obligatoire : Non
-
Type : string
hermes-server.datamodel.data-type-name.sources.datasource-name.commit_one.vars
Sous-section facultative contenant certaines variables à transmettre au plugin de source de données.
Le nom de la variable comme clé et sa valeur comme valeur. Chaque valeur peut être un template Jinja.
Les variables Jinja disponibles sont :
- REMOTE_ATTRIBUTES : la liste des noms d’attributs distants utilisés dans
attrsmapping. Peut être utile pour générer des requêtes SQL avec les données requises sans utiliser de wildcards ni saisir manuellement la liste d’attributs. - ITEM_CACHED_VALUES : la valeur de l’entrée courante en cache. Un dictionnaire avec le nom d’attribut comme clé et sa valeur comme valeur.
- ITEM_FETCHED_VALUES : la valeur de l’entrée courante fraîchement récupérée. Un dictionnaire avec le nom d’attribut comme clé et sa valeur comme valeur.
hermes-server.datamodel.data-type-name.sources.datasource-name.commit_all
Sous-section facultative permettant de configurer une requête à exécuter une fois que toutes les données ont été traitées sans erreur.
Selon le plugin de source de données utilisé, query et vars peuvent être facultatifs : configurez-les selon la documentation du plugin de source de données.
commit_all et commit_one s’excluent mutuellement : vous pouvez définir aucun ou l’un d’entre eux, mais pas les deux en même temps.
hermes-server.datamodel.data-type-name.sources.datasource-name.commit_all.type
- Description : Indique au plugin de source de données le type de requête à exécuter.
- Obligatoire : Oui
- Type : string
- Valeurs valides :
fetch: Indique que le plugin doit récupérer des données, sans modifier le jeu de données.add: Indique que le plugin doit ajouter des données au jeu de données.delete: Indique que le plugin doit supprimer des données du jeu de données.modify: Indique que le plugin doit modifier des données dans le jeu de données.
hermes-server.datamodel.data-type-name.sources.datasource-name.commit_all.query
-
Description : La requête à envoyer à la source de données. Peut être un template Jinja.
Les variables Jinja disponibles sont :
- REMOTE_ATTRIBUTES : la liste des noms d’attributs distants utilisés dans
attrsmapping. Peut être utile pour générer des requêtes SQL avec les données requises sans utiliser de wildcards ni saisir manuellement la liste d’attributs. - CACHED_VALUES : le cache de la requête précédente. Une liste de dictionnaires dans laquelle chaque dictionnaire est une entrée avec le nom d’attribut comme clé et sa valeur comme valeur. Peut être utile pour filtrer la requête à l’aide d’une valeur mise en cache.
- FETCHED_VALUES: les valeurs fraîchement récupérées. Une liste de dictionnaires dans laquelle chaque dictionnaire est une entrée avec le nom d’attribut comme clé et sa valeur comme valeur. Peut être utile pour filtrer la requête à l’aide d’une valeur mise en cache.
- REMOTE_ATTRIBUTES : la liste des noms d’attributs distants utilisés dans
-
Obligatoire : Non
-
Type : string
hermes-server.datamodel.data-type-name.sources.datasource-name.commit_all.vars
Sous-section facultative contenant certaines variables à transmettre au plugin de source de données.
Le nom de la variable comme clé et sa valeur comme valeur. Chaque valeur peut être un template Jinja.
Les variables Jinja disponibles sont :
- REMOTE_ATTRIBUTES : la liste des noms d’attributs distants utilisés dans
attrsmapping. Peut être utile pour générer des requêtes SQL avec les données requises sans utiliser de wildcards ni saisir manuellement la liste d’attributs. - CACHED_VALUES : le cache de la requête précédente. Une liste de dictionnaires dans laquelle chaque dictionnaire est une entrée avec le nom d’attribut comme clé et sa valeur comme valeur. Peut être utile pour filtrer la requête à l’aide d’une valeur mise en cache.
- FETCHED_VALUES: les valeurs fraîchement récupérées. Une liste de dictionnaires dans laquelle chaque dictionnaire est une entrée avec le nom d’attribut comme clé et sa valeur comme valeur. Peut être utile pour filtrer la requête à l’aide d’une valeur mise en cache.
hermes-server.datamodel.data-type-name.sources.datasource-name.attrsmapping
Sous-section obligatoire pour configurer le mapping d’attributs. Les attributs HERMES comme clés, les attributs DISTANTS (sur la source de données) comme valeurs.
Une liste de plusieurs attributs distants peut être définie pour plus de commodité, leurs valeurs non-NULL seront combinées dans une liste.
Les valeurs NULL et les listes vides ne seront pas chargées.
Un template Jinja peut être défini comme valeur. Si vous le faites, la valeur entière doit être un
template. Vous ne pouvez pas définir "{{ ATTRIBUTE.split('separator') }} SOME_NON_JINJA_ATTR".
Ceci est indispensable pour permettre à l’application d’identifier les REMOTE_ATTRIBUTES
Les variables Jinja disponibles sont :
- chaque attribut distant pour le type de données courant et la source de données dont sa valeur extraite, uniquement si sa valeur n’est pas
NULLet n’est pas une liste vide. - ITEM_CACHED_VALUES: la valeur de l’entrée courante en cache. Un dictionnaire avec le nom d’attribut comme clé et sa valeur comme valeur.
hermes-server.datamodel.data-type-name.sources.datasource-name.secrets_attrs
- Description : Définit les attributs qui contiendront des données sensibles, comme des mots de passe.
Ceci indique à Hermes de ne pas les mettre en cache. Les noms d’attribut définis ici doivent exister en tant que clés dans attrsmapping. Ils seront envoyés aux clients à moins qu’ils ne soient également définis dans local_attrs. Comme ils ne sont pas mis en cache, ils seront considérés comme ajoutés CHAQUE FOIS que le serveur sera redémarré, et les événements consécutifs à cet ajout seront émis. - Obligatoire : Non
- Type : string[]
hermes-server.datamodel.data-type-name.sources.datasource-name.cacheonly_attrs
- Description : Définit les attributs qui seront uniquement stockés dans le cache.
Ils ne seront pas envoyés dans les événements, ni utilisés pour calculer le différentiel avec le cache. Les noms d’attribut définis ici doivent exister en tant que clés dans attrsmapping. - Obligatoire : Non
- Type : string[]
hermes-server.datamodel.data-type-name.sources.datasource-name.local_attrs
- Description : Définit les attributs qui ne seront pas envoyés aux clients, mis en cache ni utilisés pour calculer le différentiel avec le cache.
Ils ne seront pas envoyés dans les événements, ni utilisés pour calculer le différentiel avec le cache. Les noms d’attributs définis ici doivent exister en tant que clés dans attrsmapping. - Obligatoire : Non
- Type : string[]
hermes-server.datamodel.data-type-name.sources.datasource-name.pkey_merge_constraint
- Description : Contraintes sur les clés primaires lors de la fusion : elles seront appliquées lors de la fusion des sources de données.
Comme la fusion sera appliquée dans l’ordre de déclaration des sources de données dans le fichier de configuration, la première contrainte source sera ignorée (puisqu’elle sera créée et non fusionnée). Ensuite, les premières données sources seront fusionnées avec celles de la deuxième source conformément à lapkey_merge_constraintde la deuxième. Ensuite, les données résultantes seront fusionnées avec les données sources de la troisième source conformément à lapkey_merge_constraintde la troisième, etc. - Obligatoire : Non
- Type : string
- Valeur par défaut :
noConstraint - Valeurs valides :
noConstraint: n’applique aucune contrainte de fusionmustNotExist: la clé primaire de la source actuelle ne doit pas exister dans la précédente (dans l’ordre de déclaration des sources de données), sinon les données de la source actuelle seront suppriméesmustAlreadyExist: la clé primaire de la source actuelle doit déjà exister dans la précédente (dans l’ordre de déclaration des sources de données), sinon les données de la source actuelle seront suppriméesmustExistInBoth: la clé primaire de la source actuelle doit déjà exister dans la précédente (dans l’ordre de déclaration des sources de données), sinon les données des deux sources seront supprimées
hermes-server.datamodel.data-type-name.sources.datasource-name.merge_constraints
- Description : Contraintes de fusion avancées avec des règles Jinja.
Jinja vars available are:Avertissement
Horriblement lent, évitez au maximum de les utiliser.
- _SELF: the data type item in current datasource being currently merged.
- For each datasource declared in current data type:
- datasource-name_pkeys: un ensemble (set) contenant chaque clé primaire du type de données courant dans la source de données spécifiée.
- datasource-name: les valeurs fraîchement récupérées depuis la source de données spéifiée. Une liste de dictionnaires dans laquelle chaque dictionnaire est une entrée avec le nom d’attribut comme clé et sa valeur comme valeur.
Remarque
si pkey_merge_constraint est défini, il sera appliqué avant
merge_constraints, et les variables Jinja contiendront les valeurs résultantes.
- Obligatoire : Non
- Type : string[]
hermes-client
Paramètres partagés par tous les clients.
Sous-sections principales :
- hermes-client
- datamodel
- data-type-name
- datamodel
hermes-client.autoremediation
- Description : Politique d’auto remédiation à appliquer dans la file d’erreurs pour les événements concernant un même objet.
Avertissement
L’activation de cette fonctionnalité peut modifier l’ordre de traitement normal des événements : si vos types de données ne sont liés que par des clés primaires, cela ne devrait pas poser de problème, mais si les liens entre eux sont plus complexes, vous devriez vraiment réfléchir à ce qui pourrait mal se passer avant de l’activer.
e.g. avec la politique
maximumet la corbeille activée, l’auto remédiation supprimera les deux événements lorsqu’un événementaddedsera suivi d’un événementremoved. Sans erreur, l’objet aurait été créé puis stocké dans la corbeille, mais dans ce cas, il ne sera jamais créé.Voir comment fonctionne l’auto remédiation pour plus de détails.
- Obligatoire : Non
- Type : string
- Valeur par défaut :
disabled - Valeurs valides :
disabled: pas d’auto remédiation, les événements sont empilés tels quels (par défaut).conservative: fusionne uniquement les événementsaddedetmodifiedentre eux.- fusionne un événement
addedavec un événementmodifiedsuivant. - fusionne deux événements
modifiedsuccessifs.
- fusionne un événement
maximum: fusionne tous les événements qui peuvent être fusionnés.- fusionne un événement
addedavec un événementmodifiedsuivant. - fusionne deux événements
modifiedsuccessifs. - supprime les deux événements lorsqu’un événement
addedest suivi d’un événementremoved. - fusionne un événement
removedsuivi d’un événementaddeddans un événementmodified. - supprime un événement
modifiedlorsqu’il est suivi d’un événementremoved.
- fusionne un événement
hermes-client.foreignkeys_policy
- Description : Définit les types d’événements qui seront placés dans la file d’erreurs si l’objet qui les concerne est le parent (par clé étrangère) d’un objet déjà présent dans la file d’erreurs. Voir Clés étrangères pour plus de détails.
- Obligatoire : Non
- Type : string
- Valeur par défaut :
on_remove_event - Valeurs valides :
disabled: Aucun événement, la politique est désactivée.on_remove_event: Uniquement sur les événements removed.on_every_event: Tous les types d’événements (added, modified, removed)
hermes-client.errorQueue_retryInterval
- Description : Nombre de minutes entre deux tentatives de traitement des événements en erreur.
- Obligatoire : Non
- Type : integer
- Valeur par défaut : 60 (1 heure)
- Valeurs valides : 1 - 65535
hermes-client.trashbin_purgeInterval
- Description : Nombre de minutes entre deux tentatives de purge de la corbeille..
- Obligatoire : Non
- Type : integer
- Valeur par défaut : 60 (1 heure)
- Valeurs valides : 1 - 65535
- Ignoré lorsque : trashbin_retention vaut
0/unset
hermes-client.trashbin_retention
- Description : Nombre de jours pendant lesquels les données supprimées doivent être conservées dans la corbeille avant de les supprimer définitivement.
0/unsetdésactive la corbeille : les données seront immédiatement supprimées. - Obligatoire : Non
- Type : integer
- Valeur par défaut : 0 (pas de corbeille)
- Valeurs valides : >= 0
hermes-client.updateInterval
- Description : Nombre de secondes pendant lesquelles attendre une fois qu’il n’y a plus d’événements disponibles sur le bus de messages.
- Obligatoire : Non
- Type : integer
- Valeur par défaut : 5
- Valeurs valides : >= 0
hermes-client.useFirstInitsyncSequence
- Description : Si
true, le client utilisera la première (plus ancienne) séquence initsync disponible sur le bus de messages. Sifalse, la dernière (plus récente) sera utilisée. - Obligatoire : Non
- Type : boolean
- Valeur par défaut :
false
hermes-client.datamodel
Sous-section obligatoire utilisée pour configurer le modèle de données client.
Pour chaque type de données requis, une sous-section avec le nom souhaité du type de données doit être créée et configurée. Le nom du type de données DOIT commencer par un caractère alphanumérique.
Bien évidemment, au moins un type de données doit être configuré.
hermes-client.datamodel.data-type-name.hermesType
- Description : Nom du type de données correspondant sur
hermes-server. - Obligatoire : Oui
- Type : string
hermes-client.datamodel.data-type-name.toString
- Description : Template Jinja permettant de définir la représentation d’une entrée dans les fichiers journaux.
- Obligatoire : Non
- Type : string
hermes-client.datamodel.data-type-name.attrsmapping
Sous-section pour configurer le mapping d’attributs. Les attributs CLIENT comme clés, les attributs DISTANTS (identifiés comme des attributs HERMES sur le serveur hermes) comme valeurs.
Un template Jinja peut être défini comme valeur. Dans ce cas, toute valeur en dehors du template sera utilisée comme chaîne brute et non comme un nom d’attribut distant.
Les variables Jinja disponibles sont :
- chaque attribut distant pour le type de données actuel, uniquement si sa valeur n’est pas
NULLet n’est pas une liste vide.
Si vous n’utilisez pas leur valeur, il n’est pas nécessaire de déclarer un mapping pour les clés primaires. Pour certains types de données, vous pouvez omettre le mapping d’attributs, ce qui équivaut à définir un modèle de données vide : il ne contiendra donc que sa ou ses clés primaires.
Plugins
Plugins serveur et clients
- attributes: filtres Jinja personnalisés pour transformer les données
Plugins serveur
- datasources: connecteur entre un type de source de données et hermes-server
- messagebus_producers: connecteur entre hermes-server et un type de bus de messages
Plugins clients
- hermes-client: implémentation d’hermes-client pour propager des données vers une cible spécifique
- messagebus_consumers: connecteur entre un type de bus de messages et hermes-client
Sous-sections de Plugins
plugins d'attribut
-
crypto_RSA_OAEP : chiffrer/déchiffrer des chaînes avec des clés RSA asymétriques, en utilisant PKCS#1 OAEP, un chiffrement asymétrique basé sur RSA et le remplissage OAEP
-
ldapPasswordHash: générer des hachages LDAP de formats spécifiés à partir d’une chaîne contenant un mot de passe en clair
-
regex_search: effectuer une recherche dans une chaîne pour en extraire la partie qui correspond à l’expression régulière spécifiée
Sous-sections de plugins d'attribut
crypto_RSA_OAEP
Description
Ce plugin permet de chiffrer/déchiffrer des chaînes avec des clés RSA asymétriques, en utilisant PKCS#1 OAEP, un chiffrement asymétrique basé sur RSA et le remplissage OAEP.
Configuration
Vous pouvez configurer autant de clés que vous le souhaitez dans les paramètres du plugin. Une clé peut être utilisée pour chiffrer ou déchiffrer, mais pas les deux. Le plugin déterminera s’il s’agit d’une opération de chiffrement ou de déchiffrement en fonction du type de clé : déchiffrement pour les clés privées et chiffrement pour les clés publiques.
hermes:
plugins:
attributes:
crypto_RSA_OAEP:
settings:
keys:
# Nom de la clé, vous pouvez définir ce que vous voulez
encrypt_to_messagebus:
# Type de hachage, lors du déchiffrement, vous devez évidemment
# utiliser la même valeur qui a été utilisée pour le chiffrement
hash: SHA3_512
# Clé publique RSA utilisée pour chiffrer
# ATTENTION - CETTE CLÉ EST FAIBLE ET PUBLIQUE, NE L'UTILISEZ JAMAIS
rsa_key: |-
-----BEGIN PUBLIC KEY-----
MCgCIQCy2W1bAPOa1JIeLuV8qq1Qg7h0jxpf8QCik11H9xZcfwIDAQAB
-----END PUBLIC KEY-----
# Une autre clé
decrypt_from_messagebus:
hash: SHA3_512
# Clé privée RSA utilisée pour déchiffrer
# ATTENTION - CETTE CLÉ EST FAIBLE ET PUBLIQUE, NE L'UTILISEZ JAMAIS
rsa_key: |-
-----BEGIN RSA PRIVATE KEY-----
MIGrAgEAAiEAstltWwDzmtSSHi7lfKqtUIO4dI8aX/EAopNdR/cWXH8CAwEAAQIh
AKfflFjGNOJQwvJX3Io+/juxO+HFd7SRC++zBD9paZqZAhEA5OtjZQUapRrV/aC5
NXFsswIRAMgBtgpz+t0FxyEXdzlcTwUCEHU6WZ8M2xU7xePpH49Ps2MCEQC+78s+
/WvfNtXcRI+gJfyVAhAjcIWzHC5q4wzgL7psbPGy
-----END RSA PRIVATE KEY----- Les valeurs valides pour hash sont :
- SHA224
- SHA256
- SHA384
- SHA512
- SHA3_224
- SHA3_256
- SHA3_384
- SHA3_512
Utilisation
crypto_RSA_OAEP(value: bytes | str, keyname: str) → strUne fois que tout est configuré, vous pouvez chiffrer les données avec la clé encrypt_to_messagebus comme ceci dans un filtre Jinja :
password_encrypted: "{{ PASSWORD_CLEAR | crypto_RSA_OAEP('encrypt_to_messagebus') }}"
password_decrypted: "{{ PASSWORD_ENCRYPTED | crypto_RSA_OAEP('decrypt_from_messagebus') }}"Vous pouvez même déchiffrer et rechiffrer immédiatement les données avec une autre clé comme ceci :
password_reencrypted: "{{ PASSWORD_ENCRYPTED | crypto_RSA_OAEP('decrypt_from_datasource') | crypto_RSA_OAEP('encrypt_to_messagebus') }}"ldapPasswordHash
Description
Ce plugin permet de générer des hachages LDAP aux formats spécifiés, depuis une chaîne contenant un mot de passe en clair.
Configuration
Vous pouvez configurer une liste facultative de types de hachage par défaut dans les paramètres du plug-in. Cette liste sera utilisée si les types de hachage ne sont pas spécifiés dans les arguments du filtre, sinon les types de hachage spécifiés seront utilisés.
hermes:
plugins:
attributes:
ldapPasswordHash:
settings:
default_hash_types:
- SMD5
- SSHA
- SSHA256
- SSHA512Les valeurs valides pour default_hash_types sont :
- MD5
- SHA
- SMD5
- SSHA
- SSHA256
- SSHA512
Utilisation
ldapPasswordHash(password: str, hashtypes: None | str | list[str] = None) → list[str]Une fois que tout est configuré, vous pouvez générer votre liste de hachages comme ceci dans un filtre Jinja :
# Contiendra une liste de hachages de PASSWORD_CLEAR selon les paramètres
# de default_hash_types : SMD5, SSHA, SSHA256, SSHA512
ldap_password_hashes: "{{ PASSWORD_CLEAR | ldapPasswordHash }}"
# Contiendra une liste contenant uniquement le hachage SSHA512 de PASSWORD_CLEAR
ldap_password_hashes: "{{ PASSWORD_CLEAR | ldapPasswordHash('SSHA512') }}"
# Contiendra une liste contenant uniquement les hachages SSHA256
# et SSHA512 de PASSWORD_CLEAR
ldap_password_hashes: "{{ PASSWORD_CLEAR | ldapPasswordHash(['SSHA256', 'SSHA512']) }}"regex_search
Description
Ce plugin permet d’effectuer une recherche dans une chaîne pour en extraire la partie qui correspond à l’expression régulière spécifiée.
Configuration
Rien à configurer pour le plugin.
hermes:
plugins:
attributes:
regex_search:Utilisation
regex_search(string: str, regex: str, multiline=False, ignorecase=False) → list[str] | NoneComme ce plugin n’est qu’une adaptation du filtre regex_search_filter d’Ansible, vous pouvez également consulter sa documentation.
# Sera sans valeur (contiendra None)
regex_results: "{{ 'foo' | regex_search('bar') }}"
# Sera sans valeur (contiendra None)
regex_results: "{{ 'foobar' | regex_search('foo$') }}"
# Contiendra 'foo'
regex_results: "{{ 'foobar' | regex_search('^foo') }}"
# Contiendra 'foobar'
regex_results: "{{ 'foobar' | regex_search('^foo.*$') }}"
# Ci-dessous, une approche plus complexe, où LDAP_PASSWORD_HASHES est une liste de hachages de mots de passe LDAP :
# LDAP_PASSWORD_HASHES:
# - "{SMD5}NGnIxNg+ZqB3XwhQK/jCRDWWpUQYVbwg"
# - "{SSHA}9u8ZbEbeLPLI2f4isG7YjJsz6sfonjQAfbbadQ=="
# - "{SSHA256}l0rZ10MhH6jKGogg2qFvCdiNAqkKVH9OuL0R3FgWRrV4mIaYM2cnYQ=="
# - "{SSHA512}zKR46tmGg0NKq1FdkmLGZCqXqfnApvFRHSTW4H0Sem9zJH66mgZ6/aB/aypGX+dLAI02akd9lZbplX6y0Typzzir8RIKh6cw,"
# Contiendra ['{SSHA}9u8ZbEbeLPLI2f4isG7YjJsz6sfonjQAfbbadQ==']
regex_results: "{{ LDAP_PASSWORD_HASHES | map('regex_search', '^{SSHA}.*$') | reject('none') | list }}"
# Contiendra ['{SSHA}9u8ZbEbeLPLI2f4isG7YjJsz6sfonjQAfbbadQ==', '{SSHA512}zKR46tmGg0NKq1FdkmLGZCqXqfnApvFRHSTW4H0Sem9zJH66mgZ6/aB/aypGX+dLAI02akd9lZbplX6y0Typzzir8RIKh6cw,']
regex_results: "{{ LDAP_PASSWORD_HASHES | map('regex_search', '^({SSHA}|{SSHA512}).*$') | reject('none') | list }}"plugins de source de données
-
ldap: utiliser un serveur LDAP comme source de données
-
oracle: utiliser une base de données Oracle comme source de données
-
postgresql: utiliser une base de données PostgreSQL comme source de données
-
sqlite: utiliser une base de données SQLite comme source de données
Sous-sections de plugins de source de données
ldap
Description
Ce plugin permet d’utiliser un serveur LDAP comme source de données.
Configuration
Les paramètres de connexion sont requis dans la configuration du plugin.
hermes:
plugins:
datasources:
# Nom de la source. Utilisez ce que vous voulez. Sera utilisé dans le modèle de données
your_source_name:
type: ldap
settings:
# OBLIGATOIRE : URI du serveur LDAP
uri: ldaps://ldap.example.com:636
# OBLIGATOIRE : identifiants de connexion au serveur LDAP
binddn: cn=account,dc=example,dc=com
bindpassword: s3cReT_p4s5w0rD
# OBLIGATOIRE : DN de base LDAP
basedn: dc=example,dc=com
ssl: # Facultatif
# Chemin vers le fichier PEM avec les certificats CA
cafile: /path/to/INTERNAL-CA-chain.crt # Facultatif
# Chemin vers le fichier de certificat au format PEM pour l'authentification du certificat client, nécessite de définir keyfile
certfile: /path/to/client.crt # Facultatif
# Chemin vers le fichier de clé privée du certificat au format PEM pour l'authentification du certificat client, nécessite de définir certfile
keyfile: /path/to/client.pem # Facultatif
# Facultatif. Par défaut : false.
# Comme le client n'a pas connaissance du schéma LDAP, il ne peut pas savoir si
# un attribut est à valeur unique ou à valeurs multiples. Par défaut, il
# s'adapte à la valeur qui lui est renvoyée : si elle est unique, il la renverra
# dans son type de base, et s'il y en a plusieurs, il la renverra sous forme de liste.
# Si ce paramètre est activé, toutes les valeurs seront toujours renvoyées dans une liste.
always_return_values_in_list: trueUtilisation
L’utilisation diffère selon le type d’opération spécifié
fetch
Récupérer les entrées depuis le serveur LDAP.
hermes-server:
datamodel:
oneDataType:
sources:
your_source_name: # 'your_source_name' a été défini dans les paramètres du plugin
fetch:
type: fetch
vars:
# Facultatif : le basedn à utiliser pour l'opération 'fetch'.
# Si ce paramètre n'est pas défini, le paramètre basedn de la
# configuration sera utilisé
base: "ou=exampleOU,dc=example,dc=com"
# Facultatif : la portée de l'opération 'fetch'
# Les valeurs valides sont :
# - base : pour rechercher l'objet "de base" lui-même
# - one, onelevel : pour rechercher les enfants immédiats de l'objet "de base"
# - sub, subtree : pour rechercher l'objet "de base" et tous ses descendants
# Si non défini, "subtree" sera utilisé
scope: subtree
# Facultatif : le filtre LDAP à utiliser pour l'opération 'fetch'
# Si non défini, "(objectClass=*)" sera utilisé
filter: "(objectClass=*)"
# Facultatif : les attributs à récupérer, sous forme de liste de chaînes
# Si non défini, tous les attributs de chaque entrée sont renvoyés
attrlist: "{{ REMOTE_ATTRIBUTES }}"add
Ajouter des entrées au serveur LDAP.
hermes-server:
datamodel:
oneDataType:
sources:
your_source_name: # 'your_source_name' a été défini dans les paramètres du plugin
fetch:
type: add
vars:
# Facultatif : une liste d'entrées à ajouter.
# Si elle n'est pas définie, une liste vide sera utilisée (et rien ne sera ajouté)
addlist:
# OBLIGATOIRE : le DN de l'entrée. S'il n'est pas spécifié, l'entrée
# sera silencieusement ignorée
- dn: uid=newentry1,ou=exampleOU,dc=example,dc=com
# Facultatif : les attributs à ajouter à l'entrée
add:
# Créer l'attribut s'il n'existe pas et lui ajouter la valeur "value"
"attrnameToAdd": "value",
# Créer l'attribut s'il n'existe pas et lui ajouter les valeurs
# "value1" et "value2"
"attrnameToAddList": ["value1", "value2"],
- dn: uid=newentry2,ou=exampleOU,dc=example,dc=com
# ...delete
Supprimer des entrées du serveur LDAP.
hermes-server:
datamodel:
oneDataType:
sources:
your_source_name: # 'your_source_name' a été défini dans les paramètres du plugin
fetch:
type: delete
vars:
# Facultatif : une liste d'entrées à supprimer.
# Si elle n'est pas définie, une liste vide sera utilisée (et rien ne sera supprimé)
dellist:
# OBLIGATOIRE : le DN de l'entrée. S'il n'est pas spécifié, l'entrée
# sera silencieusement ignorée
- dn: uid=entryToDelete1,ou=exampleOU,dc=example,dc=com
- dn: uid=entryToDelete2,ou=exampleOU,dc=example,dc=com
# ...modify
Modifier des entrées sur le serveur LDAP.
hermes-server:
datamodel:
oneDataType:
sources:
your_source_name: # 'your_source_name' a été défini dans les paramètres du plugin
fetch:
type: modify
vars:
# Facultatif : une liste d'entrées à modifier.
# Si elle n'est pas définie, une liste vide sera utilisée (et rien ne sera modifié)
modlist:
# OBLIGATOIRE : le DN de l'entrée. S'il n'est pas spécifié, l'entrée
# sera silencieusement ignorée
- dn: uid=entryToModify1,ou=exampleOU,dc=example,dc=com
# Facultatif : les attributs à ajouter à l'entrée
add:
# Créer l'attribut s'il n'existe pas et lui ajouter la valeur "value"
attrnameToAdd: value
# Créer l'attribut s'il n'existe pas et lui ajouter les valeurs
# "value1" et "value2"
attrnameToAddList: [value1, value2]
# Facultatif : les attributs de l'entrée à modifier
modify:
# Créer l'attribut s'il n'existe pas et remplacer toutes ses valeurs
# par la valeur "value"
attrnameToModify: newvalue
# Créer l'attribut s'il n'existe pas et remplacer toutes ses valeurs
# par les valeurs "newvalue1" and "newvalue2"
attrnameToModifyList: [newvalue1, newvalue2]
# Facultatif: les attributs de l'entrée à supprimer
delete:
# Supprimer l'attribut spécifié et toutes ses valeurs
attrnameToDelete: null
# Supprimer la valeur "value" de l'attribut spécifié.
# Génère une erreur si la valeur est manquante
attrnameToDeleteValue: value
# Supprimer les valeurs "value1" et "value2" de l'attribut spécifié.
# Génère une erreur si une des valeurs est manquante
attrnameToDeleteValueList: [value1, value2]
- dn: uid=entryToModify2,ou=exampleOU,dc=example,dc=com
# ...oracle
Description
Ce plugin permet d’utiliser une base de données Oracle comme source de données.
Configuration
Les paramètres de connexion sont requis dans la configuration du plugin.
hermes:
plugins:
datasources:
# Nom de la source. Utilisez ce que vous voulez. Sera utilisé dans le modèle de données
your_source_name:
type: oracle
settings:
# OBLIGATOIRE : le nom DNS ou l'adresse IP du serveur de base de données
server: dummy.example.com
# OBLIGATOIRE : le port de connexion à la base de données
port: 1234
# OBLIGATOIRE : le "service name" de la base de données. Ne peut pas être défini si 'sid' est défini
service_name: DUMMY.example.com
# OBLIGATOIRE : le "sid" de la base de données. Ne peut pas être défini si 'service_name' est défini
sid: DUMMY
# OBLIGATOIRE : les identifiants de connexion à la base de données
login: HERMES_DUMMY
password: "DuMmY_p4s5w0rD"Utilisation
Spécifiez une requête. Si vous souhaitez utiliser des valeurs provenant du cache, il est possible de les indiquer dans un dictionnaire vars, et y faire référence en spécifiant le nom de variable (clé) préfixé par un double-points : dans la requête : cela nettoiera automatiquement les données dans la requête pour limiter les risques d’injection SQL.
Les noms d’exemple dans vars sont préfixés par sanitized_ pour donner plus de clarté, mais cela n’a rien d’obligatoire.
hermes-server:
datamodel:
oneDataType:
sources:
your_source_name: # 'your_source_name' a été défini dans les paramètres du plugin
fetch:
type: fetch
query: >-
SELECT {{ REMOTE_ATTRIBUTES | join(', ') }}
FROM AN_ORACLE_TABLE
commit_one:
type: modify
query: >-
UPDATE AN_ORACLE_TABLE
SET
valueToSet = :sanitized_valueToSet
WHERE pkey = :sanitized_pkey
vars:
sanitized_pkey: "{{ ITEM_FETCHED_VALUES.pkey }}"
sanitized_valueToSet: "{{ ITEM_FETCHED_VALUES.valueToSet }}"postgresql
Description
Ce plugin permet d’utiliser une base de données PostgreSQL comme source de données.
Configuration
Les paramètres de connexion sont requis dans la configuration du plugin.
hermes:
plugins:
datasources:
# Nom de la source. Utilisez ce que vous voulez. Sera utilisé dans le modèle de données
your_source_name:
type: postgresql
settings:
# OBLIGATOIRE : le nom DNS ou l'adresse IP du serveur de base de données
server: dummy.example.com
# OBLIGATOIRE : le port de connexion à la base de données
port: 1234
# MANDATORY: le nom de la base de données
dbname: DUMMY
# OBLIGATOIRE : les identifiants de connexion à la base de données
login: HERMES_DUMMY
password: "DuMmY_p4s5w0rD"Utilisation
Spécifiez une requête. Si vous souhaitez utiliser des valeurs provenant du cache, il est possible de les indiquer dans un dictionnaire vars, et y faire référence en spécifiant le nom de variable (clé) entouré par %()s dans la requête : cela nettoiera automatiquement les données dans la requête pour limiter les risques d’injection SQL. Voir l’exemple ci-dessous.
Les noms d’exemple dans vars sont préfixés par sanitized_ pour donner plus de clarté, mais cela n’a rien d’obligatoire.
hermes-server:
datamodel:
oneDataType:
sources:
your_source_name: # 'your_source_name' a été défini dans les paramètres du plugin
fetch:
type: fetch
query: >-
SELECT {{ REMOTE_ATTRIBUTES | join(', ') }}
FROM A_POSTGRESQL_TABLE
commit_one:
type: modify
query: >-
UPDATE A_POSTGRESQL_TABLE
SET
valueToSet = %(sanitized_valueToSet)s
WHERE pkey = %(sanitized_pkey)s
vars:
sanitized_pkey: "{{ ITEM_FETCHED_VALUES.pkey }}"
sanitized_valueToSet: "{{ ITEM_FETCHED_VALUES.valueToSet }}"sqlite
Description
Ce plugin permet d’utiliser une base de données SQLite comme source de données.
Configuration
Les paramètres de connexion sont requis dans la configuration du plugin.
hermes:
plugins:
datasources:
# Nom de la source. Utilisez ce que vous voulez. Sera utilisé dans le modèle de données
your_source_name:
type: sqlite
settings:
# OBLIGATOIRE : le chemin vers le fichier de base de données
uri: /path/to/sqlite.dbUtilisation
Spécifiez une requête. Si vous souhaitez utiliser des valeurs provenant du cache, il est possible de les indiquer dans un dictionnaire vars, et y faire référence en spécifiant le nom de variable (clé) préfixé par un double-points : dans la requête : cela nettoiera automatiquement les données dans la requête pour limiter les risques d’injection SQL.
Les noms d’exemple dans vars sont préfixés par sanitized_ pour donner plus de clarté, mais cela n’a rien d’obligatoire.
hermes-server:
datamodel:
oneDataType:
sources:
your_source_name: # 'your_source_name' a été défini dans les paramètres du plugin
fetch:
type: fetch
query: >-
SELECT {{ REMOTE_ATTRIBUTES | join(', ') }}
FROM AN_SQLITE_TABLE
commit_one:
type: modify
query: >-
UPDATE AN_SQLITE_TABLE
SET
valueToSet = :sanitized_valueToSet
WHERE pkey = :sanitized_pkey
vars:
sanitized_pkey: "{{ ITEM_FETCHED_VALUES.pkey }}"
sanitized_valueToSet: "{{ ITEM_FETCHED_VALUES.valueToSet }}"plugins producteur de bus de messages
Sous-sections de plugins producteur de bus de messages
kafka
Description
Ce plugin permet à hermes-server d’envoyer les événements produits vers un serveur Apache Kafka.
Configuration
Il est possible de se connecter au serveur Kafka sans authentification, ou avec une authentification SSL (TLS).
hermes:
plugins:
messagebus:
kafka:
settings:
# OBLIGATOIRE : la liste des serveurs Kafka pouvant être utilisés
servers:
- dummy.example.com:9093
# Facultatif : quelle version de l'API Kafka utiliser. Si elle n'est
# pas définie, la version de l'API sera détectée au démarrage et
# indiquée dans les fichiers log.
# Ne définissez pas cette directive à moins que vous ne rencontriez
# des erreurs "kafka.errors.NoBrokersAvailable : NoBrokersAvailable"
# générées par un appel "self.check_version()".
api_version: [2, 6, 0]
# Facultatif : limite stricte de la taille d'un message envoyé à Kafka.
# Vous devez définir une valeur plus élevée si vos messages Kafka sont
# susceptibles de dépasser la valeur par défaut de 1 Mo ou si vous
# rencontrez l'erreur
# "MessageSizeTooLargeError: The message is xxx bytes when
# serialized which is larger than the maximum request size you
# have configured with the max_request_size configuration".
# Par défaut : 1048576.
max_request_size: 1048576
# Facultatif : active l'authentification SSL. Si active, les 3 options
# ci-dessous doivent être définies
ssl:
# OBLIGATOIRE : fichier de certificat hermes-server qui sera
# utilisé pour l'authentification
certfile: /path/to/.hermes/dummy.crt
# OBLIGATOIRE : Chemin vers le fichier de clé privée du certificat
# hermes-server
keyfile: /path/to/.hermes/dummy.pem
# OBLIGATOIRE : le certificat CA de la PKI
cafile: /path/to/.hermes/INTERNAL-CA-chain.crt
# OBLIGATOIRE : le sujet sur lequel publier les événements
topic: hermessqlite
Description
Ce plugin permet à hermes-server d’envoyer les événements produits vers une base de données SQLite.
Configuration
Pour imiter le comportement d’autres bus de messages qui suppriment les messages une fois certaines conditions remplies, retention_in_days peut être définie. Les messages plus anciens que le nombre de jours spécifié seront automatiquement supprimés.
hermes:
plugins:
messagebus:
sqlite:
settings:
# OBLIGATOIRE :
uri: /path/to/.hermes/bus.sqlite
retention_in_days: 1plugins hermes-client
Les plugins clients sont regroupés par catégories servant le même objectif sur différents types de cibles. Il n’existe actuellement qu’une seule catégorie de plugins :
- usersgroups : gérer les utilisateurs, les groupes, les mots de passe des utilisateurs et l’appartenance des utilisateurs aux groupes
Sous-sections de plugins hermes-client
usergroups
Gère les utilisateurs, les groupes, les mots de passe des utilisateurs et l’appartenance des utilisateurs aux groupes.
Les clients disponibles sont :
- usersgroups_adpypsrp : stocke les données dans un Active Directory via des commandes Powershell via
pypsrp. - usersgroups_bsspartage: stocke les données dans le tableau de bord de PARTAGE via son API, gérée par libPythonBssApi.
- usersgroups_flatfiles_emails_of_groups : génére un fichier txt plat par
Groups, contenant les adresses e-mail de ses membres (une par ligne). - usersgroups_kadmin_heimdal : stocke les données sur un serveur Kerberos Heimdal.
- usersgroups_ldap : stocke les données dans un annuaire LDAP.
- usersgroups_null : ne fait rien d’autre que de générer des logs.
Sous-sections de usergroups
adpypsrp
Description
Ce client traite les événements de type Users, Groups et UserPasswords, et stocke les données dans un Active Directory via des commandes Powershell sur pypsrp.
La liste des paramètres standardAttributes contient les paramètres des cmdlet utilisées pour les utilisateurs (New-ADUser / Set-ADUser) et les groupes (New-ADGroup / Set-ADGroup).
La liste de paramètres otherAttributes peut contenir des noms d’attributs LDAP (ldapDisplayName) disponibles pour gérer ceux qui ne sont pas représentés par les paramètres de cmdlet pour les utilisateurs et les groupes.
Les clés du modèle de données local DOIVENT exister dans standardAttributes ou otherAttributes, et seront utilisées comme paramètres de cmdlet avec leurs valeurs associées, permettant de gérer tous les attributs AD.
GroupsMembers associera uniquement un User à un Group.
SubGroupsMembers associera uniquement un Group à un Group, permettant ainsi de gérer les groupes imbriqués.
Pour éviter les problèmes de sécurité et les cas particuliers avec la corbeille, un mot de passe aléatoire complexe est défini lors de la création de l’utilisateur. Ce mot de passe inconnu sera écrasé par le prochain événement UserPassword de User. Cela évite d’avoir un compte activé sans mot de passe.
La corbeille ne fait que désactiver le compte.
Configuration
hermes-client-usersgroups_adpypsrp:
WinRM: # Pour plus de détails sur les options, vous pouvez consulter https://pypi.org/project/pypsrp/ - "Connection"
# OBLIGATOIRE : URI et port du serveur AD
host: radon1.in.insa-strasbourg.fr
port: 5986
# OBLIGATOIRE : identifiants de connexion au serveur AD
login: administrator
password: "s3cReT_p4s5w0rD"
# Par défaut : true
ssl: true
# Par défaut : true
ssl_cert_validation: false
# Par défaut : true
credssp_disable_tlsv1_2: true
# Par défaut : "auto". Les valeurs valides sont [auto, always, never]
encryption: always
# Par défaut : "wsman"
path: "wsman"
# Par défaut : "negotiate". Les valeurs valides sont [basic, certificate, negotiate, ntlm, kerberos, credssp]
auth: kerberos
# Par défaut : "WSMAN". Permet de définir la partie service du SPN calculé, utilisée lors de l'authentification au serveur.
# Ceci n'est valide que si l'authentification négociée est Kerberos, ou si Kerberos a été explicitement choisi.
# Si vous obtenez l'erreur "Server not found in Kerberos database", vous pouvez essayer de définir "HTTP" ici.
negotiate_service: WSMAN
AD_domain:
# OBLIGATOIRE : nom de domaine AD et DN
name: in.insa-strasbourg.fr
dn: DC=in,DC=insa-strasbourg,DC=fr
# OBLIGATOIRE : OUs où les utilisateurs et les groupes seront stockés
users_ou: OU=INSA,OU=People,DC=in,DC=insa-strasbourg,DC=fr
groups_ou: OU=INSA,OU=Groups,DC=in,DC=insa-strasbourg,DC=fr
# Facultatif, permet de forcer l'ajout de chaque utilisateur à la liste de groupes spécifiée
# L'ajout de l'appartenance au groupe se fait uniquement à la création de l'utilisateur : tout
# changement de valeur de ce paramètre n'impactera que les utilisateurs qui seront créés ultérieurement
Users_mandatory_groups:
- MandatoryGroup1
- MandatoryGroup2
# Définit les paramètres de cmdlet qui peuvent être définis et le type valide de la valeur qui leur est associée
# Vous devriez vraiment le définir tel quel.
standardAttributes:
Users:
AccountExpirationDate: "<DateTime>"
AccountNotDelegated: "<Boolean>"
AllowReversiblePasswordEncryption: "<Boolean>"
AuthenticationPolicy: "<ADAuthenticationPolicy>"
AuthenticationPolicySilo: "<ADAuthenticationPolicySilo>"
AuthType: "<ADAuthType>"
CannotChangePassword: "<Boolean>"
ChangePasswordAtLogon: "<Boolean>"
City: "<String>"
Company: "<String>"
CompoundIdentitySupported: "<Boolean>"
Country: "<String>"
# Credential: "<PSCredential>" # Inutile : spécifie les identifiants de connexion du compte utilisateur à utiliser pour effectuer cette tâche
Department: "<String>"
Description: "<String>"
DisplayName: "<String>"
Division: "<String>"
EmailAddress: "<String>"
EmployeeID: "<String>"
EmployeeNumber: "<String>"
Enabled: "<Boolean>"
Fax: "<String>"
GivenName: "<String>"
HomeDirectory: "<String>"
HomeDrive: "<String>"
HomePage: "<String>"
HomePhone: "<String>"
KerberosEncryptionType: "<ADKerberosEncryptionType>"
LogonWorkstations: "<String>"
Manager: "<ADUser>"
MobilePhone: "<String>"
Office: "<String>"
OfficePhone: "<String>"
Organization: "<String>"
OtherName: "<String>"
PasswordNeverExpires: "<Boolean>"
PasswordNotRequired: "<Boolean>"
POBox: "<String>"
PostalCode: "<String>"
# PrincipalsAllowedToDelegateToAccount: "<ADPrincipal[]>" # Ne sera pas défini
ProfilePath: "<String>"
SamAccountName: "<String>"
ScriptPath: "<String>"
# Server: "<String>" # Inutile : spécifie l'instance de service de domaine Active Directory à laquelle se connecter
SmartcardLogonRequired: "<Boolean>"
State: "<String>"
StreetAddress: "<String>"
Surname: "<String>"
Title: "<String>"
# TrustedForDelegation: "<Boolean>" # Ne sera pas défini
UserPrincipalName: "<String>"
Groups:
AuthType: "<ADAuthType>"
# Credential: "<PSCredential>" # Inutile : spécifie les identifiants de connexion du compte utilisateur à utiliser pour effectuer cette tâche
Description: "<String>"
DisplayName: "<String>"
GroupCategory: "<ADGroupCategory>"
GroupScope: "<ADGroupScope>"
HomePage: "<String>"
ManagedBy: "<ADPrincipal>"
SamAccountName: "<String>"
# Server: "<String>" # Inutile : spécifie l'instance de service de domaine Active Directory à laquelle se connecter
# Définit les attributs LDAP (ldapDisplayName) à gérer, qui ne sont pas gérés par les attributs standard.
# Vous pouvez définir les valeurs souhaitées. Les valeurs ci-dessous sont données à titre d'exemple.
otherAttributes:
Users:
otherMobile: "<String[]>"
otherTelephone: "<String[]>"
url: "<String[]>"
# Paramètres de génération de mot de passe aléatoire facultatifs. Par défaut : les valeurs spécifiées ci-dessous
# Un mot de passe aléatoire est généré pour initialiser un utilisateur dont le mot de passe n'est pas encore disponible
random_passwords:
# Longueur du mot de passe
length: 32
# Si true, le mot de passe généré peut contenir des lettres majuscules
with_upper_letters: true
# Le mot de passe généré contiendra au moins ce nombre de lettres majuscules
minimum_number_of_upper_letters: 1
# Si true, le mot de passe généré peut contenir des lettres minuscules
with_lower_letters: true
# Le mot de passe généré contiendra au moins ce nombre de lettres minuscules
minimum_number_of_lower_letters: 1
# Si true, le mot de passe généré peut contenir des chiffres
with_numbers: true
# Le mot de passe généré contiendra au moins ce nombre de chiffres
minimum_number_of_numbers: 1
# Si true, le mot de passe généré peut contenir des caractères spéciaux
with_special_chars: true
# Le mot de passe généré contiendra au moins ce nombre de caractères spéciaux
minimum_number_of_special_chars: 1
# Si true, le mot de passe généré ne contiendra pas les caractères spécifiés dans 'ambigous_chars_dictionary'
avoid_ambigous_chars: false
# Le dictionnaire des caractères ambigus (sensibles à la casse) qui peuvent être interdits dans le mot de passe, même si certains sont présents dans d'autres dictionnaires
ambigous_chars_dictionary: "lIO01"
# Le dictionnaire des lettres (insensibles à la casse) autorisées dans le mot de passe
letters_dictionary: "abcdefghijklmnopqrstuvwxyz"
# Le dictionnaire des caractères spéciaux autorisés dans le mot de passe
special_chars_dictionary: "!@#$%^&*"Datamodel
Les types de données suivants peuvent être configurés :
Users: nécessite que l’attributSamAccountNamesoit définiUserPasswords: nécessite évidemmentUsers, et que l’attributuser_pkeycorresponde aux clés primaires deUsers, et nécessite l’attributpassword. Tous les autres attributs seront ignorésGroups: nécessite que l’attributSamAccountNamesoit définiGroupsMembers: nécessite évidemmentUsersetGroups, et nécessite que les attributsuser_pkeyetgroup_pkeycorrespondent aux clés primaires deUsersetGroups. Tous les autres attributs seront ignorésSubGroupsMembers: nécessite évidemmentGroups, et nécessite que les attributssubgroup_pkeyetgroup_pkeycorrespondent respectivement à la clé primaire du sous-groupe à affecter, et à celle du groupe d’affectation. Tous les autres attributs seront ignorés
datamodel:
Users:
hermesType: your_server_Users_type_name
attrsmapping:
user_pkey: user_primary_key_on_server
SamAccountName: login_on_server
UserPrincipalName: "{{ login_on_server ~ '@YOU.AD.DOMAIN.TLD' }}"
# Pas obligatoire, juste présent pour exemple :
MobilePhone: "{{ (mobile | default([None]))[0] }}" # <String>
otherMobile: "{{ (mobile | default([]))[1:] }}" # <String[]>
# ...
UserPasswords:
hermesType: your_server_UserPasswords_type_name
attrsmapping:
user_pkey: user_primary_key_on_server
password: cleartext_password_on_server
# ...
Groups:
hermesType: your_server_Groups_type_name
attrsmapping:
group_pkey: group_primary_key_on_server
SamAccountName: group_name_on_server
# ...
GroupsMembers:
hermesType: your_server_GroupsMembers_type_name
attrsmapping:
user_pkey: user_primary_key_on_server
group_pkey: group_primary_key_on_server
# ...
SubGroupsMembers:
hermesType: your_server_SubGroupsMembers_type_name
attrsmapping:
subgroup_pkey: subgroup_primary_key_on_server
group_pkey: group_primary_key_on_server
# ...bsspartage
Description
Ce client traite les évènements de type Users, UserPasswords, Groups, GroupsMembers, GroupsSenders et Ressources, et stocke les données dans le tableau de bord de PARTAGE via son API, gérée par libPythonBssApi.
Pour éviter les problèmes de sécurité, si aucun hash n’est disponible à la création de l’utilisateur, un mot de passe aléatoire complexe sera défini. Ce mot de passe inconnu sera modifié lorsqu’un attribut userPassword sera défini sur User ou sur UserPassword. Cela évite d’avoir un compte activé sans mot de passe.
La corbeille ne fait que désactiver le compte.
Configuration
Vous devez configurer un mapping d’authentification authentication contenant tous les domaines gérés par ce client en tant que clés et leur clé API en tant que valeurs.
hermes-client-usersgroups_bsspartage:
authentication:
example.com: "Secret_API_key_of_example.com"
subdomain.example.com: "Secret_API_key_of_subdomain.example.com"
# Lorsqu'un attribut n'a plus de valeur, le comportement par défaut est de conserver sa dernière valeur en place.
# Ce paramètre permet de remplacer ce comportement pour les attributs spécifiés, avec des valeurs de remplacement.
# Veuillez noter qu'il est interdit de définir Users.userPassword, car le comportement par défaut est de générer un nouveau mot de passe aléatoire.
# Il est également interdit de définir des valeurs nulles, car cela revient au comportement par défaut. Dans ce cas, supprimez simplement l'attribut concerné de cette liste.
#
# Les valeurs définies ci-dessous sont les valeurs par défaut utilisées si default_removed_values n'est pas défini
default_removed_values:
Users:
co: ""
company: ""
description: ""
displayName: ""
facsimileTelephoneNumber: ""
givenName: ""
homePhone: ""
initials: ""
l: ""
mobile: ""
name: ""
pager: ""
postalCode: ""
st: ""
street: ""
telephoneNumber: ""
title: ""
zimbraNotes: ""
zimbraPrefMailForwardingAddress: ""
zimbraMailCanonicalAddress: ""
zimbraPrefFromDisplay: ""
zimbraMailQuota: 0
Groups:
# Ces valeurs devraient être définies comme des chaînes vides, mais un bug dans l'API les ignore.
# Ce bug a été signalé à l'équipe de PARTAGE.
description: "-"
displayName: "-"
zimbraNotes: "-"
Resources:
co: ""
description: ""
l: ""
postalCode: ""
st: ""
street: ""
zimbraCalResBuilding: ""
zimbraCalResContactEmail: ""
zimbraCalResContactName: ""
zimbraCalResContactPhone: ""
zimbraCalResFloor: ""
zimbraCalResLocationDisplayName: ""
zimbraCalResRoom: ""
zimbraCalResSite: ""
zimbraNotes: ""
zimbraCalResCapacity: "-1"
# Paramètres de génération de mot de passe aléatoire facultatifs. Par défaut : les valeurs spécifiées ci-dessous
# Un mot de passe aléatoire est généré pour initialiser un utilisateur dont le mot de passe n'est pas encore disponible
random_passwords:
# Longueur du mot de passe
length: 32
# Si true, le mot de passe généré peut contenir des lettres majuscules
with_upper_letters: true
# Le mot de passe généré contiendra au moins ce nombre de lettres majuscules
minimum_number_of_upper_letters: 1
# Si true, le mot de passe généré peut contenir des lettres minuscules
with_lower_letters: true
# Le mot de passe généré contiendra au moins ce nombre de lettres minuscules
minimum_number_of_lower_letters: 1
# Si true, le mot de passe généré peut contenir des chiffres
with_numbers: true
# Le mot de passe généré contiendra au moins ce nombre de chiffres
minimum_number_of_numbers: 1
# Si true, le mot de passe généré peut contenir des caractères spéciaux
with_special_chars: true
# Le mot de passe généré contiendra au moins ce nombre de caractères spéciaux
minimum_number_of_special_chars: 1
# Si true, le mot de passe généré ne contiendra pas les caractères spécifiés dans 'ambigous_chars_dictionary'
avoid_ambigous_chars: false
# Le dictionnaire des caractères ambigus (sensibles à la casse) qui peuvent être interdits dans le mot de passe, même si certains sont présents dans d'autres dictionnaires
ambigous_chars_dictionary: "lIO01"
# Le dictionnaire des lettres (insensibles à la casse) autorisées dans le mot de passe
letters_dictionary: "abcdefghijklmnopqrstuvwxyz"
# Le dictionnaire des caractères spéciaux autorisés dans le mot de passe
special_chars_dictionary: "!@#$%^&*"Datamodel
Les types de données suivants peuvent être configurés :
Users: pour les comptes utilisateurs. Nécessite la définition des attributsnameetsn, un attribut facultatifaliasespeut être défini, et les autres sont des attributs tels que définis et utilisés par libPythonBssApi et sont facultatifs. Notez que les attributszimbraAllowFromAddress,zimbraFeatureContactsEnabledetzimbraMailForwardingAddressne sont pas pris en charge par libPythonBssApi.UserPasswords: nécessite évidemmentUserset que ses clés primaires correspondent aux clés primaires deUsers, et nécessite l’attributuserPasswordqui doit contenir un hash LDAP valide. Tous les autres attributs seront ignorés. Comme l’attributuserPasswordpeut également être géré parUsers, vous devez choisir : soit vous le gérez parUsers, soit parUserPasswords, mais pour des raisons évidentes vous ne devez en aucun cas utiliser les deux en même temps.Groups: pour les groupes et les listes de distribution. Nécessite que les attributsnameetzimbraMailStatussoient définis, un attribut facultatifaliasespeut être défini, et les autres sont des attributs tels que définis et utilisés par libPythonBssApi et sont facultatifs.GroupsMembers: pour ajouter des utilisateurs en tant que membres du groupe. Nécessite évidemmentUsersetGroups, et nécessite les attributsuser_pkeyetgroup_pkeycorrespondant aux clés primaires deUsersetGroups. Tous les autres attributs seront ignorés.GroupsSenders: pour ajouter des utilisateurs en tant qu’expéditeurs du groupe. Nécessite évidemmentUsersetGroups, et nécessite les attributsuser_pkeyetgroup_pkeycorrespondant aux clés primaires deUsersetGroups. Tous les autres attributs seront ignorés.Resources: pour les ressources. Nécessite que les attributsname,zimbraCalResTypeetdisplayNamesoient définis, et les autres sont des attributs tels que définis et utilisés par libPythonBssApi et sont facultatifs.
Si vous définissez Users.zimbraCOSId, vous devriez éviter de définir des attributs gérés par la COS dans votre modèle de données, car le remplacement de la valeur par défaut de la COS peut entraîner des comportements inattendus.
Étant donné que l’API ne permet pas de renommer des Groups et Resources, cette opération est effectuée en supprimant l’ancienne instance et en recréant la nouvelle dans la foulée. Cependant, cela peut entraîner des pertes de liens et d’informations (par exemple, des calendriers de ressources), et il est probablement préférable d’éviter ces renommages.
Pour gérer Users.zimbraCOSId, il est probable que votre source de données fournisse un nom plutôt que le COSId. Il est possible de déclarer une table de correspondance en Jinja directement dans votre configuration :
datamodel:
Users:
hermesType: your_server_Users_type_name
attrsmapping:
# ...
zimbraCOSId: >-
{{
{
'name_of_cos1': '11111111-1111-1111-1111-111111111111',
'name_of_cos2': '22222222-2222-2222-2222-222222222222',
'name_of_cos3': '33333333-3333-3333-3333-333333333333',
}[zimbraCOSName_value_from_server | default('name_of_cos1') | lower]
| default('11111111-1111-1111-1111-111111111111')
}}
# ... datamodel:
Users:
hermesType: your_server_Users_type_name
attrsmapping:
# Adresse mail principale de l’utilisateur <Adresse mail valide>
name: name_value_from_server
# Nom de famille de l'utilisateur <String>
sn: sn_value_from_server
# Liste des alias de cet utilisateur <String[]>
aliases: aliases_value_from_server
# Numéro EPPN de l’utilisateur <String>
carLicense: carLicense_value_from_server
# Nom du pays <String>
co: co_value_from_server
# Nom de la société ou de l'établissement <String>
company: company_value_from_server
# Description du compte <String>
description: description_value_from_server
# Nom affiché dans les emails <String>
displayName: displayName_value_from_server
# Fax de l'utilisateur <String>
facsimileTelephoneNumber: facsimileTelephoneNumber_value_from_server
# Prénom de l'utilisateur <String>
givenName: givenName_value_from_server
# Téléphone domicile de l'utilisateur <String>
homePhone: homePhone_value_from_server
# Initiales (M. ou Mme) <String>
initials: initials_value_from_server
# Ville de l'utilisateur <String>
l: l_value_from_server
# Numéro de mobile de l'utilisateur <String>
mobile: mobile_value_from_server
# Numéro raccourci de l'utilisateur <String>
pager: pager_value_from_server
# Code postal <String>
postalCode: postalCode_value_from_server
# État de l'utilisateur <String>
st: st_value_from_server
# Rue de l'utilisateur <String>
street: street_value_from_server
# Téléphone de l'utilisateur <String>
telephoneNumber: telephoneNumber_value_from_server
# Fonction de l'utilisateur <String>
title: title_value_from_server
# Empreinte du mot de passe <String>
userPassword: userPassword_value_from_server
# État du compte (défaut active) <String(active, closed, locked)>
zimbraAccountStatus: zimbraAccountStatus_value_from_server
# Id de la classe de service <String>
zimbraCOSId: zimbraCOSId_value_from_server
# Onglet porte document <String (TRUE, FALSE)>
zimbraFeatureBriefcasesEnabled: zimbraFeatureBriefcasesEnabled_value_from_server
# Onglet calendrier <String (TRUE, FALSE)>
zimbraFeatureCalendarEnabled: zimbraFeatureCalendarEnabled_value_from_server
# Onglet mail <String (TRUE, FALSE)>
zimbraFeatureMailEnabled: zimbraFeatureMailEnabled_value_from_server
# Permettre à l’utilisateur d’indiquer une adresse de redirection <String (TRUE, FALSE)>
zimbraFeatureMailForwardingEnabled: zimbraFeatureMailForwardingEnabled_value_from_server
# Onglet préférences <String (TRUE, FALSE)>
zimbraFeatureOptionsEnabled: zimbraFeatureOptionsEnabled_value_from_server
# Onglet tâche <String (TRUE, FALSE)>
zimbraFeatureTasksEnabled: zimbraFeatureTasksEnabled_value_from_server
# Masquer dans la GAL <String (TRUE, FALSE)>
zimbraHideInGal: zimbraHideInGal_value_from_server
# 0=illimité <Integer (octet)>
zimbraMailQuota: zimbraMailQuota_value_from_server
# Notes libres <String>
zimbraNotes: zimbraNotes_value_from_server
# Doit changer son mot de passe à la prochaine connexion <String (TRUE, FALSE)>
zimbraPasswordMustChange: zimbraPasswordMustChange_value_from_server
# Adresse de redirection saisie par l’utilisateur <Adresse mail valide>
zimbraPrefMailForwardingAddress: zimbraPrefMailForwardingAddress_value_from_server
# Ne pas conserver de copie des mails sur le client local <String (TRUE, FALSE)>
zimbraPrefMailLocalDeliveryDisabled: zimbraPrefMailLocalDeliveryDisabled_value_from_server
# Adresse email visible pour les messages sortants <String>
zimbraMailCanonicalAddress: zimbraMailCanonicalAddress_value_from_server
# Nom affiché visible pour les messages sortants <String>
zimbraPrefFromDisplay: zimbraPrefFromDisplay_value_from_server
UserPasswords:
hermesType: your_server_UserPasswords_type_name
attrsmapping:
# Empreinte du mot de passe <String>
userPassword: userPassword_value_from_server
Groups:
hermesType: your_server_Groups_type_name
attrsmapping:
# Adresse mail principale du groupe <Adresse mail valide>
name: name_value_from_server
# Discriminant liste de distribution / groupe <String (enabled, disabled)>
zimbraMailStatus: zimbraMailStatus_value_from_server
# Liste des alias de ce groupe <String[]>
aliases: aliases_value_from_server
# Description du groupe <String>
description: description_value_from_server
# Nom affiché <String>
displayName: displayName_value_from_server
# Signaler les partages disponibles aux nouveaux membres <String (TRUE, FALSE)>
zimbraDistributionListSendShareMessageToNewMembers: zimbraDistributionListSendShareMessageToNewMembers_value_from_server
# Masquer le groupe dans la liste GAL <String (TRUE, FALSE)>
zimbraHideInGal: zimbraHideInGal_value_from_server
# Notes libres <String>
zimbraNotes: zimbraNotes_value_from_server
GroupsMembers:
hermesType: your_server_GroupsMembers_type_name
attrsmapping:
user_pkey: user_pkey_value_from_server
group_pkey: group_pkey_value_from_server
GroupsSenders:
hermesType: your_server_GroupsSenders_type_name
attrsmapping:
user_pkey: user_pkey_value_from_server
group_pkey: group_pkey_value_from_server
Resources:
hermesType: your_server_Resources_type_name
attrsmapping:
# Adresse mail principale de la ressource <Adresse mail valide>
name: name_value_from_server
# Nom affiché <String>
displayName: displayName_value_from_server
# Type de la ressource <String (Location, Equipment)>
zimbraCalResType: zimbraCalResType_value_from_server
# Nom du pays <String>
co: co_value_from_server
# Description <String>
description: description_value_from_server
# Ville de la ressource <String>
l: l_value_from_server
# Code postal <String>
postalCode: postalCode_value_from_server
# État de la ressource <String>
st: st_value_from_server
# Rue de la ressource <String>
street: street_value_from_server
# empreinte du mot de passe <String>
userPassword: userPassword_value_from_server
# État de la ressource (défaut active) <String (active, closed)>
zimbraAccountStatus: zimbraAccountStatus_value_from_server
# Accepte ou décline automatiquement les invitations <String (TRUE, FALSE)>
zimbraCalResAutoAcceptDecline: zimbraCalResAutoAcceptDecline_value_from_server
# Décline automatiquement les invitations si risque de conflit <String (TRUE, FALSE)>
zimbraCalResAutoDeclineIfBusy: zimbraCalResAutoDeclineIfBusy_value_from_server
# Décline automatiquement les invitations récurrente <String (TRUE, FALSE)>
zimbraCalResAutoDeclineRecurring: zimbraCalResAutoDeclineRecurring_value_from_server
# Bâtiment <String>
zimbraCalResBuilding: zimbraCalResBuilding_value_from_server
# Capacité <Integer>
zimbraCalResCapacity: zimbraCalResCapacity_value_from_server
# Adresse mail du contact <String>
zimbraCalResContactEmail: zimbraCalResContactEmail_value_from_server
# Nom du contact <String>
zimbraCalResContactName: zimbraCalResContactName_value_from_server
# Téléphone du contact <String>
zimbraCalResContactPhone: zimbraCalResContactPhone_value_from_server
# Etage <String>
zimbraCalResFloor: zimbraCalResFloor_value_from_server
# Nom du lieu affiché <String>
zimbraCalResLocationDisplayName: zimbraCalResLocationDisplayName_value_from_server
# Salle <String>
zimbraCalResRoom: zimbraCalResRoom_value_from_server
# Lieu <String>
zimbraCalResSite: zimbraCalResSite_value_from_server
# Notes libres <String>
zimbraNotes: zimbraNotes_value_from_server
# Faire suivre les invitations de calendrier à cette adresse <Array>
zimbraPrefCalendarForwardInvitesTo: zimbraPrefCalendarForwardInvitesTo_value_from_serverflatfiles_emails_of_groups
Description
Ce client génére un fichier txt plat par Groups, contenant les adresses e-mail de ses membres (une par ligne).
Configuration
hermes-client-usersgroups_flatfiles_emails_of_groups:
# OBLIGATOIRE
destDir: "/path/where/files/are/stored"
# Facultatif : si défini, générera un fichier uniquement pour les noms de groupe spécifiés dans cette liste
onlyTheseGroups:
- group1
- group2Datamodel
Les types de données suivants doivent être configurés :
Users, nécessite les noms d’attribut suivants :user_pkey: la clé primaire de l’utilisateurmail: l’adresse e-mail de l’utilisateur
Groups, nécessite les noms d’attribut suivants :group_pkey: la clé primaire du groupename: le nom du groupe, qui sera comparé à ceux deonlyTheseGroups, et utilisé pour nommer le fichier de destination “groupName.txt”
GroupsMembers, nécessite les noms d’attribut suivants :user_pkey: la clé primaire de l’utilisateurgroup_pkey: la clé primaire du groupe
datamodel:
Users:
hermesType: your_server_Users_type_name
attrsmapping:
user_pkey: user_pkey_on_server
mail: mail_on_server
Groups:
hermesType: your_server_Groups_type_name
attrsmapping:
group_pkey: group_pkey_on_server
name: group_name_on_server
GroupsMembers:
hermesType: your_server_GroupsMembers_type_name
attrsmapping:
user_pkey: user_pkey_on_server
group_pkey: group_pkey_on_serverkadmin_heimdal
Description
Ce client traite les événements de type Users and UserPassword et stocke les données sur un serveur Kerberos Heimdal.
Configuration
hermes-client-usersgroups_kadmin_heimdal:
# OBLIGATOIRE : Principal disposant des droits requis pour gérer les utilisateurs et les mots de passe dans kadmin
kadmin_login: root/admin
# OBLIGATOIRE : Mot de passe du principal ci-dessus
kadmin_password: "s3cReT_p4s5w0rD"
# OBLIGATOIRE : nom du domaine Kerberos
kadmin_realm: KERBEROS_REALM
# Nom du principal de service pour lequel obtenir un ticket. Par défaut : kadmin/admin
kinit_spn: kadmin/admin
# Commande kinit à utiliser. Par défaut : kinit.heimdal
kinit_cmd: kinit.heimdal
# Commande kadmin à utiliser. Par défaut : kadmin.heimdal
kadmin_cmd: kadmin.heimdal
# Commande kdestroy à utiliser. Par défaut : kdestroy.heimdal
kdestroy_cmd: kdestroy.heimdal
# Paramètre kadmin supplémentaires à utiliser lors de l'ajout d'un utilisateur. Doit être une liste de chaînes. Valeur par défaut :
# - "--max-ticket-life=1 day"
# - "--max-renewable-life=1 week"
# - "--attributes="
# - "--expiration-time=never"
# - "--policy=default"
# - "--pw-expiration-time=never"
kadmin_user_add_additional_options:
- "--max-ticket-life=1 day"
- "--max-renewable-life=1 week"
- "--attributes="
- "--expiration-time=never"
- "--policy=default"
- "--pw-expiration-time=never"
# Définir à true pour démarrer avec une base de données Kerberos déjà remplie. Valeur par défaut : false
dont_fail_on_existing_user: false
# Paramètres de génération de mot de passe aléatoire facultatifs. Par défaut : les valeurs spécifiées ci-dessous
# Un mot de passe aléatoire est généré pour initialiser un utilisateur dont le mot de passe n'est pas encore disponible
random_passwords:
# Longueur du mot de passe
length: 32
# Si true, le mot de passe généré peut contenir des lettres majuscules
with_upper_letters: true
# Le mot de passe généré contiendra au moins ce nombre de lettres majuscules
minimum_number_of_upper_letters: 1
# Si true, le mot de passe généré peut contenir des lettres minuscules
with_lower_letters: true
# Le mot de passe généré contiendra au moins ce nombre de lettres minuscules
minimum_number_of_lower_letters: 1
# Si true, le mot de passe généré peut contenir des chiffres
with_numbers: true
# Le mot de passe généré contiendra au moins ce nombre de chiffres
minimum_number_of_numbers: 1
# Si true, le mot de passe généré peut contenir des caractères spéciaux
with_special_chars: true
# Le mot de passe généré contiendra au moins ce nombre de caractères spéciaux
minimum_number_of_special_chars: 1
# Si true, le mot de passe généré ne contiendra pas les caractères spécifiés dans 'ambigous_chars_dictionary'
avoid_ambigous_chars: false
# Le dictionnaire des caractères ambigus (sensibles à la casse) qui peuvent être interdits dans le mot de passe, même si certains sont présents dans d'autres dictionnaires
ambigous_chars_dictionary: "lIO01"
# Le dictionnaire des lettres (insensibles à la casse) autorisées dans le mot de passe
letters_dictionary: "abcdefghijklmnopqrstuvwxyz"
# Le dictionnaire des caractères spéciaux autorisés dans le mot de passe
special_chars_dictionary: "!@#$%^&*"Datamodel
Les types de données suivants doivent être configurés :
Users, nécessite les noms d’attribut suivants :login: le login de l’utilisateur qui sera utilisé comme principal
UserPasswords, nécessite les noms d’attribut suivants :password: le mot de passe de l’utilisateur
Évidemment, les clés primaires de Users et UserPasswords doivent correspondre pour pouvoir lier le login au mot de passe.
datamodel:
Users:
hermesType: your_server_Users_type_name
attrsmapping:
login: login_on_server
UserPasswords:
hermesType: your_server_UserPasswords_type_name
attrsmapping:
password: password_on_serverldap
Description
Ce client traite les événements de type Users, Groups et UserPasswords, et stocke les données dans un annuaire LDAP.
Les clés du modèle de données local seront utilisées comme noms d’attributs LDAP, sans aucune contrainte, et il est possible de spécifier avec le paramètre attributesToIgnore certaines clés du modèle de données à ignorer (généralement les clés primaires) qui ne seront pas stockées dans l’annuaire LDAP.
GroupMembers stockera uniquement les données (généralement l’attribut LDAP member) dans les entrées LDAP des groupes puisqu’il est possible d’utiliser des overlays LDAP (dynlist ou le désormais obsolète memberOf) pour générer dynamiquement les données correspondantes dans les entrées utilisateur. Vous devriez envisager de lire la documentation du paramètre propagateUserDNChangeOnGroupMember.
Si vous devez générer des hachages de mots de passe LDAP, vous devriez regarder le plugin d’attribut ldapPasswordHash.
Configuration
hermes-client-usersgroups_ldap:
# OBLIGATOIRE : URI du serveur LDAP
uri: ldaps://ldap.example.com:636
# OBLIGATOIRE : identifiants de connexion au serveur LDAP
binddn: cn=account,dc=example,dc=com
bindpassword: s3cReT_p4s5w0rD
# OBLIGATOIRE : DN de base LDAP
basedn: dc=example,dc=com
users_ou: ou=users,dc=example,dc=com
groups_ou: ou=groups,dc=example,dc=com
ssl: # Facultatif
# Chemin vers le fichier PEM avec les certificats CA
cafile: /path/to/INTERNAL-CA-chain.crt # Facultatif
# Chemin vers le fichier de certificat au format PEM pour l'authentification du certificat client, nécessite de définir keyfile
certfile: /path/to/client.crt # Facultatif
# Chemin vers le fichier de clé privée du certificat au format PEM pour l'authentification du certificat client, nécessite de définir certfile
keyfile: /path/to/client.pem # Facultatif
# OBLIGATOIRE : nom de l'attribut DN pour les utilisateurs, les mots de passe utilisateur et les groupes
# Vous devez définir des valeurs pour les trois, même si vous n'utilisez pas certains d'entre eux
dnAttributes:
Users: uid
UserPasswords: uid
Groups: cn
# En fonction des paramètres de groupe et d'appartenance au groupe du serveur LDAP,
# vous pourriez utiliser un autre attribut que l'attribut par défaut 'member' pour
# stocker le DN du membre du groupe
# Facultatif. Valeur par défaut : "member"
groupMemberAttribute: member
# En fonction des paramètres de groupe et d'appartenance au groupe du serveur LDAP,
# vous pourriez vouloir propager un changement de DN d'utilisateur vers les
# attributs d'appartenance au groupe. Mais dans certains cas, c'est géré par un
# overlay, par exemple avec l'overlay memberOf et son paramètre
# memberof-refint/olcMemberOfRefint à TRUE
# Si 'propagateUserDNChangeOnGroupMember' est définie à true, il faudra également
# définir 'groupsObjectclass'
# Facultatif. Valeur par défaut : true
propagateUserDNChangeOnGroupMember: true
# Si vous avez défini 'propagateUserDNChangeOnGroupMember' à true,
# vous DEVEZ indiquer l'objectClass sera utilisé pour rechercher
# vos entrées de groupes
# Obligatoire uniquement si 'propagateUserDNChangeOnGroupMember' est vrai
groupsObjectclass: groupOfNames
# Il est possible de définir une valeur par défaut pour certains attributs pour les Users, UserPasswords et Groups
# La valeur par défaut sera appliquée lors du traitements des événements added et modified, si l'attribut local n'a pas de valeur
defaultValues:
Groups:
member: "" # Hack pour permettre la création d'un groupe vide, néessaire à cause du "MUST member" dans le schéma
# Les attributs locaux répertoriés ici ne seront pas stockés dans LDAP pour les types Users, UserPasswords and Groups
attributesToIgnore:
Users:
- user_pkey
UserPasswords:
- user_pkey
Groups:
- group_pkeyDatamodel
Les types de données suivants peuvent être configurés :
UsersUserPasswords: nécessite évidemmentUserset nécessite l’attributuser_pkeycorrespondant aux clés primaires deUsersGroupsGroupsMembers: nécessite évidemmentUsersetGroupset nécessite les attributsuser_pkeyetgroup_pkeycorrespondant aux clés primaires deUsersetGroups
datamodel:
Users:
hermesType: your_server_Users_type_name
attrsmapping:
user_pkey: user_primary_key_on_server
uid: login_on_server
# ...
UserPasswords:
hermesType: your_server_UserPasswords_type_name
attrsmapping:
user_pkey: user_primary_key_on_server
userPassword: ldap_pwd_hash_list_on_server
# ...
Groups:
hermesType: your_server_Groups_type_name
attrsmapping:
group_pkey: group_primary_key_on_server
cn: group_name_on_server
# ...
GroupsMembers:
hermesType: your_server_GroupsMembers_type_name
attrsmapping:
user_pkey: user_primary_key_on_server
group_pkey: group_primary_key_on_server
# ...null
Description
Ce client traite les événements de type Users, Groups and UserPasswords, mais ne fait rien d’autre que de générer des logs.
Configuration
Rien à configurer pour le plugin.
hermes-client-usersgroups_null:Datamodel
Les types de données suivants peuvent être configurés, sans contrainte particulière puisque rien ne sera traité.
- Users
- UserPasswords
- Groups
- GroupsMembers
datamodel:
Users:
hermesType: your_server_Users_type_name
attrsmapping:
attr1_client: attr1_server
# ...
UserPasswords:
hermesType: your_server_UserPasswords_type_name
attrsmapping:
attr1_client: attr1_server
# ...
Groups:
hermesType: your_server_Groups_type_name
attrsmapping:
attr1_client: attr1_server
# ...
GroupsMembers:
hermesType: your_server_GroupsMembers_type_name
attrsmapping:
attr1_client: attr1_server
# ...plugins consommateur de bus de messages
Sous-sections de plugins consommateur de bus de messages
kafka
Description
Ce plugin permet à hermes-client de recevoir des événements depuis un serveur Apache Kafka.
Configuration
Il est possible de se connecter au serveur Kafka sans authentification, ou avec une authentification SSL (TLS).
hermes:
plugins:
messagebus:
kafka:
settings:
# OBLIGATOIRE : la liste des serveurs Kafka pouvant être utilisés
servers:
- dummy.example.com:9093
# Facultatif : quelle version de l'API Kafka utiliser. Si elle n'est
# pas définie, la version de l'API sera détectée au démarrage et
# indiquée dans les fichiers log.
# Ne définissez pas cette directive à moins que vous ne rencontriez
# des erreurs "kafka.errors.NoBrokersAvailable : NoBrokersAvailable"
# générées par un appel "self.check_version()".
api_version: [2, 6, 0]
# Facultatif : active l'authentification SSL. Si active, les 3 options
# ci-dessous doivent être définies
ssl:
# OBLIGATOIRE : fichier de certificat hermes-server qui sera
# utilisé pour l'authentification
certfile: /path/to/.hermes/dummy.crt
# OBLIGATOIRE : Chemin vers le fichier de clé privée du certificat
# hermes-server
keyfile: /path/to/.hermes/dummy.pem
# OBLIGATOIRE : le certificat CA de la PKI
cafile: /path/to/.hermes/INTERNAL-CA-chain.crt
# OBLIGATOIRE : le sujet sur lequel retrouver les événements
topic: hermes
# OBLIGATOIRE : le group_id auquel rattacher le client. Définissez ce que vous voulez ici.
group_id: hermes-grpsqlite
Description
Ce plugin permet à hermes-client de recevoir des événements depuis une base de données SQLite.
Configuration
hermes:
plugins:
messagebus:
sqlite:
settings:
# OBLIGATOIRE :
uri: /path/to/.hermes/bus.sqliteExécution
Vous pouvez démarrer n’importe quelle application Hermes (server, server-cli, client, client-cli) directement avec le lanceur hermes.py, en spécifiant le nom de l’application comme premier argument, ou par un lien symbolique.
Dans les deux cas, la configuration sera recherchée dans le répertoire de travail courant.
Exécution depuis le lanceur
# Server
/path/to/hermes.py server
# Server CLI
/path/to/hermes.py server-cli
# Client usersgroups_null
/path/to/hermes.py client-usersgroups_null
# Client usersgroups_null CLI
/path/to/hermes.py client-usersgroups_null-cliExécution depuis un lien symbolique
Si vous souhaitez éviter de spécifier le nom de l’application hermes comme premier argument, il est possible de créer un lien symbolique de hermes.py vers hermes-appname, e.g.:
ln -s hermes.py hermes-server
ln -s hermes.py hermes-server-cli
ln -s hermes.py hermes-client-usersgroups_null
ln -s hermes.py hermes-client-usersgroups_null-cli
# ...et de l’exécuter ainsi :
# Server
/path/to/hermes-server
# Server CLI
/path/to/hermes-server-cli
# Client usersgroups_null
/path/to/hermes-client-usersgroups_null
# Client usersgroups_null CLI
/path/to/hermes-client-usersgroups_null-cliArguments des commandes
Le serveur et les clients n’acceptent aucun argument, car ils sont conçus pour être contrôlés via la CLI.
Une fois le serveur ou le client démarré, vous pouvez demander la liste des commandes CLI disponibles avec l’option -h ou --help.
Pour le serveur :
$ ./hermes.py server-cli -h
usage: hermes-server-cli [-h] {initsync,update,quit,pause,resume,status} ...
Hermes Server CLI
positional arguments:
{initsync,update,quit,pause,resume,status}
Sub-commands
initsync Send specific init message containing all data but passwords. Useful to fill new client
update Force update now, ignoring updateInterval
quit Stop server
pause Pause processing until 'resume' command is sent
resume Resume processing that has been paused with 'pause'
status Show server status
options:
-h, --help show this help message and exitPour un client :
$ ./hermes.py client-usersgroups_null-cli -h
usage: hermes-client-usersgroups_null-cli [-h] {quit,pause,resume,status} ...
Hermes client hermes-client-usersgroups_null CLI
positional arguments:
{quit,pause,resume,status}
Sub-commands
quit Stop hermes-client-usersgroups_null
pause Pause processing until 'resume' command is sent
resume Resume processing that has been paused with 'pause'
status Show hermes-client-usersgroups_null status
options:
-h, --help show this help message and exitMaintenance
Cette section détaille les procédures d’exploitation courantes.
Sous-sections de Maintenance
Mise à jour du modèle de données serveur
Un modèle de données n’est pas figé dans le temps, il peut évoluer et donc être mis à jour, que ce soit depuis le serveur ou sur un ou plusieurs clients.
A chaque modification du modèle de données sur le serveur, sa nouvelle version est propagée aux clients avec ses données “publiques” : chaque type de données est inclus, avec sa clé primaire, la liste de ses attributs et la liste de ses attributs secrets. Des événements consécutifs sont ensuite émis.
Ajouter un attribut à un type de données existant
- 👱 Ajouter l’attribut au modèle de données serveur, redémarrer le serveur
- 💻 Émission d’un événement dataschema par le serveur
- 💻 Emission d’événements “modified” pour les entrées concernées, avec l’attribut ajouté et sa valeur
- 💻 Traitement de l’événement dataschema par les clients : mise à jour de leur schéma. Traitement des événements “modified” entrants : comme l’attribut n’est pas encore déclaré dans leurs modèles de données, sa valeur est ignorée mais stockée dans le cache complet
- 👱 Ajouter l’attribut aux modèles de données clients, redémarrer les clients
- 💻 Traitement des mises à jour du modèle de données local par les clients : génération et traitement des événements locaux “modified” depuis le cache complet
ou
- 👱 Ajouter l’attribut aux modèles de données clients afin qu’ils puissent le traiter lorsqu’il sera ajouté au modèle de données serveur, redémarrer les clients : ⚠️ avertissement du modèle de données “remote attributes don’t exist in current Dataschema”
- 👱 Ajouter l’attribut au modèle de données serveur, redémarrer le serveur
- 💻 Émission d’un événement dataschema par le serveur
- 💻 Émission d’événements “modified” pour les entrées concernées, avec l’attribut ajouté et sa valeur
- 💻 Traitement de l’événement dataschema par les clients : mise à jour de leur schéma. ✅ Plus d’avertissement de modèle de données. Traitement des événements “modified” entrants
Supprimer un attribut d’un type de données
- 👱 Supprimer l’attribut des modèles de données clients, redémarrer les clients
- 💻 Traitement des mises à jour du modèle de données local par les clients : génération et traitement des événements locaux “modified” consécutifs
- 👱 Supprimer l’attribut du modèle de données serveur, redémarrer le serveur
- 💻 Émission d’un événement dataschema par le serveur
- 💻 Émission d’événements “modified” pour les entrées concernées, avec l’attribut supprimé. Ils seront ignorés par les clients
- 💻 Traitement de l’événement dataschema par les clients : mise à jour de leur schéma
ou
- 👱 Supprimer l’attribut du modèle de données serveur, redémarrer le serveur
- 💻 Émission d’un événement dataschema par le serveur
- 💻 Émission d’événements “modified” pour les entrées concernées, avec l’attribut supprimé
- 💻 Traitement de l’événement dataschema par les clients : mise à jour de leur schéma. ⚠️ avertissement du modèle de données “remote attributes don’t exist in current Dataschema”. Traitement des événements “modified” entrants
- 👱 Supprimer l’attribut des modèles de données clients, redémarrer les clients: ✅ Plus d’avertissement de modèle de données
Modifier la valeur d’un attribut (en changeant son filtre Jinja, ou son attribut distant de la source de données)
- 👱 Modifier l’attribut dans le modèle de données serveur, redémarrer le serveur
- 💻 Émission d’événements “modified” pour les entrées concernées, avec les nouvelles valeurs de l’attribut modifié
- 💻 Traitement des événements “modified” entrants
Ajouter un attribut existant d’un type de données à secrets_attrs
- 👱 Modifier secrets_attrs dans le modèle de données serveur, redémarrer le serveur
- 💻 Purge de l’attribut dans le cache du serveur
- 💻 Émission d’un événement dataschema par le serveur
- 💻 Émission d’événements “modified” pour les entrées concernées, avec l’attribut ajouté et ses valeurs
- 💻 Traitement de l’événement dataschema par les clients : mise à jour de leur schéma, purge de l’attribut dans leur cache
- 💻 Traitement des événements “modified” entrants
Supprimer un attribut existant d’un type de données de secrets_attrs
- 👱 Modifier secrets_attrs in modèle de données serveur, redémarrer le serveur
- 💻 Émission d’un événement dataschema par le serveur
- 💻 Émission d’événements “modified” pour les entrées concernées, avec l’attribut ajouté et ses valeurs
- 💻 Traitement de l’événement dataschema par les clients : mise à jour de leur schéma
- 💻 Traitement des événements “modified” entrants
Ajouter un nouveau type de données
- 👱 Ajouter le type de données au modèle de données serveur, redémarrer le serveur
- 💻 Émission d’un événement dataschema par le serveur
- 💻 Émission d’événements “added” pour chaque entrée du type de données ajouté
- 💻 Traitement de l’événement dataschema par les clients : mise à jour de leur schéma. Traitement des événements “added” entrants : comme le type de données n’est pas encore déclaré dans leur modèle de données, ses entrées sont ignorées mais stockées dans le cache complet
- 👱 Ajouter le type de données aux modèles de données client, redémarrer les clients
- 💻 Traitement des mises à jour du modèle de données local par les clients : génération et traitement des événements locaux “added” depuis le cache complet
ou
- 👱 Ajouter le type de données aux modèles de données client afin qu’ils puissent le traiter lorsqu’il sera ajouté au modèle de données serveur, redémarrer les clients : ⚠️ avertissement du modèle de données “remote types don’t exist in current Dataschema”
- 👱 Ajouter le type de données au modèle de données serveur, redémarrer le serveur
- 💻 Émission d’un événement dataschema par le serveur
- 💻 Émission d’événements “added” pour chaque entrée du type de données ajouté
- 💻 Traitement de l’événement dataschema par les clients : mise à jour de leur schéma. ✅ Plus d’avertissement de modèle de données. Traitement des événements “added” entrants
Supprimer un type de données existant
- 👱 Supprimer le type de données dans les modèles de données client, redémarrer les clients
- 💻 Traitement des mises à jour du modèle de données local par les clients : génération et traitement des événements locaux “removed” consécutifs
- 💻 Purge des fichiers de cache locaux du type de données supprimé
- 👱 Supprimer le type de données dans le modèle de données serveur, redémarrer le serveur
- 💻 Émission d’événements “removed” pour chaque entrée du type de données supprimé
- 💻 Purge des fichiers de cache du type de données supprimé
- 💻 Émission d’un événement dataschema par le serveur
- 💻 Traitement des événements “removed” entrants par les clients : tous sont ignorés
- 💻 Traitement de l’événement dataschema par les clients : mise à jour de leur schéma
- 💻 Purge des fichiers de cache distants du type de données supprimé
ou
- 👱 Supprimer le type de données dans le modèle de données serveur, redémarrer le serveur
- 💻 Émission d’événements “removed” pour chaque entrée du type de données supprimé
- 💻 Purge des fichiers de cache du type de données supprimé
- 💻 Émission d’un événement dataschema par le serveur
- 💻 Traitement des événements “removed” entrants par les clients
- 💻 Traitement de l’événement dataschema par les clients : mise à jour de leur schéma. ⚠️ avertissement du modèle de données “remote types don’t exist in current Dataschema”
- 💻 Purge des fichiers de cache distants du type de données supprimé
- 👱 Supprimer le type de données dans les modèles de données client, redémarrer les clients: ✅ Plus d’avertissement de modèle de données
- 💻 Purge des fichiers de cache locaux du type de données supprimé
Modifier l’attribut de clé primaire d’un type de données
Il s’agit de la mise à jour du modèle de données la plus risquée, car il peut y avoir des liens entre les types de données, utilisant des clés primaires comme clés étrangères. Cela signifie que vous devrez mettre à jour tous les types de données en une fois, sans rien manquer.
Vous devriez vraiment envisager d’effectuer cette mise à jour dans un environnement de test avant de la faire en production, car si quelque chose échoue, vos clients pourraient être définitivement compromis.
Prérequis
Les attributs à utiliser comme nouvelle clé primaire doivent déjà exister dans votre modèle de données serveur, et leurs valeurs doivent déjà avoir été propagées et exister dans le cache des clients.
La nouvelle clé primaire DOIT exister dans chaque entrée de son type de données avant la mise à jour du modèle de données. Si la corbeille est activée sur certains de vos clients, le nouvel attribut de clé primaire peut être absent des entrées qui s’y trouvent.
La manière la plus sûre de gérer cela est d’ajouter l’attribut à votre modèle de données serveur et de retarder le changement de clé primaire d’au moins un jour + autant de jours que la valeur trashbin_retention la plus élevée de tous vos clients.
Si vous ne gérez pas cela de cette manière, le client purgera toutes les entrées en corbeille qui ne contiennent pas la valeur du ou des nouveaux attributs de clé primaire, comme si le délai trashbin_retention était échu.
Mise à jour
- 👱 Mettre à jour tous les types de données dans le modèle de données serveur, redémarrer le serveur
- 💻 Mise à jour des clés primaires modifiées dans les fichiers cache du serveur
- 💻 Émission d’un événement dataschema par le serveur
- 💻 Traitement de l’événement dataschema par les clients : purge des entrées en corbeille auxquelles il manque la nouvelle clé primaire, mise à jour de leurs schémas, mise à jour des clés primaires modifiées dans les fichiers cache et la file d’erreurs
Mise à jour du modèle de données client
Un modèle de données n’est pas figé dans le temps, il peut évoluer et donc être mis à jour, que ce soit depuis le serveur ou sur un ou plusieurs clients.
Chaque fois que le modèle de données est modifié sur un client, le client génère des événements locaux pour propager les modifications de données consécutives sur les cibles. Le client peut notifier des avertissements concernant le modèle de données si certains types de données ou attributs distants sont définis dans son modèle de données mais n’existent pas dans le schéma de données actuel reçu d’hermes-server.
Ajouter un attribut à un type de données existant
- 👱 Ajouter l’attribut aux modèles de données clients, redémarrer les clients
- 💻 Traitement des mises à jour du modèle de données local par les clients : génération et traitement des événements locaux “modified” depuis le cache complet
Supprimer un attribut d’un type de données
- 👱 Supprimer l’attribut des modèles de données clients, redémarrer les clients
- 💻 Traitement des mises à jour du modèle de données local par les clients : génération et traitement des événements locaux “modified” consécutifs
Modifier la valeur d’un attribut (en changeant son filtre Jinja ou son attribut distant de la source de données)
- 👱 Modifier l’attribute dans les modèles de données client, redémarrer les clients
- 💻 Traitement des mises à jour du modèle de données local par les clients : génération et traitement des événements locaux “modified” consécutifs
Ajouter un nouveau type de données
Si son hermesType existe déjà dans le schéma de données
- 👱 Ajouter le type de données aux modèles de données client, redémarrer les clients
- 💻 Traitement des mises à jour du modèle de données local par les clients : génération et traitement des événements locaux “added” depuis le cache complet
Si son hermesType n’existe pas encore dans le schéma de données
- 👱 Ajouter le type de données aux modèles de données client afin qu’ils puissent le traiter lorsqu’il sera ajouté au modèle de données serveur, redémarrer les clients : ⚠️ avertissement du modèle de données “remote types don’t exist in current Dataschema”
- 👱 Ajouter le type de données aux modèles de données serveur, redémarrer le serveur
- 💻 Émission d’un événement dataschema par le serveur
- 💻 Émission d’événements “added” pour chaque entrée du type de données ajouté
- 💻 Traitement de l’événement dataschema par les clients : mise à jour de leur schéma. ✅ Plus d’avertissement de modèle de données. Traitement des événements “added” entrants
Supprimer un type de données existant
- 👱 Supprimer le type de données dans les modèles de données client, redémarrer les clients
- 💻 Traitement des mises à jour du modèle de données local par les clients : génération et traitement des événements locaux “removed” consécutifs
- 💻 Purge des fichiers de cache locaux du type de données supprimé
Exemples
Cette section contient quelques exemples de différents cas d’utilisation et leurs fichiers de configuration.
Sous-sections de Exemples
01. Source de données unique
Contexte
Dans cet exemple, nous disposons d’une unique source de données (une base de données Oracle) que nous utiliserons pour gérer le cas classique des utilisateurs, mots de passe, groupes et l’appartenance des utilisateurs aux groupes, dans le but de remplir un serveur LDAP.
Schéma Oracle
classDiagram
direction BT
ORA_USERPASSWORDS <-- ORA_USERS
ORA_GROUPSMEMBERS <-- ORA_USERS
ORA_GROUPSMEMBERS <-- ORA_GROUPS
class ORA_USERS{
USER_ID - NUMBER, NOT NULL
LOGIN - VARCHAR2
FIRSTNAME - VARCHAR2
LASTNAME - VARCHAR2
EMAIL - VARCHAR2
}
class ORA_USERPASSWORDS{
USER_ID - NUMBER, NOT NULL
PASSWORD_ENCRYPTED - RAW
LDAP_HASHES - VARCHAR2
}
class ORA_GROUPS{
GROUP_ID - NUMBER, NOT NULL
GROUP_NAME - VARCHAR2
GROUP_DESC - VARCHAR2
}
class ORA_GROUPSMEMBERS{
USER_ID - NUMBER, NOT NULL
GROUP_ID - NUMBER, NOT NULL
}
hermes-server-config
hermes:
cache:
dirpath: /path/to/.hermes/hermes-server/cache
enable_compression: true
backup_count: 1
cli_socket:
path: /path/to/.hermes/hermes-server.sock # Facultatif, requis pour utiliser la CLI
owner: user_login # Facultatif
group: group_name # Facultatif
# Facultatif, '0600' par défaut.
# La valeur DOIT être préfixée par un 0 pour indiquer qu'il s'agit d'un entier octal
mode: 0660
logs:
logfile: /path/to/.hermes/hermes-server/logs/hermes-server.log
backup_count: 31 # 1 mois
verbosity: info
mail:
server: dummy.example.com
from: Hermes Server <no-reply@example.com>
to:
- user@example.com
plugins:
# Plugins d'attribut (filtres jinja)
attributes:
ldapPasswordHash:
settings:
default_hash_types:
- SMD5
- SSHA
- SSHA256
- SSHA512
crypto_RSA_OAEP:
settings:
keys:
decrypt_from_datasource:
hash: SHA256
# ATTENTION - CETTE CLÉ EST FAIBLE ET PUBLIQUE, NE L'UTILISEZ JAMAIS
rsa_key: |-
-----BEGIN RSA PRIVATE KEY-----
MIGrAgEAAiEAstltWwDzmtSSHi7lfKqtUIO4dI8aX/EAopNdR/cWXH8CAwEAAQIh
AKfflFjGNOJQwvJX3Io+/juxO+HFd7SRC++zBD9paZqZAhEA5OtjZQUapRrV/aC5
NXFsswIRAMgBtgpz+t0FxyEXdzlcTwUCEHU6WZ8M2xU7xePpH49Ps2MCEQC+78s+
/WvfNtXcRI+gJfyVAhAjcIWzHC5q4wzgL7psbPGy
-----END RSA PRIVATE KEY-----
# SERVEUR UNIQUEMENT - Sources utilisées pour récupérer les données. Au moins une doit être définie
datasources:
datasource_of_example1: # Nom de la source. Utilisez ce que vous voulez. Sera utilisé dans le modèle de données
type: oracle # Type de source. Un plugin de source de données portant ce nom doit exister
settings: # Paramètres de la source actuelle
login: HERMES_DUMMY
password: "DuMmY_p4s5w0rD"
port: 1234
server: dummy.example.com
sid: DUMMY
messagebus:
kafka:
settings:
servers:
- dummy.example.com:9093
ssl:
certfile: /path/to/.hermes/dummy.crt
keyfile: /path/to/.hermes/dummy.pem
cafile: /path/to/.hermes/INTERNAL-CA-chain.crt
topic: hermes
hermes-server:
updateInterval: 60 # Intervalle entre deux actualisations des données, en secondes
# L'ordre de déclaration des types de données est important :
# - les événements d'ajout/modification seront traités dans l'ordre de déclaration
# - les événements de suppression seront traités dans l'ordre de déclaration inverse
datamodel:
SRVGroups: # Paramètres pour le type de données SRVGroups
primarykeyattr: srv_group_id # Nom de l'attribut du modèle de données utilisé comme clé primaire
# Facultatif : template Jinja permettant de définir la représentation d'une entrée dans les fichiers journaux
toString: "<SRVGroups[{{ srv_group_id }}, {{ srv_group_name | default('#UNDEF#') }}]>"
# Sources de données à utiliser pour récupérer les données.
# Dans la plupart des cas il n'y en a qu'une seule, mais plusieurs peuvent être utilisées
sources:
datasource_of_example1: # Le nom de la source défini dans hermes.plugins.datasources
# La requête permettant de récupérer les données.
# 'type' est obligatoire et indique au plugin quel type de requête exécuter
# Les valeurs 'type' possibles sont 'add', 'delete', 'fetch' et 'modify'
# 'query' contient la requête à exécuter
# 'vars' est un dictionnaire avec des variables à utiliser (et à nettoyer !) dans la requête
#
# Selon le plugin de source de données utilisé, 'query' et 'vars' peuvent être facultatifs.
# Un template Jinja peut être inséré dans les valeurs 'query' et 'vars' pour éviter
# d'utiliser des wildcards, de saisir manuellement la liste d'attributs, ou pour filtrer la
# requête à l'aide d'une valeur mise en cache.
#
# Les variables Jinja disponibles sont [REMOTE_ATTRIBUTES, CACHED_VALUES].
# Voir la documentation pour plus de détails :
# https://hermes.insa-strasbourg.fr/fr/setup/configuration/hermes-server/#hermes-server.datamodel.data-type-name.sources.datasource-name.fetch
fetch:
type: fetch
query: >-
SELECT {{ REMOTE_ATTRIBUTES | join(', ') }}
FROM ORA_GROUPS
attrsmapping:
srv_group_id: GROUP_ID
srv_group_name: GROUP_NAME
srv_group_desc: GROUP_DESC
SRVUsers: # Paramètres pour le type de données SRVUsers
primarykeyattr: srv_user_id # Nom de l'attribut du modèle de données utilisé comme clé primaire
# Facultatif : template Jinja permettant de définir la représentation d'une entrée dans les fichiers journaux
toString: "<SRVUsers[{{ srv_user_id }}, {{ srv_login | default('#UNDEF#') }}]>"
# Sources de données à utiliser pour récupérer les données.
# Dans la plupart des cas il n'y en a qu'une seule, mais plusieurs peuvent être utilisées
sources:
datasource_of_example1: # Le nom de la source défini dans hermes.plugins.datasources
# La requête permettant de récupérer les données.
# 'type' est obligatoire et indique au plugin quel type de requête exécuter
# Les valeurs 'type' possibles sont 'add', 'delete', 'fetch' et 'modify'
# 'query' contient la requête à exécuter
# 'vars' est un dictionnaire avec des variables à utiliser (et à nettoyer !) dans la requête
#
# Selon le plugin de source de données utilisé, 'query' et 'vars' peuvent être facultatifs.
# Un template Jinja peut être inséré dans les valeurs 'query' et 'vars' pour éviter
# d'utiliser des wildcards, de saisir manuellement la liste d'attributs, ou pour filtrer la
# requête à l'aide d'une valeur mise en cache.
#
# Les variables Jinja disponibles sont [REMOTE_ATTRIBUTES, CACHED_VALUES].
# Voir la documentation pour plus de détails :
# https://hermes.insa-strasbourg.fr/fr/setup/configuration/hermes-server/#hermes-server.datamodel.data-type-name.sources.datasource-name.fetch
fetch:
type: fetch
query: >-
SELECT {{ REMOTE_ATTRIBUTES | join(', ') }}
FROM ORA_USERS
attrsmapping:
srv_user_id: USER_ID
srv_login: LOGIN
# Garantit que la première lettre de chaque nom sera en majuscule et que les autres seront en minuscules
srv_firstname: "{{ FIRSTNAME | title}}"
srv_lastname: "{{ LASTNAME | title}}"
srv_mail: MAIL
SRVUserPasswords: # Paramètres pour le type de données SRVUserPasswords
primarykeyattr: srv_user_id # Nom de l'attribut du modèle de données utilisé comme clé primaire
# Contraintes d'intégrité entre les types de données, en Jinja.
# ATTENTION : cela peut être affreusement lent, vous devriez donc essayer de faire
# au plus simple et idéalement vous limiter aux clés primaires.
# Les variables Jinja disponibles sont '_SELF' : l'objet actuel, et chaque type de données déclaré
# Pour chaque "typename" declaré, deux variables sont disponibles :
# - typename_pkeys: un ensemble (set) contenant chaque clé primaire du type de données spécifié
# - typename: une liste de dictionnaires contenant chaque entrée du type de données spécifié
# https://hermes.insa-strasbourg.fr/fr/setup/configuration/hermes-server/#hermes-server.datamodel.data-type-name.integrity_constraints
integrity_constraints:
- "{{ _SELF.srv_user_id in SRVUsers_pkeys }}"
# Sources de données à utiliser pour récupérer les données.
# Dans la plupart des cas il n'y en a qu'une seule, mais plusieurs peuvent être utilisées
sources:
datasource_of_example1: # Le nom de la source défini dans hermes.plugins.datasources
# La requête permettant de récupérer les données.
# 'type' est obligatoire et indique au plugin quel type de requête exécuter
# Les valeurs 'type' possibles sont 'add', 'delete', 'fetch' et 'modify'
# 'query' contient la requête à exécuter
# 'vars' est un dictionnaire avec des variables à utiliser (et à nettoyer !) dans la requête
#
# Selon le plugin de source de données utilisé, 'query' et 'vars' peuvent être facultatifs.
# Un template Jinja peut être inséré dans les valeurs 'query' et 'vars' pour éviter
# d'utiliser des wildcards, de saisir manuellement la liste d'attributs, ou pour filtrer la
# requête à l'aide d'une valeur mise en cache.
#
# Les variables Jinja disponibles sont [REMOTE_ATTRIBUTES, CACHED_VALUES].
# Voir la documentation pour plus de détails :
# https://hermes.insa-strasbourg.fr/fr/setup/configuration/hermes-server/#hermes-server.datamodel.data-type-name.sources.datasource-name.fetch
fetch:
type: fetch
query: >-
SELECT p.{{ REMOTE_ATTRIBUTES | join(', p.') }}
FROM ORA_USERPASSWORDS p
# Pour chaque entrée traitée avec succès, nous supprimerons PASSWORD_ENCRYPTED
# et stockerons les LDAP_HASHES fraîchement calculés.
#
# Facultatif. Une requête à exécuter à chaque fois qu'une entrée a été traitée.
# sans erreur.
# 'type' est obligatoire et indique au plugin quel type de requête exécuter
# Les valeurs 'type' possibles sont 'add', 'delete', 'fetch' et 'modify'
# 'query' contient la requête à exécuter
# 'vars' est un dictionnaire avec des variables à utiliser (et à nettoyer !) dans la requête
#
# Selon le plugin de source de données utilisé, 'query' et 'vars' peuvent être facultatifs.
# Un template Jinja peut être inséré dans les valeurs 'query' et 'vars' pour éviter
# d'utiliser des wildcards, de saisir manuellement la liste d'attributs, ou pour filtrer la
# requête à l'aide d'une valeur mise en cache.
#
# Les variables Jinja disponibles sont [REMOTE_ATTRIBUTES, ITEM_CACHED_VALUES, ITEM_FETCHED_VALUES].
# Voir la documentation pour plus de détails :
# https://hermes.insa-strasbourg.fr/fr/setup/configuration/hermes-server/#hermes-server.datamodel.data-type-name.sources.datasource-name.commit_one
commit_one:
type: modify
query: >-
UPDATE ORA_USERPASSWORDS
SET
PASSWORD_ENCRYPTED = NULL,
LDAP_HASHES = :ldap_hashes
WHERE USER_ID = :user_id
vars:
user_id: "{{ ITEM_FETCHED_VALUES.srv_user_id }}"
ldap_hashes: "{{ ';'.join(ITEM_FETCHED_VALUES.srv_password_ldap) }}"
attrsmapping:
srv_user_id: USER_ID
# Déchiffre la valeur PASSWORD_ENCRYPTED pour générer les hachages LDAP.
srv_password_ldap: >-
{{
(
PASSWORD_ENCRYPTED
| crypto_RSA_OAEP('decrypt_from_datasource')
| ldapPasswordHash
)
| default(None if LDAP_HASHES is None else LDAP_HASHES.split(';'))
}}
SRVGroupsMembers:
# Nom de l'attribut du modèle de données utilisé comme clé primaire : un tuple dans le cas présent
primarykeyattr: [srv_group_id, srv_user_id]
# Déclaration des clés étrangères entre des types de données
# https://hermes.insa-strasbourg.fr/fr/setup/configuration/hermes-server/#hermes-server.datamodel.data-type-name.foreignkeys
foreignkeys:
srv_group_id:
from_objtype: SRVGroups
from_attr: srv_group_id
srv_user_id:
from_objtype: SRVUsers
from_attr: srv_user_id
# Contraintes d'intégrité entre les types de données, en Jinja.
# ATTENTION : cela peut être affreusement lent, vous devriez donc essayer de faire
# au plus simple et idéalement vous limiter aux clés primaires.
# Les variables Jinja disponibles sont '_SELF' : l'objet actuel, et chaque type de données déclaré
# Pour chaque "typename" declaré, deux variables sont disponibles :
# - typename_pkeys: un ensemble (set) contenant chaque clé primaire du type de données spécifié
# - typename: une liste de dictionnaires contenant chaque entrée du type de données spécifié
# https://hermes.insa-strasbourg.fr/fr/setup/configuration/hermes-server/#hermes-server.datamodel.data-type-name.integrity_constraints
integrity_constraints:
- "{{ _SELF.srv_user_id in SRVUsers_pkeys and _SELF.srv_group_id in SRVGroups_pkeys }}"
# Sources de données à utiliser pour récupérer les données.
# Dans la plupart des cas il n'y en a qu'une seule, mais plusieurs peuvent être utilisées
sources:
datasource_of_example1: # Le nom de la source défini dans hermes.plugins.datasources
# La requête permettant de récupérer les données.
# 'type' est obligatoire et indique au plugin quel type de requête exécuter
# Les valeurs 'type' possibles sont 'add', 'delete', 'fetch' et 'modify'
# 'query' contient la requête à exécuter
# 'vars' est un dictionnaire avec des variables à utiliser (et à nettoyer !) dans la requête
#
# Selon le plugin de source de données utilisé, 'query' et 'vars' peuvent être facultatifs.
# Un template Jinja peut être inséré dans les valeurs 'query' et 'vars' pour éviter
# d'utiliser des wildcards, de saisir manuellement la liste d'attributs, ou pour filtrer la
# requête à l'aide d'une valeur mise en cache.
#
# Les variables Jinja disponibles sont [REMOTE_ATTRIBUTES, CACHED_VALUES].
# Voir la documentation pour plus de détails :
# https://hermes.insa-strasbourg.fr/fr/setup/configuration/hermes-server/#hermes-server.datamodel.data-type-name.sources.datasource-name.fetch
fetch:
type: fetch
query: >-
SELECT {{ REMOTE_ATTRIBUTES | join(', ') }}
FROM ORA_GROUPSMEMBERS
attrsmapping:
srv_user_id: USER_ID
srv_group_id: GROUP_IDhermes:
cache:
dirpath: /path/to/.hermes/hermes-server/cache
cli_socket:
path: /path/to/.hermes/hermes-server.sock
logs:
logfile: /path/to/.hermes/hermes-server/logs/hermes-server.log
verbosity: info
mail:
server: dummy.example.com
from: Hermes Server <no-reply@example.com>
to:
- user@example.com
plugins:
attributes:
ldapPasswordHash:
settings:
default_hash_types:
- SMD5
- SSHA
- SSHA256
- SSHA512
crypto_RSA_OAEP:
settings:
keys:
decrypt_from_datasource:
hash: SHA256
# ATTENTION - CETTE CLÉ EST FAIBLE ET PUBLIQUE, NE L'UTILISEZ JAMAIS
rsa_key: |-
-----BEGIN RSA PRIVATE KEY-----
MIGrAgEAAiEAstltWwDzmtSSHi7lfKqtUIO4dI8aX/EAopNdR/cWXH8CAwEAAQIh
AKfflFjGNOJQwvJX3Io+/juxO+HFd7SRC++zBD9paZqZAhEA5OtjZQUapRrV/aC5
NXFsswIRAMgBtgpz+t0FxyEXdzlcTwUCEHU6WZ8M2xU7xePpH49Ps2MCEQC+78s+
/WvfNtXcRI+gJfyVAhAjcIWzHC5q4wzgL7psbPGy
-----END RSA PRIVATE KEY-----
datasources:
datasource_of_example1:
type: oracle
settings:
login: HERMES_DUMMY
password: "DuMmY_p4s5w0rD"
port: 1234
server: dummy.example.com
sid: DUMMY
messagebus:
kafka:
settings:
servers:
- dummy.example.com:9093
ssl:
certfile: /path/to/.hermes/dummy.crt
keyfile: /path/to/.hermes/dummy.pem
cafile: /path/to/.hermes/INTERNAL-CA-chain.crt
topic: hermes
hermes-server:
# L'ordre de déclaration des types de données est important :
# - les événements d'ajout/modification seront traités dans l'ordre de déclaration
# - les événements de suppression seront traités dans l'ordre de déclaration inverse
datamodel:
SRVGroups:
primarykeyattr: srv_group_id
toString: "<SRVGroups[{{ srv_group_id }}, {{ srv_group_name | default('#UNDEF#') }}]>"
sources:
datasource_of_example1:
fetch:
type: fetch
query: >-
SELECT {{ REMOTE_ATTRIBUTES | join(', ') }}
FROM ORA_GROUPS
attrsmapping:
srv_group_id: GROUP_ID
srv_group_name: GROUP_NAME
srv_group_desc: GROUP_DESC
SRVUsers:
primarykeyattr: srv_user_id
toString: "<SRVUsers[{{ srv_user_id }}, {{ srv_login | default('#UNDEF#') }}]>"
sources:
datasource_of_example1:
fetch:
type: fetch
query: >-
SELECT {{ REMOTE_ATTRIBUTES | join(', ') }}
FROM ORA_USERS
attrsmapping:
srv_user_id: USER_ID
srv_login: LOGIN
# Garantit que la première lettre de chaque nom sera en majuscule et que les autres seront en minuscules
srv_firstname: "{{ FIRSTNAME | title}}"
srv_lastname: "{{ LASTNAME | title}}"
srv_mail: MAIL
SRVUserPasswords:
primarykeyattr: srv_user_id
# Contraintes d'intégrité entre les types de données, en Jinja.
# https://hermes.insa-strasbourg.fr/fr/setup/configuration/hermes-server/#hermes-server.datamodel.data-type-name.integrity_constraints
integrity_constraints:
- "{{ _SELF.srv_user_id in SRVUsers_pkeys }}"
sources:
datasource_of_example1:
fetch:
type: fetch
query: >-
SELECT p.{{ REMOTE_ATTRIBUTES | join(', p.') }}
FROM ORA_USERPASSWORDS p
# Pour chaque entrée traitée avec succès, nous supprimerons PASSWORD_ENCRYPTED
# et stockerons les LDAP_HASHES fraîchement calculés.
# https://hermes.insa-strasbourg.fr/fr/setup/configuration/hermes-server/#hermes-server.datamodel.data-type-name.sources.datasource-name.commit_one
commit_one:
type: modify
query: >-
UPDATE ORA_USERPASSWORDS
SET
PASSWORD_ENCRYPTED = NULL,
LDAP_HASHES = :ldap_hashes
WHERE USER_ID = :user_id
vars:
user_id: "{{ ITEM_FETCHED_VALUES.srv_user_id }}"
ldap_hashes: "{{ ';'.join(ITEM_FETCHED_VALUES.srv_password_ldap) }}"
attrsmapping:
srv_user_id: USER_ID
# Déchiffre la valeur PASSWORD_ENCRYPTED pour générer les hachages LDAP.
srv_password_ldap: >-
{{
(
PASSWORD_ENCRYPTED
| crypto_RSA_OAEP('decrypt_from_datasource')
| ldapPasswordHash
)
| default(None if LDAP_HASHES is None else LDAP_HASHES.split(';'))
}}
SRVGroupsMembers:
# La clé primaire est un tuple
primarykeyattr: [srv_group_id, srv_user_id]
foreignkeys:
srv_group_id:
from_objtype: SRVGroups
from_attr: srv_group_id
srv_user_id:
from_objtype: SRVUsers
from_attr: srv_user_id
# Contraintes d'intégrité entre les types de données, en Jinja.
# https://hermes.insa-strasbourg.fr/fr/setup/configuration/hermes-server/#hermes-server.datamodel.data-type-name.integrity_constraints
integrity_constraints:
- "{{ _SELF.srv_user_id in SRVUsers_pkeys and _SELF.srv_group_id in SRVGroups_pkeys }}"
sources:
datasource_of_example1:
fetch:
type: fetch
query: >-
SELECT {{ REMOTE_ATTRIBUTES | join(', ') }}
FROM ORA_GROUPSMEMBERS
attrsmapping:
srv_user_id: USER_ID
srv_group_id: GROUP_IDhermes-client-usersgroups_ldap-config
hermes:
cache:
dirpath: /path/to/.hermes/hermes-client-usersgroups_ldap/cache
cli_socket:
path: /path/to/.hermes/hermes-client-usersgroups_ldap.sock
logs:
logfile: /path/to/.hermes/hermes-client-usersgroups_ldap/logs/hermes-client-usersgroups_ldap.log
verbosity: info
mail:
server: dummy.example.com
from: hermes-client-usersgroups_ldap <no-reply@example.com>
to:
- user@example.com
plugins:
messagebus:
kafka:
settings:
servers:
- dummy.example.com:9093
ssl:
certfile: /path/to/.hermes/dummy.crt
keyfile: /path/to/.hermes/dummy.pem
cafile: /path/to/.hermes/INTERNAL-CA-chain.crt
topic: hermes
group_id: hermes-grp
hermes-client-usersgroups_ldap:
uri: ldaps://ldap.example.com:636
binddn: cn=account,dc=example,dc=com
bindpassword: s3cReT_p4s5w0rD
basedn: dc=example,dc=com
users_ou: ou=users,dc=example,dc=com
groups_ou: ou=groups,dc=example,dc=com
# OBLIGATOIRE : nom de l'attribut DN pour les utilisateurs, les mots de passe utilisateur et les groupes
# Vous devez définir des valeurs pour les trois, même si vous n'utilisez pas certains d'entre eux
dnAttributes:
Users: uid
UserPasswords: uid
Groups: cn
propagateUserDNChangeOnGroupMember: true
groupsObjectclass: groupOfNames
# Il est possible de définir une valeur par défaut pour certains attributs pour les Users, UserPasswords et Groups
# La valeur par défaut sera appliquée lors du traitements des événements added et modified, si l'attribut local n'a pas de valeur
defaultValues:
# Hack pour permettre la création d'un groupe vide, néessaire à cause du "MUST member" dans le schéma
Groups:
member: ""
# Les attributs locaux répertoriés ici ne seront pas stockés dans LDAP pour les types Users, UserPasswords and Groups
attributesToIgnore:
Users:
- user_pkey
UserPasswords:
- user_pkey
Groups:
- group_pkey
hermes-client:
# Politique d'auto remédiation à appliquer dans la file d'erreurs pour les événements
# concernant un même objet
# - "disabled" : pas d'auto remédiation, les événements sont empilés tels quels (par défaut)
# - "conservative" :
# - fusionne un événement added avec un événement modified suivant
# - fusionne deux événements modified successifs
# - "maximum" :
# - fusionne un événement added avec un événement modified suivant
# - fusionne deux événements modified successifs
# - supprime les deux événements lorsqu'un événement added est suivi d'un événement removed
# - fusionne un événement removed suivi d'un événement added dans un événement modified
# - supprime un événement modified lorsqu'il est suivi d'un événement removed
autoremediation: conservative
datamodel:
Users:
hermesType: SRVUsers
# Facultatif : template Jinja permettant de définir la représentation d'une entrée dans les fichiers journaux
toString: "<Users[{{ user_pkey }}, {{ uid | default('#UNDEF#') }}]>"
attrsmapping:
user_pkey: srv_user_id
uid: srv_login
givenname: srv_firstname
sn: srv_lastname
mail: srv_mail
# Compose le displayname avec deux autres attributs
displayname: "{{ srv_firstname ~ ' ' ~ srv_lastname }}"
#
# Valeurs statiques
# Les définir ici plutôt que dans les valeurs par défaut (defaultValues)
# permettra de propager d'éventuelles modifications sur chaque entrée
#
objectclass: "{{ ['person', 'inetOrgPerson', 'eduPerson'] }}"
UserPasswords:
hermesType: SRVUserPasswords
attrsmapping:
user_pkey: srv_user_id
userPassword: srv_password_ldap
Groups:
hermesType: SRVGroups
toString: "<Groups[{{ group_pkey }}, {{ cn | default('#UNDEF#') }}]>"
attrsmapping:
group_pkey: srv_group_id
cn: srv_group_name
description: srv_group_desc
#
# Valeurs statiques
# Les définir ici plutôt que dans les valeurs par défaut (defaultValues)
# permettra de propager d'éventuelles modifications sur chaque entrée
#
objectclass: "{{ ['groupOfNames'] }}"
GroupsMembers:
hermesType: SRVGroupsMembers
attrsmapping:
# Le nom des clés 'user_pkey' et 'group_pkey' est imposé
user_pkey: srv_user_id
group_pkey: srv_group_idFlux d’attributs
flowchart LR
subgraph Oracle
direction LR
ORA_GROUPS
ORA_USERS
ORA_USERPASSWORDS
ORA_GROUPSMEMBERS
end
subgraph ORA_GROUPS
direction LR
ORA_GROUPS_GROUP_ID["GROUP_ID"]
ORA_GROUPS_GROUP_NAME["GROUP_NAME"]
ORA_GROUPS_GROUP_DESC["GROUP_DESC"]
end
subgraph ORA_USERS
direction LR
ORA_USERS_USER_ID["USER_ID"]
ORA_USERS_LOGIN["LOGIN"]
ORA_USERS_FIRSTNAME["FIRSTNAME"]
ORA_USERS_LASTNAME["LASTNAME"]
ORA_USERS_EMAIL["EMAIL"]
end
subgraph ORA_USERPASSWORDS
direction LR
ORA_USERPASSWORDS_USER_ID["USER_ID"]
ORA_USERPASSWORDS_PASSWORD_ENCRYPTED["PASSWORD_ENCRYPTED"]
ORA_USERPASSWORDS_LDAP_HASHES["LDAP_HASHES"]
end
subgraph ORA_GROUPSMEMBERS
direction LR
ORA_GROUPSMEMBERS_USER_ID["USER_ID"]
ORA_GROUPSMEMBERS_GROUP_ID["GROUP_ID"]
end
subgraph hermes-server
direction LR
SRVGroups
SRVUsers
SRVUserPasswords
SRVGroupsMembers
end
subgraph SRVGroups
direction LR
SRVGroups_srv_group_id["srv_group_id"]
SRVGroups_srv_group_name["srv_group_name"]
SRVGroups_srv_group_desc["srv_group_desc"]
end
ORA_GROUPS_GROUP_ID --> SRVGroups_srv_group_id
ORA_GROUPS_GROUP_NAME --> SRVGroups_srv_group_name
ORA_GROUPS_GROUP_DESC --> SRVGroups_srv_group_desc
subgraph SRVUsers
direction LR
SRVUsers_srv_user_id["srv_user_id"]
SRVUsers_srv_login["srv_login"]
SRVUsers_srv_firstname["srv_firstname"]
SRVUsers_srv_lastname["srv_lastname"]
SRVUsers_srv_mail["srv_mail"]
end
ORA_USERS_USER_ID --> SRVUsers_srv_user_id
ORA_USERS_LOGIN --> SRVUsers_srv_login
ORA_USERS_FIRSTNAME -->|filtre Jinja 'title'| SRVUsers_srv_firstname
ORA_USERS_LASTNAME -->|filtre Jinja 'title'| SRVUsers_srv_lastname
ORA_USERS_EMAIL --> SRVUsers_srv_mail
subgraph SRVUserPasswords
direction LR
SRVUserPasswords_srv_user_id["srv_user_id"]
SRVUserPasswords_srv_password_ldap["srv_password_ldap"]
end
ORA_USERPASSWORDS_USER_ID --> SRVUserPasswords_srv_user_id
ORA_USERPASSWORDS_PASSWORD_ENCRYPTED -->|"filtre Jinja 'crypto_RSA_OAEP | ldapPasswordHash'"| SRVUserPasswords_srv_password_ldap
ORA_USERPASSWORDS_LDAP_HASHES <-->|LDAP_HASHED est rempli par, ou fournit sa valeur| SRVUserPasswords_srv_password_ldap
subgraph SRVGroupsMembers
direction LR
SRVGroupsMembers_srv_user_id["srv_user_id"]
SRVGroupsMembers_srv_group_id["srv_group_id"]
end
ORA_GROUPSMEMBERS_USER_ID --> SRVGroupsMembers_srv_user_id
ORA_GROUPSMEMBERS_GROUP_ID --> SRVGroupsMembers_srv_group_id
subgraph hermes-client-usersgroups_ldap
direction LR
ClientGroups
ClientUsers
ClientUserPasswords
ClientGroupsMembers
end
subgraph ClientGroups
direction LR
ClientGroups_group_pkey["group_pkey"]
ClientGroups_cn["cn"]
ClientGroups_description["description"]
ClientGroups_objectclass["objectclass"]
end
SRVGroups_srv_group_id --> ClientGroups_group_pkey
SRVGroups_srv_group_name --> ClientGroups_cn
SRVGroups_srv_group_desc --> ClientGroups_description
subgraph ClientUsers
direction LR
ClientUsers_user_pkey["user_pkey"]
ClientUsers_uid["uid"]
ClientUsers_givenname["givenname"]
ClientUsers_sn["sn"]
ClientUsers_mail["mail"]
ClientUsers_displayname["displayname"]
ClientUsers_objectclass["objectclass"]
end
SRVUsers_srv_user_id --> ClientUsers_user_pkey
SRVUsers_srv_login --> ClientUsers_uid
SRVUsers_srv_firstname --> ClientUsers_givenname
SRVUsers_srv_firstname --> ClientUsers_displayname
SRVUsers_srv_lastname --> ClientUsers_displayname
SRVUsers_srv_lastname --> ClientUsers_sn
SRVUsers_srv_mail --> ClientUsers_mail
subgraph ClientUserPasswords
direction LR
ClientUserPasswords_user_pkey["user_pkey"]
ClientUserPasswords_userPassword["userPassword"]
end
SRVUserPasswords_srv_user_id --> ClientUserPasswords_user_pkey
SRVUserPasswords_srv_password_ldap --> ClientUserPasswords_userPassword
subgraph ClientGroupsMembers
direction LR
ClientGroupsMembers_user_pkey["user_pkey"]
ClientGroupsMembers_group_pkey["group_pkey"]
end
SRVGroupsMembers_srv_user_id --> ClientGroupsMembers_user_pkey
SRVGroupsMembers_srv_group_id --> ClientGroupsMembers_group_pkey
subgraph LDAP
direction LR
LDAPGroups
LDAPUsers
end
subgraph LDAPGroups
direction LR
LDAPGroups_cn["cn"]
LDAPGroups_description["description"]
LDAPGroups_objectclass["objectclass"]
LDAPGroups_member["member"]
end
ClientGroups_cn --> LDAPGroups_cn
ClientGroups_description --> LDAPGroups_description
ClientGroups_objectclass --> LDAPGroups_objectclass
ClientGroupsMembers_user_pkey -->|converti en DN d'utilisateur| LDAPGroups_member
ClientGroupsMembers_group_pkey -->|converti en DN de groupe| LDAPGroups_member
subgraph LDAPUsers
direction LR
LDAPUsers_uid["uid"]
LDAPUsers_givenname["givenname"]
LDAPUsers_displayname["displayname"]
LDAPUsers_displayname["displayname"]
LDAPUsers_sn["sn"]
LDAPUsers_mail["mail"]
LDAPUsers_objectclass["objectclass"]
LDAPUsers_userPassword["userPassword"]
end
ClientUsers_uid --> LDAPUsers_uid
ClientUsers_givenname --> LDAPUsers_givenname
ClientUsers_displayname --> LDAPUsers_displayname
ClientUsers_sn --> LDAPUsers_sn
ClientUsers_mail --> LDAPUsers_mail
ClientUsers_objectclass --> LDAPUsers_objectclass
ClientUserPasswords_userPassword --> LDAPUsers_userPassword
classDef global fill:#fafafa,stroke-dasharray: 5 5
class Oracle,hermes-server,hermes-client-usersgroups_ldap,LDAP global
Développement
Cette section contient la documentation pour démarrer avec le développement de plugin et la contribution “noyau” d’Hermes.
Journalisation
Une instance Logger est disponible via la variable “__hermes__.logger”. Comme cette variable est déclarée comme builtin, elle est toujours disponible et ne nécessite aucun import ou appel à logging.getLogger().
Contribuer
Avant de proposer une pull request pour fusionner du code dans Hermes, vous devez vous assurer que votre code :
- fournit des docstrings et des annotations de type
- a été formaté avec black
- est conforme à Flake8
- passe la suite de tests
tox peut être utilisé pour valider les trois dernières conditions, en exécutant l’une des commandes ci-dessous :
# # Test séquentiel (lent mais plus détaillé) uniquement sur la version Python par défaut de votre système
tox run -e linters,tests
# Test en parallèle (plus rapide, mais sans détails) uniquement sur la version Python par défaut de votre système
tox run-parallel -e linters,tests
# Test séquentiel (lent mais plus détaillé) sur toutes les versions Python compatibles - elles doivent être disponibles sur votre système
tox run
# Test en parallèle (plus rapide, mais sans détails) sur toutes les versions Python compatibles - elles doivent être disponibles sur votre système
tox run-paralleltox >= 4 doit être installé mais est probablement disponible dans les dépôts de votre distribution
Sous-sections de Développement
Plugins
Quel que soit son type, un plugin est toujours un dossier nommé ‘plugin_name’ contenant au moins les 4 fichiers suivants :
Code source du plugin
Hermes va essayer d’importer le fichier plugin_name.py. Il est possible de découper le code du plugin en plusieurs fichiers et dossiers, mais le plugin sera toujours importé à partir de ce fichier.
Pour plus de détails sur l’API du plugin, veuillez consulter les sections suivantes :
- Attributs
- Clients
- Sources de données
- Consommateurs de bus de messages
- Producteurs de bus de messages
Certains modules utilitaires sont disponibles dans helpers :
helpers.command: pour exécuter des commandes locales sur l’hôte du clienthelpers.ldaphashes: pour générer les hachages LDAP à partir de mots de passe en clairhelpers.randompassword: pour générer des mots de passe aléatoires avec des contraintes spécifiques
Schéma de configuration du plugin
Selon le type de plugin, le fichier de schéma de configuration varie légèrement.
Schéma de configuration du plugin pour les plugins clients
Hermes va essayer de valider la configuration du plugin avec un schéma de validation Cerberus spécifié dans un fichier YAML : config-schema-client-plugin_name.yml.
Le fichier de validation des plugins clients doit être vide ou ne contenir qu’une seule clé de premier niveau qui doit être le nom du plugin préfixé par hermes-client-.
Exemple pour le nom du plugin usersgroups_flatfiles_emails_of_groups :
# https://docs.python-cerberus.org/validation-rules.html
hermes-client-usersgroups_flatfiles_emails_of_groups:
type: dict
required: true
empty: false
schema:
destDir:
type: string
required: true
empty: false
onlyTheseGroups:
type: list
required: true
nullable: false
default: []
schema:
type: stringSchéma de configuration du plugin pour les autres types de plugins
Hermes va essayer de valider la configuration du plugin avec un schéma de validation Cerberus spécifié dans un fichier YAML : config-schema-plugin-plugin_name.yml.
Même si le plugin ne nécessite aucune configuration, il nécessite tout de même un fichier de validation vide.
Exemple pour le nom de plugin ldapPasswordHash :
# https://docs.python-cerberus.org/validation-rules.html
default_hash_types:
type: list
required: false
nullable: false
empty: true
default: []
schema:
type: string
allowed:
- MD5
- SHA
- SMD5
- SSHA
- SSHA256
- SSHA512Fichier README.md du plugin
La documentation doit être écrite dans README.md et doit contenir les sections suivantes :
# `plugin_name` attribute plugin
## Description
## Configuration
## Usage
Uniquement pour les plugins `attributes` et `datasources`.
## Datamodel
Uniquement pour les plugins `clients`.Dépendances du plugin : requirements.txt
Même si le plugin n’a pas de dépendance Python, veuillez créer un fichier pip requirements.txt commençant par un commentaire contenant le chemin du plugin et se terminant par une ligne vide.
Exemple :
# plugins/attributes/crypto_RSA_OAEP
pycryptodomex==3.21.0
Sous-sections de Plugins
Attributs
Description
Un plugin d’attribut est simplement une classe fille de AbstractAttributePlugin conçue pour implémenter un filtre Jinja.
Conditions requises
Voici une implémentation de plugin minimale commentée qui ne fera rien.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Nécessaire pour hériter de la classe AbstractAttributePlugin
from lib.plugins import AbstractAttributePlugin
# Nécessaire pour utiliser le statut Jinja Undefined
from jinja2 import Undefined
# Nécessaire pour les annotations de type
from typing import Any
# Nécessaire pour indiquer à hermes quelle classe il devra instancier
HERMES_PLUGIN_CLASSNAME = "MyPluginClassName"
class MyPluginClassName(AbstractAttributePlugin):
def __init__(self, settings: dict[str, any]):
# Crée une nouvelle instance du plugin et stocke une copie de
# son dictionnaire de paramètres dans self._settings
super().__init__(settings)
# ... code d'initialisation du plugin
def filter(self, value: Any | None | Undefined) -> Any:
# Filtre qui ne fait rien
return valueMéthode filter
Vous devriez consulter la documentation officielle de Jinja sur les filtres personnalisés.
La méthode filter() prend toujours au moins un paramètre value et peut en avoir d’autres.
Son prototype générique est :
def filter(self, value: Any | None | Undefined, *args: Any, **kwds: Any) -> Any:En Jinja, on l’utilise ainsi :
"{{ value | filter }}"
"{{ value | filter(otherarg1, otherarg2) }}"
"{{ value | filter(otherarg1=otherarg1_value, otherarg2=otherarg2_value) }}"Les expressions ci-dessus seront remplacées par la valeur de retour du filtre.
Exemple : le plugin d’attribut datetime_format
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Nécessaire pour hériter de la classe AbstractAttributePlugin
from lib.plugins import AbstractAttributePlugin
# Nécessaire pour utiliser le statut Jinja Undefined
from jinja2 import Undefined
# Nécessaire pour les annotations de type
from typing import Any
from datetime import datetime
# Nécessaire pour indiquer à hermes quelle classe il devra instancier
HERMES_PLUGIN_CLASSNAME = "DatetimeFormatPlugin"
class DatetimeFormatPlugin(AbstractAttributePlugin):
def filter(self, value:Any, format:str="%H:%M %d-%m-%y") -> str | Undefined:
if isinstance(value, Undefined):
return value
if not isinstance(value, datetime):
raise TypeError(f"""Invalid type '{type(value)}' for datetime_format value: must be a datetime""")
return value.strftime(format)Le filtre peut désormais être utilisé ainsi :
"{{ a_datetime_attribute | datetime_format }}"
"{{ a_datetime_attribute | datetime_format('%m/%d/%Y, %H:%M:%S') }}"
"{{ a_datetime_attribute | datetime_format(format='%m/%d/%Y') }}"Clients
Description
Un plugin client est simplement une classe fille de GenericClient conçue pour implémenter des gestionnaires d’événements simples et pour diviser leurs tâches en sous-tâches atomiques afin de garantir un retraitement cohérent en cas d’erreur.
Conditions requises
Voici une implémentation de plugin minimale commentée qui ne fera rien, car elle n’implémente pas encore de gestionnaires d’événements.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Nécessaire pour hériter de la classe GenericClient
from clients import GenericClient
# Nécessaire pour les annotations de type des gestionnaires d'événements
from lib.config import HermesConfig # uniquement si le plugin implémente une méthode __init__()
from lib.datamodel.dataobject import DataObject
from typing import Any
# Nécessaire pour indiquer à hermes quelle classe il devra instancier
HERMES_PLUGIN_CLASSNAME = "MyPluginClassName"
class MyPluginClassName(GenericClient):
def __init__(self, config: HermesConfig):
# La variable 'config' ne doit être ni utilisée ni modifiée par le plugin
super().__init__(config)
# ... code d'initialisation du pluginMéthodes de gestion d’événements
Gestionnaires d’événements
Pour chaque type de données configuré dans le modèle de données client, le plugin peut implémenter un gestionnaire pour chacun des 5 types d’événements possibles :
added: lorsqu’un objet est ajoutérecycled: lorsqu’un objet est restauré depuis la corbeille (ne sera jamais appelé si la corbeille est désactivée)modified: lorsqu’un objet est modifiétrashed: lorsqu’un objet est placé dans la corbeille (ne sera jamais appelé si la corbeille est désactivée)removed: lorsqu’un objet est supprimé
Si un événement est reçu par un client, mais que son gestionnaire n’est pas implémenté, il sera ignoré silencieusement.
Chaque gestionnaire doit être nommé on_datatypename_eventtypename.
Exemple pour un type de données Mydatatype :
def on_Mydatatype_added(
self,
objkey: Any,
eventattrs: "dict[str, Any]",
newobj: DataObject,
):
pass
def on_Mydatatype_recycled(
self,
objkey: Any,
eventattrs: "dict[str, Any]",
newobj: DataObject,
):
pass
def on_Mydatatype_modified(
self,
objkey: Any,
eventattrs: "dict[str, Any]",
newobj: DataObject,
cachedobj: DataObject,
):
pass
def on_Mydatatype_trashed(
self,
objkey: Any,
eventattrs: "dict[str, Any]",
cachedobj: DataObject,
):
pass
def on_Mydatatype_removed(
self,
objkey: Any,
eventattrs: "dict[str, Any]",
cachedobj: DataObject,
):
passArguments des gestionnaires d’événements
-
objkey: la clé primaire de l’objet affecté par l’événement -
eventattrs: un dictionnaire contenant les nouveaux attributs de l’objet. Son contenu dépend du type d’événement :added/recycled: contient tous les noms d’attributs comme clé, et leurs valeurs respectives comme valeurmodified: contient toujours trois clés :added: attributs qui n’étaient pas définis auparavant, mais qui ont maintenant une valeur. Noms d’attributs comme clé, et leurs valeurs respectives comme valeurmodified: attributs qui étaient définis auparavant, mais dont la valeur a changé. Noms d’attributs comme clé, et leurs nouvelles valeurs respectives comme valeurremoved: attributs qui étaient définis auparavant, mais qui n’ont plus de valeur. Noms d’attributs comme clé, etNonecomme valeur
trashed/removed: toujours un dictionnaire vide{}
-
newobj: une instanceDataObjectcontenant toutes les valeurs mises à jour de l’objet affecté par l’événement (voir Instances de DataObject ci-dessous) -
cachedobj: une instanceDataObjectcontenant toutes les valeurs précédentes (mises en cache) de l’objet affecté par l’événement (voir Instances de DataObject ci-dessous)
Instances de DataObject
Chaque objet de type de données peut être utilisé intuitivement via une instance DataObject.
Utilisons un exemple simple avec ces valeurs d’objet User (sans adresse email) du modèle de données ci-dessous :
{
"user_pkey": 42,
"uid": "jdoe",
"givenname": "John",
"sn": "Doe"
}hermes-client:
datamodel:
Users:
hermesType: SRVUsers
attrsmapping:
user_pkey: srv_user_id
uid: srv_login
givenname: srv_firstname
sn: srv_lastname
mail: srv_mailMaintenant, si cet objet est stocké dans une instance de DataObject newobj :
>>> newobj.getPKey()
42
>>> newobj.user_pkey
42
>>> newobj.uid
'jdoe'
>>> newobj.givenname
'John'
>>> newobj.sn
'Doe'
>>> newobj.mail
AttributeError: 'Users' object has no attribute 'mail'
>>> hasattr(newobj, 'sn')
True
>>> hasattr(newobj, 'mail')
FalseGestion d’erreur
Toute exception non gérée levée dans un gestionnaire d’événements sera gérée par GenericClient, qui ajoutera l’événement à sa file d’erreurs.
GenericClient essaiera alors de re-traiter l’événement régulièrement jusqu’à ce qu’il réussisse, et appellera donc le gestionnaire d’événements.
Mais parfois, un gestionnaire d’événements doit procéder à plusieurs opérations sur la cible. Imaginons un gestionnaire comme celui-ci :
def on_Mydatatype_added(
self,
objkey: Any,
eventattrs: "dict[str, Any]",
newobj: DataObject,
):
if condition:
operation1() # "condition" vaut False, operation1() n'est pas appelée
operation2() # aucune erreur ne se produit
operation3() # celle-ci lève une exceptionA chaque nouvelle tentative la fonction operation2() sera à nouveau appelée, mais ce n’est probablement pas souhaitable.
Il est possible de découper les étapes d’un gestionnaire d’événements en utilisant l’attribut currentStep hérité de GenericClient, afin de retenter les traitements des événements en erreur depuis l’étape qui avait échouée.
currentStep démarre toujours à 0 lors du traitement normal des événements. L’évolution de sa valeur doit être gérée par les plugins.
Lorsqu’une erreur se produit, la valeur currentStep est enregistrée dans la file d’erreurs avec l’événement.
Les re-tentatives de traitement de la file d’erreurs restaureront toujours la valeur de currentStep avant d’appeler le gestionnaire d’événements.
Ainsi en l’implémentant comme ci-dessous, operation2() ne sera appelée qu’une seule fois.
def on_Mydatatype_added(
self,
objkey: Any,
eventattrs: "dict[str, Any]",
newobj: DataObject,
):
if self.currentStep == 0:
if condition:
operation1() # "condition" vaut False, operation1() n'est pas appelée
# Indique que des modifications ont été propagées sur la cible
self.isPartiallyProcessed = True
self.currentStep += 1
if self.currentStep == 1:
operation2() # aucune erreur ne se produit
# Indique que des modifications ont été propagées sur la cible
self.isPartiallyProcessed = True
self.currentStep += 1
if self.currentStep == 2:
operation3() # celle-ci lève une exception
# Indique que des modifications ont été propagées sur la cible
self.isPartiallyProcessed = True
self.currentStep += 1Comprendre l’attribut isPartiallyProcessed
L’attribut isPartiallyProcessed hérité de GenericClient indique si le traitement de l’événement en cours a déjà propagé des modifications sur la cible. Il doit donc être mis à True dès que la moindre modification a été propagée sur la cible.
Il permet à l’autoremédiation de fusionner les événements dont currentStep est différent de 0 mais dont les étapes précédentes n’ont rien modifié sur la cible.
isPartiallyProcessed vaut toujours False lors d’un traitement d’événement normal. L’évolution de sa valeur doit être gérée par les plugins.
Avec l’exemple d’implémentation ci-dessus, et une exception levée par operation3(), l’autoremédiation ne tenterait pas de fusionner cet événement partiellement traité avec d’éventuels événements ultérieurs, car isPartiallyProcessed vaut True.
Avec l’exemple d’implémentation ci-dessus, mais une exception levée par operation2(), l’autoremédiation essaierait de fusionner cet événement non traité avec d’éventuels événements ultérieurs, car isPartiallyProcessed vaudrait encore False.
Gestionnaire d’événement on_save
Un gestionnaire d’événements spécial qui peut être implémenté lorsque Hermes vient de sauvegarder ses fichiers cache : une fois que certains événements ont été traités et qu’aucun événement n’est en attente sur le bus de messages, ou avant la fin.
Comme ce gestionnaire n’est pas un gestionnaire d’événements standard, GenericClient ne peut pas gérer les exceptions pour lui et procéder à une nouvelle tentative ultérieurement.
Toute exception non gérée levée dans ce gestionnaire d’événements mettra immédiatement fin au client.
Il appartient à l’implémentation d’éviter les erreurs.
def on_save(self):
passPropriétés et méthodes de GenericClient
Propriétés
-
currentStep: intNuméro d’étape de l’événement en cours de traitement. Nécessaire pour permettre aux clients de reprendre un événement où il a échoué.
-
isPartiallyProcessed: boolIndique si le traitement de l’événement en cours a déjà propagé des modifications sur la cible.
Doit être défini àTruedès que la moindre modification a été propagée sur la cible.
Il permet à l’autoremédiation de fusionner les événements dontcurrentStepest différent de 0 mais dont les étapes précédentes n’ont rien modifié sur la cible. -
isAnErrorRetry: boolAttribut en lecture seule qui peut permettre au gestionnaire d’événements de savoir si l’événement actuel est en cours de traitement dans le cadre d’une nouvelle tentative suite à une erreur. Cela peut être utile par exemple pour effectuer des vérifications complémentaires lorsqu’une bibliothèque génère des exceptions même si elle a correctement traité les modifications demandées, comme le fait parfois python-ldap.
-
config: dict[str, Any]Dictionnaire contenant la configuration du plugin client.
Méthodes
-
def getDataobjectlistFromCache(objtype: str) -> DataObjectListRenvoie le cache du type d’objet spécifié, par référence. Lève
IndexErrorsiobjtypen’est pas valideAvertissementToute modification du contenu du cache corrompra votre client !!!
-
def getObjectFromCache(objtype: str, objpkey: Any ) -> DataObjectRenvoie une copie complète (deepcopy) d’un objet depuis le cache. Lève une erreur
IndexErrorsiobjtypen’est pas valide ou siobjpkeyn’est pas trouvée -
def mainLoop() -> NoneBoucle principale du client
AvertissementAppelée par Hermes, pour démarrer le client. Ne doit jamais être appelée ni surchargée
Consommateurs de bus de messages
Description
Un plugin consommateur de bus de messages est simplement une classe fille de AbstractMessageBusConsumerPlugin conçue pour permettre à hermes-client d’interroger n’importe quel bus de messages.
Il faudra y implémenter des méthodes pour se connecter et se déconnecter au bus de messages, et pour consommer les événements disponibles.
Fonctionnalités requises du bus de messages
- Permettre de spécifier une clé/catégorie de message (producteurs) et de filtrer les messages d’une clé/catégorie spécifiée (consommateurs)
- Permettre de consommer un même message plusieurs fois
- Implémenter un offset de message, permettant aux consommateurs de rechercher le prochain message attendu. Comme il sera stocké dans le cache des clients, cet offset doit être de l’un des types Python ci-dessous :
- int
- float
- str
- bytes
Conditions requises
Voici une implémentation de plugin minimale commentée qui ne fera rien.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Nécessaire pour hériter de la classe AbstractMessageBusConsumerPlugin
from lib.plugins import AbstractMessageBusConsumerPlugin
# Required to return Event
from lib.datamodel.event import Event
# Nécessaire pour les annotations de type
from typing import Any, Iterable
# Nécessaire pour indiquer à hermes quelle classe il devra instancier
HERMES_PLUGIN_CLASSNAME = "MyMessagebusConsumerPluginClassName"
class MyMessagebusConsumerPluginClassName(AbstractMessageBusConsumerPlugin):
def __init__(self, settings: dict[str, Any]):
# Crée une nouvelle instance du plugin et stocke une copie de
# son dictionnaire de paramètres dans self._settings
super().__init__(settings)
# ... code d'initialisation du plugin
def open(self) -> Any:
"""Établit la connexion avec le bus de message"""
def close(self):
"""Ferme la connexion avec le bus de message"""
def seekToBeginning(self):
"""Rechercher le premier événement (le plus ancien) dans la file du bus
de messages"""
def seek(self, offset: Any):
"""Rechercher l'événement de l'offset spécifié dans la file du bus de
messages"""
def setTimeout(self, timeout_ms: int | None):
"""Définit le délai d'attente (en millisecondes) avant d'interrompre
l'attente du prochain événement. Si None, attend indéfiniment"""
def findNextEventOfCategory(self, category: str) -> Event | None:
"""Recherche le premier message avec la catégorie spécifiée et le renvoie,
ou renvoie None si aucun n'a été trouvé"""
def __iter__(self) -> Iterable:
"""Itère sur le bus de messages en renvoyant chaque événement, en
commençant à l'offset courant.
Lorsque chaque événement a été consommé, attends le message suivant
jusqu'à ce que le délai d'expiration défini avec setTimeout() soit
atteint"""Méthodes à implémenter
Méthodes de connexion
Comme elles ne prennent aucun argument, les méthodes open et close doivent s’appuyer sur les paramètres du plugin.
Méthode seekToBeginning
Rechercher le premier événement (le plus ancien) dans la file du bus de messages.
Méthode seek
Rechercher l’événement de l’offset spécifié dans la file du bus de messages.
Méthode setTimeout
Définit le délai d’attente (en millisecondes) avant d’interrompre l’attente du prochain événement. Si None, attend indéfiniment.
Méthode findNextEventOfCategory
Recherche le premier message avec la catégorie spécifiée et le renvoie, ou renvoie None si aucun n’a été trouvé.
Comme cette méthode parcourt le bus de messages, l’offset courant sera modifié.
Méthode __iter__
Renvoie un Iterable qui génère (yield) tous les événements disponibles sur le bus de messages, à partir de l’offset courant.
Ces attributs non-sérialisables de l’instance Event doivent être définis avant de le générer (yield) :
offset(int | float | str | bytes) : offset de l’événement dans le bus de messagestimestamp(dattime.datetime) : horodatage de l’événement
Propriétés et méthodes de la classe Event
Méthodes
-
@staticmethod def from_json(jsondata: str | dict[Any, Any]) -> EventDésérialise une chaîne ou un dictionnaire JSON en une nouvelle instance Event et la renvoie
Producteurs de bus de messages
Description
Un plugin producteurs de bus de messages est simplement une classe fille de AbstractMessageBusProducerPlugin conçue pour permettre à hermes-server d’émettre des événements vers n’importe quel bus de messages.
Il faudra y implémenter des méthodes pour se connecter et se déconnecter au bus de messages, et pour y produire (émettre) des événements.
Fonctionnalités requises du bus de messages
- Permettre de spécifier une clé/catégorie de message (producteurs) et de filtrer les messages d’une clé/catégorie spécifiée (consommateurs)
- Permettre de consommer un même message plusieurs fois
- Implémenter un offset de message, permettant aux consommateurs de rechercher le prochain message attendu. Comme il sera stocké dans le cache des clients, cet offset doit être de l’un des types Python ci-dessous :
- int
- float
- str
- bytes
Conditions requises
Voici une implémentation de plugin minimale commentée qui ne fera rien.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Nécessaire pour hériter de la classe AbstractMessageBusProducerPlugin
from lib.plugins import AbstractMessageBusProducerPlugin
# Nécessaire pour les annotations de type
from lib.datamodel.event import Event
from typing import Any
# Nécessaire pour indiquer à hermes quelle classe il devra instancier
HERMES_PLUGIN_CLASSNAME = "MyMessagebusProducerPluginClassName"
class MyMessagebusProducerPluginClassName(AbstractMessageBusProducerPlugin):
def __init__(self, settings: dict[str, Any]):
# Crée une nouvelle instance du plugin et stocke une copie de
# son dictionnaire de paramètres dans self._settings
super().__init__(settings)
# ... code d'initialisation du plugin
def open(self) -> Any:
"""Établit la connexion avec le bus de message"""
def close(self):
"""Ferme la connexion avec le bus de message"""
def _send(self, event: Event):
"""Émet l'événement spécifié sur le bus de message"""Méthodes à implémenter
Méthodes de connexion
Comme elles ne prennent aucun argument, les méthodes open et close doivent s’appuyer sur les paramètres du plugin.
Méthode _send
Attention à surcharger la méthode _send() et non pas send().
La méthode send() est un wrapper qui gère les exceptions lors de l’appel à _send().
Envoie un message contenant l’événement spécifié.
Le consommateur aura besoin des propriétés suivantes :
evcategory(str) : clé/catégorie de l’événement (stockée dans l’événement)timestamp(dattime.datetime) : horodatage de l’événementoffset(int | float | str | bytes) : offset de l’événement dans le bus de messages
Voir Propriétés et méthodes de la classe Event ci-dessous.
Propriétés et méthodes de la classe Event
Propriétés
-
evcategory: strClé/catégorie à attribuer au message
Méthodes
-
def to_json() -> strSérialise un événement dans une chaîne JSON qui pourra être utilisée ultérieurement pour être désérialisée dans une nouvelle instance Event
Sources de données
Description
Un plugin de source de données est simplement une classe fille de AbstractDataSourcePlugin conçue pour permettre à hermes-server d’interroger n’importe quelle source de données.
Il faudra y implémenter des méthodes pour se connecter et se déconnecter de la source de données, et pour récupérer, ajouter, modifier et supprimer des données.
Conditions requises
Voici une implémentation de plugin minimale commentée qui ne fera rien.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Nécessaire pour hériter de la classe AbstractDataSourcePlugin
from lib.plugins import AbstractDataSourcePlugin
# Nécessaire pour les annotations de type
from typing import Any
# Nécessaire pour indiquer à hermes quelle classe il devra instancier
HERMES_PLUGIN_CLASSNAME = "MyDatasourcePluginClassName"
class MyDatasourcePluginClassName(AbstractDataSourcePlugin):
def __init__(self, settings: dict[str, Any]):
# Crée une nouvelle instance du plugin et stocke une copie de
# son dictionnaire de paramètres dans self._settings
super().__init__(settings)
# ... code d'initialisation du plugin
def open(self):
"""Établit la connexion avec la source de données"""
def close(self):
"""Ferme la connexion avec la source de données"""
def fetch(
self,
query: str | None,
vars: dict[str, Any],
) -> list[dict[str, Any]]:
"""Récupère des données à partir de la source de données avec la requête
spécifiée et/ou des variables.
Renvoie une liste de dictionnaires contenant chaque entrée extraite, avec
REMOTE_ATTRIBUTES comme clés et les valeurs extraites correspondantes
comme valeurs"""
def add(self, query: str | None, vars: dict[str, Any]):
"""Ajoute des données à la source de données avec la requête spécifiée
et/ou des variables"""
def delete(self, query: str | None, vars: dict[str, Any]):
"""Supprime des données de la source de données avec la requête spécifiée
et/ou des variables"""
def modify(self, query: str | None, vars: dict[str, Any]):
"""Modifie des données sur la source de données avec la requête spécifiée
et/ou des variables"""Méthodes
Méthodes de connexion
Comme elles ne prennent aucun argument, les méthodes open et close doivent s’appuyer sur les paramètres du plugin.
Pour les sources de données sans état, elles peuvent ne rien faire.
Méthode fetch
Cette méthode est appelée pour récupérer des données et les fournir à hermes-server.
Selon l’implémentation du plugin, elle peut s’appuyer sur l’argument query, l’argument vars, ou les deux.
Le résultat doit être renvoyé sous forme de liste de dictionnaires. Chaque élément de la liste est une entrée récupérée stockée dans un dictionnaire, avec le nom de l’attribut comme clé et sa valeur correspondante comme valeur. La valeur doit être de l’un des types Python suivants :
None- int
- float
- str
- datetime.datetime
- bytes
Les types itérables autorisés sont :
- list
- dict
Les valeurs doivent impérativement être d’un des types mentionnés ci-dessus. Tous les autres types sont invalides.
Méthodes add, delete, et modify
Ces méthodes permettent de modifier le contenu de la source de données, lorsque cela est possible.
Selon les contraintes techniques de la source de données, elles peuvent toutes être implémentées de la même manière ou non.
Selon l’implémentation du plugin, elles peuvent s’appuyer sur l’argument query, l’argument vars, ou les deux.
Gestion d’erreur
Aucune exception ne devrait être interceptée afin de permettre à la gestion d’erreur d’Hermes de fonctionner correctement.