Chapter 4
Examples
This section contains some examples of different use cases, and their config files.
This section contains some examples of different use cases, and their config files.
In this example, we have a unique Datasource (an Oracle database) that we’ll use to convert typical users, password, groups and group membership data to fill an LDAP server.
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:
cache:
dirpath: /path/to/.hermes/hermes-server/cache
enable_compression: true
backup_count: 1
cli_socket:
path: /path/to/.hermes/hermes-server.sock # Facultative, required to use cli
owner: user_login # Facultative
group: group_name # Facultative
# Facultative, '0600' will be used by default.
# The value MUST be prefixed by a 0 to indicate that it's an octal integer
mode: 0660
logs:
logfile: /path/to/.hermes/hermes-server/logs/hermes-server.log
backup_count: 31 # 1 month
verbosity: info
mail:
server: dummy.example.com
from: Hermes Server <no-reply@example.com>
to:
- user@example.com
plugins:
# Attribute transform plugins (jinja filters)
attributes:
ldapPasswordHash:
settings:
default_hash_types:
- SMD5
- SSHA
- SSHA256
- SSHA512
crypto_RSA_OAEP:
settings:
keys:
decrypt_from_datasource:
hash: SHA256
# WARNING - THIS KEY IS WEAK AND PUBLIC, NEVER USE IT
rsa_key: |-
-----BEGIN RSA PRIVATE KEY-----
MIGrAgEAAiEAstltWwDzmtSSHi7lfKqtUIO4dI8aX/EAopNdR/cWXH8CAwEAAQIh
AKfflFjGNOJQwvJX3Io+/juxO+HFd7SRC++zBD9paZqZAhEA5OtjZQUapRrV/aC5
NXFsswIRAMgBtgpz+t0FxyEXdzlcTwUCEHU6WZ8M2xU7xePpH49Ps2MCEQC+78s+
/WvfNtXcRI+gJfyVAhAjcIWzHC5q4wzgL7psbPGy
-----END RSA PRIVATE KEY-----
# SERVER ONLY - Sources used to fetch data. At lease one must be defined
datasources:
datasource_of_example1: # Source name. Use whatever you want. Will be used in datamodel
type: oracle # Source type. A datasource plugin with this name must exist
settings: # Settings of current source
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 # Interval between two data update, in seconds
# The declaration order of data types is important:
# - add/modify events will be processed in the declaration order
# - remove events will be processed in the reversed declaration order
datamodel:
SRVGroups: # Settings for SRVGroups data type
primarykeyattr: srv_group_id # Attribute name that will be used as primary key
# Facultative template of object string representation that will be used in logs
toString: "<SRVGroups[{{ srv_group_id }}, {{ srv_group_name | default('#UNDEF#') }}]>"
sources: # datasource(s) to use to fetch data. Usually one, but several could be used
datasource_of_example1: # The source name set in hermes.plugins.datasources
# The query to fetch data.
# 'type' is mandatory and indicate to plugin which flavor of query to proceed
# Possible 'type' values are 'add', 'delete', 'fetch' and 'modify'
# 'query' is the query to send
# 'vars' is a dict with vars to use (and sanitize !) in query
#
# According to source type, 'query' and 'vars' may be facultative.
# A Jinja template can be inserted in 'query' and 'vars' values to avoid wildcards
# and manually typing the attribute list, or to filter the query using a cached value.
#
# Jinja vars available are [REMOTE_ATTRIBUTES, CACHED_VALUES].
# See documentation for details:
# https://hermes.insa-strasbourg.fr/en/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: # Settings for SRVUsers data type
primarykeyattr: srv_user_id # Attribute name that will be used as primary key
# Facultative template of object string representation that will be used in logs
toString: "<SRVUsers[{{ srv_user_id }}, {{ srv_login | default('#UNDEF#') }}]>"
sources: # datasource(s) to use to fetch data. Usually one, but several could be used
datasource_of_example1: # The source name set in hermes.plugins.datasources
# The query to fetch data.
# 'type' is mandatory and indicate to plugin which flavor of query to proceed
# Possible 'type' values are 'add', 'delete', 'fetch' and 'modify'
# 'query' is the query to send
# 'vars' is a dict with vars to use (and sanitize !) in query
#
# According to source type, 'query' and 'vars' may be facultative.
# A Jinja template can be inserted in 'query' and 'vars' values to avoid wildcards
# and manually typing the attribute list, or to filter the query using a cached value.
#
# Jinja vars available are [REMOTE_ATTRIBUTES, CACHED_VALUES].
# See documentation for details:
# https://hermes.insa-strasbourg.fr/en/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
# Ensure first letter of each names is uppercase, and other are lowercase
srv_firstname: "{{ FIRSTNAME | title}}"
srv_lastname: "{{ LASTNAME | title}}"
srv_mail: MAIL
SRVUserPasswords: # Settings for SRVUserPasswords data type
primarykeyattr: srv_user_id # Attribute name that will be used as primary key
# Integrity constraints between datamodel type, in Jinja.
# WARNING: could be very slow, keep it as simple as possible, and focused upon
# primary keys
# Jinja vars available are '_SELF': the current object, and every types declared
# For each "typename" declared, two vars are available:
# - typename_pkeys: a set with every primary keys
# - typename: a list of dict containing each entries
# https://hermes.insa-strasbourg.fr/en/setup/configuration/hermes-server/#hermes-server.datamodel.data-type-name.integrity_constraints
integrity_constraints:
- "{{ _SELF.srv_user_id in SRVUsers_pkeys }}"
sources: # datasource(s) to use to fetch data. Usually one, but several could be used
datasource_of_example1: # The source name set in hermes.plugins.datasources
# The query to fetch data.
# 'type' is mandatory and indicate to plugin which flavor of query to proceed
# Possible 'type' values are 'add', 'delete', 'fetch' and 'modify'
# 'query' is the query to send
# 'vars' is a dict with vars to use (and sanitize !) in query
#
# According to source type, 'query' and 'vars' may be facultative.
# A Jinja template can be inserted in 'query' and 'vars' values to avoid wildcards
# and manually typing the attribute list, or to filter the query using a cached value.
#
# Jinja vars available are [REMOTE_ATTRIBUTES, CACHED_VALUES].
# See documentation for details:
# https://hermes.insa-strasbourg.fr/en/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
# For each entry successfully processed, we'll remove PASSWORD_ENCRYPTED
# and store the freshly computed LDAP_HASHES.
#
# Facultative. The query to run each time an item of current data have been processed
# without errors.
# 'type' is mandatory and indicate to plugin which flavor of query to proceed
# Possible 'type' values are 'add', 'delete', 'fetch' and 'modify'
# 'query' is the query to send
# 'vars' is a dict with vars to use (and sanitize !) in query
#
# According to source type, 'query' and 'vars' may be facultative.
# A Jinja template can be inserted in 'query' and 'vars' values to avoid wildcards
# and manually typing the attribute list, or to filter the query using a cached value.
#
# Jinja vars available are [REMOTE_ATTRIBUTES, ITEM_CACHED_VALUES, ITEM_FETCHED_VALUES].
# See documentation for details:
# https://hermes.insa-strasbourg.fr/en/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
# Decipher PASSWORD_ENCRYPTED value to generate the LDAP hashes.
srv_password_ldap: >-
{{
(
PASSWORD_ENCRYPTED
| crypto_RSA_OAEP('decrypt_from_datasource')
| ldapPasswordHash
)
| default(None if LDAP_HASHES is None else LDAP_HASHES.split(';'))
}}
SRVGroupsMembers:
# Attribute names that will be used as primary key: here is is a tuple
primarykeyattr: [srv_group_id, srv_user_id]
# Foreign keys declaration between data types
# https://hermes.insa-strasbourg.fr/en/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
# Integrity constraints between datamodel type, in Jinja.
# WARNING: could be very slow, keep it as simple as possible, and focused upon
# primary keys
# Jinja vars available are '_SELF': the current object, and every types declared
# For each "typename" declared, two vars are available:
# - typename_pkeys: a set with every primary keys
# - typename: a list of dict containing each entries
# https://hermes.insa-strasbourg.fr/en/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(s) to use to fetch data. Usually one, but several could be used
datasource_of_example1: # The source name set in hermes.plugins.datasources
# The query to fetch data.
# 'type' is mandatory and indicate to plugin which flavor of query to proceed
# Possible 'type' values are 'add', 'delete', 'fetch' and 'modify'
# 'query' is the query to send
# 'vars' is a dict with vars to use (and sanitize !) in query
#
# According to source type, 'query' and 'vars' may be facultative.
# A Jinja template can be inserted in 'query' and 'vars' values to avoid wildcards
# and manually typing the attribute list, or to filter the query using a cached value.
#
# Jinja vars available are [REMOTE_ATTRIBUTES, CACHED_VALUES].
# See documentation for details:
# https://hermes.insa-strasbourg.fr/en/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_ID
hermes:
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
# WARNING - THIS KEY IS WEAK AND PUBLIC, NEVER USE IT
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:
# The declaration order of data types is important:
# - add/modify events will be processed in the declaration order
# - remove events will be processed in the reversed declaration order
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
# Ensure first letter of each names is uppercase, and other are lowercase
srv_firstname: "{{ FIRSTNAME | title}}"
srv_lastname: "{{ LASTNAME | title}}"
srv_mail: MAIL
SRVUserPasswords:
primarykeyattr: srv_user_id
# Integrity constraints between datamodel type, in Jinja.
# https://hermes.insa-strasbourg.fr/en/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
# For each entry successfully processed, we'll remove PASSWORD_ENCRYPTED
# and store the freshly computed LDAP_HASHES.
# https://hermes.insa-strasbourg.fr/en/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
# Decipher PASSWORD_ENCRYPTED value to generate the LDAP hashes.
srv_password_ldap: >-
{{
(
PASSWORD_ENCRYPTED
| crypto_RSA_OAEP('decrypt_from_datasource')
| ldapPasswordHash
)
| default(None if LDAP_HASHES is None else LDAP_HASHES.split(';'))
}}
SRVGroupsMembers:
# The primary key is a 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
# Integrity constraints between datamodel type, in Jinja.
# https://hermes.insa-strasbourg.fr/en/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_ID
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
# MANDATORY: Name of DN attribute for Users, UserPasswords and Groups
# You have to set up values for the three, even if you don't use some of the types
dnAttributes:
Users: uid
UserPasswords: uid
Groups: cn
propagateUserDNChangeOnGroupMember: true
groupsObjectclass: groupOfNames
# It is possible to set a default value for some attributes for Users,
# UserPasswords and Groups. The default value will be set on added and modified
# events if the local attribute has no value
defaultValues:
# Hack to allow creation of an empty group, because of the "MUST member" in schema
Groups:
member: ""
# The local attributes listed here won't be stored in LDAP for Users,
# UserPasswords and Groups
attributesToIgnore:
Users:
- user_pkey
UserPasswords:
- user_pkey
Groups:
- group_pkey
hermes-client:
# Autoremediation policy to use in error queue for events concerning a same object
# - "disabled" : no autoremediation, events are stacked as is (default)
# - "conservative" :
# - merge an added event with a following modified event
# - merge two successive modified events
# - "maximum" :
# - merge an added event with a following modified event
# - merge two successive modified events
# - delete both events when an added event is followed by a removed event
# - merge a removed event followed by an added event in a modified event
# - delete a modified event when it is followed by a removed event
autoremediation: conservative
datamodel:
Users:
hermesType: SRVUsers
# Facultative template of object string representation that will be used in logs
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 the displayname with two other attributes
displayname: "{{ srv_firstname ~ ' ' ~ srv_lastname }}"
#
# Static values
# Defining them here instead of in default values will allow changes
# propagation on each entry
#
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
#
# Static values
# Defining them here instead of in default values will allow changes
# propagation on each entry
#
objectclass: "{{ ['groupOfNames'] }}"
GroupsMembers:
hermesType: SRVGroupsMembers
attrsmapping:
# 'user_pkey' and 'group_pkey' keys can't be renamed
user_pkey: srv_user_id
group_pkey: srv_group_id
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 -->|'title' Jinja filter| SRVUsers_srv_firstname ORA_USERS_LASTNAME -->|'title' Jinja filter| 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 -->|"'crypto_RSA_OAEP | ldapPasswordHash' Jinja filter"| SRVUserPasswords_srv_password_ldap ORA_USERPASSWORDS_LDAP_HASHES <-->|LDAP_HASHED is filled by, or provide its value| 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 -->|converted to user DN| LDAPGroups_member ClientGroupsMembers_group_pkey -->|converted to group DN| 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