Database User Persister
This plug-in is very flexible in that most database columns are optional and it allows you to specify extra where clauses and search filters to select the set of users. It also allows to fetch role information from separate tables.
Note: This persister also supports insertion and deletion of users and can be used to iterate over users.
How this plug-in finds a user record
There are two ways how this plugin finds a user record for getting user data, updating user data and deleting user data. The user record is always fetched using a select statement given a primary key and applying the configured filters (additional where clause). The two variants differ in how the primary key is determined:- The primary key for selecting the user record is the username itself. This is by far the most common and most efficient method to obtain user data.
- The primary key is determined by a separate query including the username. The query can be configured. This way of selecting the user record is more flexible but results in an additional select statement.
sqlDataSource) userTableName) colUserName) userNameResolveQuery) Such a query is useful if the username (used on the login page) is not part of the user table.
The query must be such that - given the username - it returns one value that can be used as primary key in the user table. The query must contain one question mark (?) which will be substituted by the username (a string).
If the query returns no records, it results in the user not being found.
If the query returns more than one record, it results in the username being ambiguous.
If this property is not defined, the username itself is used as primary key in the user table (the usual and efficient way).
Note: If this property is defined user insertion by this plugin is no more possible.
colPassword) In general the type of this database column is expected to be BYTE or VARBYTE because password hashes are byte sequences. However, if a password hash function is used that returns a character sequence (for example the password itself) this also works with column type CHAR or VARCHAR.
The plug-in tries to find out automatically whether the password hash is binary or string type by reading a value from the database and looking at the type of the returned object. This may lead to problems with NULL values or "too intelligent" JDBC drivers that implicitly convert HEX- or base64-strings to binary data. The optional property "Is Pwd Hash String Type" can be used to tell the plug-in explicitly what data type this column is.
isPwdHashStringType) The value
TRUE indicates that the password hash column is a string type column. The value FALSE indicates that the password hash column is a binary type column.
If this optional property is not defined or empty, the plug-in tries to determine the type of column automatically (see description of property "Col Password").
colAuthMethod) CHAR, VARCHAR) and its value may be NULL. defaultAuthMethod) colNextAuthMethod) CHAR, VARCHAR) and its value may be NULL. defaultNextAuthMethod) colAuthMigrationDate) DATETIME or TIMESTAMP and its value may be NULL. colUserLocked) Authenticators usually set a user locked after some number of consecutively failed login attempts. This column type is either
CHAR or NUMBER. The value "0" is treated as false, any other value is treated as true.
If this column is not specified, users are not considered locked.
colUserLockReason) This can be the hole description of the reason or a key to the string resource.
This column type is either
CHAR or VARCHAR. colUserLockDate) The type of this column either
DATETIME or TIMESTAMP. colFailedLogins) NUMBER.
If this column is not specified, the failed logins are not counted.
colFailedTokenCounts) CLOB (or a DB-type equivalent).
If this column is not specified, the failed attempts in the flow-based REST authentication API are not counted.
colFailedLoginsBeforeLatestLogin) NUMBER.
If this column is not specified, the failed logins before the latest successful login are not counted.
colTotalLogins) NUMBER.
If this column is not specified, the successful logins are not counted.
colPasswordChangeForced) This column type is either
CHAR or NUMBER. The value "0" is treated as false, any other value is treated as true.
If this column is not specified, no password change is enforced.
colPasswordDeliveryDate) The type of this column either
DATETIME or TIMESTAMP. Note that it will work with most data types but depending on the chosen database data type, only the date without the time is stored.
If this column is not specified, the delivery date of the latest password is not provided to callers.
colOtherCredentialsDeliveryTimestamps) The type of every referenced column is either a
DATETIME or TIMESTAMP.
This information can be used by components that care about not delivering more than one user credential at the same time.
If this column is not specified, no delivery dates are provided to callers.
colPasswordGenerationDate) The type of this column either
DATETIME or TIMESTAMP.
This information is needed by components in which the generation date of a password and its delivery date is not necessarily the same. This can - for example - be the case when a generated credential is held back because another credential for the same user is delivered at the same time.
If this column is not specified, no password generation date is provided to callers.
colLatestPasswordChange) The type of this column either
DATETIME or TIMESTAMP.
colNextEnforcedPasswordChange) The type of this column either
DATETIME or TIMESTAMP.
colPasswordOrderedFlag) This column type is either
CHAR or NUMBER. The value "0" is treated as false, any other value is treated as true.
If this column is not specified, the password order state is always reported to be
false. colPasswordOrderedUser) This column type is either
CHAR or VARCHAR.
If this column is not specified, the password order user is always
null. colPasswordOrderedDate) This column type is either
DATETIME or TIMESTAMP.
If this column is not specified, the password order date is always
null. colFailedPasswordResets) The name of the database column with the number of failed password reset attempts for flow-based password reset. The type of this column is NUMBER
Security note: If this column is not specified, failed password reset attempts are not counted, which enables brute-force attacks.
colRoleString) The type of this column is
CHAR or VARCHAR.
Note: There are other ways to determine a user's roles (see other configuration properties). If the roles granted to a user are obtained from other tables (via foreign keys), leave this property empty and use the property
roles-query instead. rolesQuery) Col Role String, this property allows to retrieve the roles based on foreign tables.
This property defines an arbitrary SQL query that returns the roles associated with the user. Note: The statement must be such that the query returns rows consisting of one column only with the granted role!
You may use the string
${userId} to reference the user id of the authenticated user inside the SQL query. The reference may be used once or multiple times.
Example: In the following example, the user table is USER, there is role table ROLE and a table with the user-to-role mappings USER2ROLE:
roles-query="SELECT r.role_name from ROLE r, USER u, USER2ROLE u2r where u.userName = ${userId} AND u.id = u2r.user AND u2r.role = r.id"
grantRoles) This set of roles is added to the otherwise determined set of roles. Thus, it does not replace otherwise determined roles but can be used in conjunction with other methods.
colUserValid) The type of this column is either
CHAR or NUMBER. The value "0" is treated as invalid, any other value is treated as valid.
If this column is not specified, all users are considered to be valid.
colUserNotValidAfter) DATETIME or TIMESTAMP. colUserNotValidBefore) DATETIME or TIMESTAMP. colLatestSuccessfulLogin) DATETIME or TIMESTAMP. colSecondLatestSuccessfulLogin) DATETIME or TIMESTAMP. colLatestLoginAttempt) DATETIME or TIMESTAMP. colFirstLogin) DATETIME or TIMESTAMP. colUnlockAttempts) NUMBER. colLatestUnlockAttempt) DATETIME or TIMESTAMP. colSelfRegisteredFlag) CHAR or NUMBER. The value "0" is treated as false, any other value is treated as true.
If this column is not specified, it will be assumed that no users are self-registered.
colSelfRegistrationDate) DATETIME or TIMESTAMP. colRealm) Setting this column is mandatory when using the Multi-Realm feature. The column specified here must not also be used as the database column of a Context Data Item.
colChannelVerificationResends) NUMBER.
If this column is not specified, the number of allowed resend attempts is not limited.
colLastGSID) colLastGSIDDate) colSecretQuestionsEnabled) additionalWhereClause) The SQL query without an additional where clause is
SELECT * FROM usertable WHERE colusername = 'username' (real values for "usertable", "colusername" are taken from the configuration and "username" is taken from the user name input field of the login mask.
The SQL query with an additional where clause
xyz is: SELECT * FROM usertable WHERE colusername = 'username' AND xyz
. Example: If the value of this configuration setting is
"GROUP = 'cus1' AND MANDATE = 'abc'" the resulting query is SELECT * FROM usertable WHERE colusername = 'username' AND GROUP = 'cus1' AND MANDATE = 'abc'
See also property
search-condition-query: It offers a more powerful (although slightly less efficient) way to control the set of valid users. additionalIteratorWhereClause) The SQL query without an additional where clause is
SELECT colusername FROM usertable (real values for "usertable", "colusername" are taken from the configuration.
The SQL query with an additional where clause
xyz is: SELECT colusername FROM usertable WHERE xyz
. Example: If the value of this configuration setting is
"GROUP = 'cus1' AND MANDATE = 'abc'" the resulting query is SELECT colusername FROM usertable WHERE GROUP = 'cus1' AND MANDATE = 'abc'
See also property
search-condition-query: It offers a more powerful (although slightly less efficient) way to control the set of valid users. iteratorQuery) Specifying such a query is only necessary if the username cannot be used as primary key in the user table (this only if property "User Name Resolve Query" is specified).
The query must be such that it returns one-column records one username (userid) per row.
Note that his query is used both when returning all user ids and when returning only matching user ids (filtered by the user). Thus, the query must be such that LIKE-clauses against context data columns work. This usually means that you must join the result with the user table (even if the user id is not read from the usertable) so the LIKE-clauses can access the context data of the user table. Failing to do so will result in runtime SQL syntax exceptions!
Note: If this property is specified, "Additional Iterator Clauses" and the deleted flag is ignored. They must be part of the query itself!
searchConditionQuery) (See also configuration property
additional-where-clause: it offers a different, slightly more efficient but less powerful way to limit the set of valid users).
After the user has been found by username (and matching the optional additional where clause as specified by configuration property
additional-where-clause) the query specified by this configuration property is executed. If the result of the query is true or 1, the user is considered valid. In all other cases, the user is not valid, i.e. the behaviour is as if the user would not exist.
The value of this configuration property can be empty (no effect) or any valid SQL query. You can use values of the user record (Record selected from table specified by configuration property user-table-name by user name and optionally additional where clause) in the query as follows: ${xxx} refers to the field (column) xxx from the selected user record.
Example: In our example the selected user record has the following values (column name = value): user_id = 'freddie', person_no = 13, ...
Further, there is a different database table PERSON which is referenced by the user table. The table PERSON has a column of type boolean called "valid" which indicates whether a person record is valid or not. Consider the following value for this configuration property: SELECT p.valid FROM PERSON p WHERE p.person_no = ${person_no}
Thus, when looking for the user record (given the username and the matching the optional additonal where part), the above query is executed where ${person_no} is substituted by the value 13 of field person_no of the selected user record.
contextDataColumns) A list of database columns that are loaded/stored in the user's context data container.
Use either an appropriately typed instance (preferred) or the legacy type using auto-detection (the default up to IAM 6.4).
additionalContextData) user-table-name by user name) in the query as follows: ${xxx} refers to the field (column) xxx from the selected user record.
Note: These context data values are read only! When fetching user records, the query will be executed for each user and the values will be added to the context data container. Modified, new or deleted values will not be written when user records are updated.
Also note that context data fields defined in configuration property context-data-columns override corresponding entries in this property.
Example:
SELECT p.mobile_no FROM person p WHERE p.person_no = ${person_no}
colDeleted) Note: If a user is deleted and this property is defined, the record is only marked as deleted and not really removed from the database! If this property is not defined and a user is deleted, the record is deleted from the database. The type of this column is either
NUMBER (recommended) or CHAR. The value "1" represents a deleted user, "0" represents a non-deleted user. colVersionId) Such a technical column is used by some applications or libraries (such as Hibernate) to implement optimistic locking.
Note that this plugin still uses its own data-based optimistic locking mechanism. It just increments the value within a transaction in order to be compliant with other components' locking mechanisms.
The column must be of an integer type. Usually a long type is used.
colRecordInsertionDate) The type of the column must be compatible with a timestamp.
Note that - if configured (see separate property) - user information may also be written to the database at the same time.
colRecordInsertionUser) Note that - if configured (see separate property) - the insertion date may also be written to the database at the same time.
colRecordModificationDate) The type of the column must be compatible with a timestamp.
Note that - if configured (see separate property) - user information may also be written to the database at the same time.
colRecordModificationUser) Note that - if configured (see separate property) - the modification date may also be written to the database at the same time.
recordModificationUser) additionalInsertData) This allows you to add arbitrary fixed or dynamic values when a new record is created. This is useful if some database fields may not be NULL but are not inserted by this plugin by default.
Caution: Make sure to appropriately escape values (e.g. use single quotes around strings). They are used as provided in the SQL insert statements. This allows calling database dependent functions (e.g. in order to get a sequence number, system date, etc).
Caution: If the columns specified here are the same as configured in the context data fields or in any "Col ..." property, remove them from the other places for this persister instance.
userChangeEventListeners) rowsetRangePattern) This property only has an effect if used in connection with a "Database User Store".
A string formatter pattern describing how to constrain the result set to a subrange of all results. Set this value in case Airlock IAM cannot determine the optimal query pattern automatically.
The first argument is the number of rows to skip (offset), and the second argument is the number of rows to return (limit).
If no pattern is set, Airlock IAM will attempt to automatically determine the query based on the database type.
Commonly used patterns:
- LIMIT %2$d OFFSET %1$d for MySQL, MariaDB, H2, HSQLDB, PostgreSQL, SQLite
- OFFSET %1$d ROWS FETCH NEXT %2$d ROWS ONLY for SQL:2008 standard, Derby, SQL Server 2012, Oracle 12c
caseSensitiveExactMatching) This property only has an effect if used in connection with a "Database User Store".
A String formatter pattern describing how to compare a string field on equality, case sensitive.If the database is already case sensitive (most DBs, except MySQL and MariaDB) the default value can be used.
The argument (%s) is the name of the field to compare, and the question mark is the value to be searched for.
Commonly used patterns:
- %s = ? – in most cases
- BINARY `%s` = ? – for MySQL and MariaDB databases with standard (case insensitive) settings
caseSensitiveMatching) This property only has an effect if used in connection with a "Database User Store".
A String formatter pattern describing how to search in a string field, case sensitive.If the database is already case sensitive (most DBs, except MySQL and MariaDB) the default value can be used.
The argument (%s) is the name of the field to compare, and the question mark is the value to be searched for. Important: This is used for approximate matching, e.g. "contains" matching, where the search value could be like "%bla%", thus the "LIKE" operator must be used instead of the equality sign.
Commonly used patterns:
- %s LIKE ? – in most cases
- %s LIKE BINARY ? – for MySQL and MariaDB databases with standard (case insensitive) settings
- %s COLLATE latin1_general_cs LIKE ? – alternative for MySQL and MariaDB, possibly more efficient, but collation must be known
caseInsensitiveExactMatching) This property only has an effect if used in connection with a "Database User Store".
A String formatter pattern describing how to compare a string database field on equality, case insensitive.If the database is already case insensitive (e.g. MySQL and MariaDB) the default value can be used.
The argument (%s) is the name of the field to compare, and the question mark is the value to be searched for. For some databases a less efficient "LIKE" operator has to be used for this.
Depending on used DB and version, as well as the specific setup, different values can be the most efficient. For large user repositories, a DB expert might be consulted or tests with different settings should be performed.
Commonly used patterns:
- LOWER( %s ) = LOWER ( ? ) – works for most DBs, tested with Oracle DB. Note that this is only really efficient, if a lower-case index is created for the relevant columns.
- %s COLLATE latin1_general_ci = ? – recommended for MSSQL.
- %s = ? – for databases with default case insensitive matching, e.g. MySQL and MariaDB with standard settings
caseInsensitiveMatching) This property only has an effect if used in connection with a "Database User Store".
A String formatter pattern describing how to compare a string database field on equality, case insensitive.If the database is already case insensitive (e.g. MySQL and MariaDB) the default value can be used.
The argument (%s) is the name of the field to compare, and the question mark is the value to be searched for. Important: This is used for approximate matching, e.g. "contains" matching, where the search value could be like "%bla%", thus the "LIKE" operator must be used instead of the equality sign.
Depending on used DB and version, as well as the specific setup, different values can be the most efficient. For large user repositories, a DB expert might be consulted or tests with different settings should be performed.
Commonly used patterns:
- LOWER( %s ) LIKE LOWER ( ? ) – works for most DBs, tested with Oracle DB. Note that this is only really efficient, if a lower-case index is created for the relevant columns.
- %s COLLATE latin1_general_ci LIKE ? – recommended for MSSQL.
- %s LIKE ? – for databases with default case insensitive matching, e.g. MySQL and MariaDB with standard settings
- %s ILIKE ? – for PostgreSQL databases
type: DatabaseUserPersister
id: DatabaseUserPersister-xxxxxx
displayName:
comment:
properties:
additionalContextData:
additionalInsertData:
additionalIteratorWhereClause:
additionalWhereClause:
caseInsensitiveExactMatching: LOWER( %s ) = LOWER ( ? )
caseInsensitiveMatching: LOWER( %s ) LIKE LOWER ( ? )
caseSensitiveExactMatching: %s = ?
caseSensitiveMatching: %s LIKE ?
colAuthMethod:
colAuthMigrationDate:
colChannelVerificationResends:
colDeleted:
colFailedLogins:
colFailedLoginsBeforeLatestLogin:
colFailedPasswordResets:
colFailedTokenCounts:
colFirstLogin:
colLastGSID:
colLastGSIDDate:
colLatestLoginAttempt:
colLatestPasswordChange:
colLatestSuccessfulLogin:
colLatestUnlockAttempt:
colNextAuthMethod:
colNextEnforcedPasswordChange:
colOtherCredentialsDeliveryTimestamps:
colPassword:
colPasswordChangeForced:
colPasswordDeliveryDate:
colPasswordGenerationDate:
colPasswordOrderedDate:
colPasswordOrderedFlag:
colPasswordOrderedUser:
colRealm:
colRecordInsertionDate:
colRecordInsertionUser:
colRecordModificationDate:
colRecordModificationUser:
colRoleString:
colSecondLatestSuccessfulLogin:
colSecretQuestionsEnabled:
colSelfRegisteredFlag:
colSelfRegistrationDate:
colTotalLogins:
colUnlockAttempts:
colUserLockDate:
colUserLockReason:
colUserLocked:
colUserName:
colUserNotValidAfter:
colUserNotValidBefore:
colUserValid:
colVersionId:
contextDataColumns:
defaultAuthMethod:
defaultNextAuthMethod:
grantRoles:
isPwdHashStringType: true
iteratorQuery:
recordModificationUser: Medusa
rolesQuery:
rowsetRangePattern:
searchConditionQuery:
sqlDataSource:
userChangeEventListeners:
userNameResolveQuery:
userTableName: