LDAP Authentication
With the LDAP authentication mechanism, users (and passwords) and roles (or groups) are loaded from an Active Directory or LDAP domain.
Setting up LDAP Authentication
Version 4.9 and Above
Encrypting Credentials in the Configuration
To encode credentials defined in dvserver-standalone.xml, do the following:
1. Encrypt the password via a CLI command:
CALL SYSADMIN.executeCli(script => '/subsystem=elytron/expression=encryption:create-expression(resolver=dv-encryption-resolver, clear-text=Password123)', "maskInLogs" => true) ;;
The output will be like the following:
{
"outcome" => "success",
"result" => {"expression" => "${ENC::dv-encryption-resolver:RUxZAUMQ42XclErCI4BDa6nzzp/r/aqx31Cu/aNwUjvCgr5Tofw=}"}
}
2. Copy the expression value to use in the configuration file like this:
<dir-contexts>
<dir-context name="ldap-connection" principal="CN=Administrator,CN=Users,DC=mydomain,DC=local" url="ldap://192.168.222.102/">
<credential-reference clear-text="${ENC::dv-encryption-resolver:RUxZAUMQ42XclErCI4BDa6nzzp/r/aqx31Cu/aNwUjvCgr5Tofw=}"/>
</dir-context>
</dir-contexts>
Configuration
To configure LDAP authentication, please modify dvserver-standalone.xml as follows:
1. Replace the following code:
<custom-realm class-name="com.datavirtuality.dv.core.teiid.users.DVLoginModule" module="com.datavirtuality.dv" name="DataVirtualityRealm"/>
with a snippet like this:
<ldap-realm dir-context="ldap-connection" direct-verification="true" name="DataVirtualityRealm">
<identity-mapping rdn-identifier="cn" search-base-dn="DC=mydomain,DC=local" use-recursive-search="true">
<attribute-mapping>
<attribute filter="(member={1})" filter-base-dn="OU=Users,DC=mydomain,DC=local" from="cn" to="Roles"/>
</attribute-mapping>
</identity-mapping>
</ldap-realm>
Filter-based authentication can be set up as follows
<ldap-realm dir-context="ldap-connection" direct-verification="true" name="DataVirtualityRealm">
<identity-mapping filter-name="(userPrincipalName={0}@mydomain.local)" rdn-identifier="userPrincipalName" use-recursive-search="true" search-base-dn="DC=mydomain,DC=local">
<attribute-mapping>
<attribute filter="(member={1})" filter-base-dn="OU=Users,DC=mydomain,DC=local" from="cn" to="Roles"/>
</attribute-mapping>
</identity-mapping>
</ldap-realm>
Adapt the values in the above fragment to the settings of your environment.
2. Find the </expression-resolver> tag and paste the following code, first replacing the required parts with your values:
<dir-contexts>
<dir-context name="ldap-connection" principal="CN=Administrator,CN=Users,DC=mydomain,DC=local" url="ldap://192.168.0.68/">
<credential-reference clear-text="Password123"/>
</dir-context>
</dir-contexts>
3. Find the </policy-decider-module> tag and insert the following code below:
<ldap>
<property name="defaultAdminGroup" value="super-group"/>
<property name="displayUserName" value="cn"/>
<property name="roleRecursion" value="5"/>
</ldap>
Other properties supported by DVLdapExtContext
and DVLdapContext
can be added in this section.
For example, to set a conditional rule for displaying user names in the system, you can configure the following property:
<property name="displayUserName" value="has('userPrincipalName') ? stripDomain(userPrincipalName) : distinguishedName"/>
Versions 2.1.12 - 4.8
Encrypting Credentials in the Configuration
To encode credentials defined in dvserver-standalone.xml, do the following:
1. Encrypt the password using the UTILS.encrypt
function:
SELECT UTILS.encrypt('password');;
2. Set encoded values for java.naming.security.credentials
and bindCredential
:
<module-option name="java.naming.security.credentials" value="ZjyMp28QJE0D47Rld0LOFw=="/>
<module-option name="bindCredential" value="ZjyMp28QJE0D47Rld0LOFw=="/>
3. Set dv.encrypted.credentials
to TRUE
:
<module-option name="dv.encrypted.credentials" value="true"/>
Configuration
To use authentication based on LDAP, you need to configure the <authentication> section in the current dv-security security-domain defined in dvserver-standalone.xml.
Here is an example configuration:
<security-domain name="dv-security" cache-type="default">
<authentication>
<login-module code="com.datavirtuality.dv.core.teiid.users.ldap.ext.DVLdapExtLoginModule" module="com.datavirtuality.dv" flag="required">
<module-option name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory"/>
<module-option name="java.naming.provider.url" value="ldap://192.168.0.68/"/>
<module-option name="java.naming.security.authentication" value="simple"/>
<module-option name="java.naming.security.principal" value="CN=Administrator,CN=Users,DC=mydomain,DC=local"/>
<module-option name="java.naming.security.credentials" value="Password123"/>
<module-option name="bindDN" value="CN=Administrator,CN=Users,DC=mydomain,DC=local"/>
<module-option name="bindCredential" value="Password123"/>
<module-option name="baseCtxDN" value="DC=mydomain,DC=local"/>
<module-option name="baseFilter" value="(CN={0})"/>
<module-option name="rolesCtxDN" value="OU=dvroles,DC=mydomain,DC=local"/>
<module-option name="roleFilter" value="(member={1})"/>
<module-option name="roleAttributeIsDN" value="false"/>
<module-option name="roleAttributeID" value="cn"/>
<module-option name="roleRecursion" value="5"/>
<module-option name="searchFilterUsers" value="(memberof=cn=DataVirtuality,OU=Users,DC=mydomain,DC=local)"/>
<module-option name="searchFilterGroups" value="CN=DataVirtuality"/>
<module-option name="allowEmptyPasswords" value="false"/>
<module-option name="defaultAdminGroup" value="dv-admins"/>
<module-option name="displayUserName" value="cn"/>
</login-module>
</authentication>
</security-domain>
This code replaces the following one in dvserver-standalone.xml:
<security-domain name="dv-security" cache-type="default">
<authentication>
<login-module code="com.datavirtuality.dv.core.teiid.users.DVLoginModule" flag="required" module="com.datavirtuality.dv"/>
</authentication>
</security-domain>
The parameters should be configured as follows:
To view the full table, click the expand button in its top right corner
Configuration Parameters
Parameter | Description |
---|---|
java.naming.provider.url | Hostname or IP address of directory server. Can use |
java.naming.security.principal | User account that has permissions to read users and groups from the directory |
java.naming.security.credentials | Credentials of the user account above |
bindDN | Same as java.naming.security.principal . For technical reasons, the same credentials need to be supplied twice |
bindCredential | Same as java.naming.security.credentials |
baseCtxDN | The container where to start searching for roles |
baseFilter | When a user logs in, this filter locates the user inside the directory based on the provided username passed as {0} . Different types of filters are possible, such as using the CN ((cn={0}) ), userPrincipalName ((userPrincipalName={0}@mydomain.local) ), or sAMAccountName ((sAMAccountName={0}) ) |
searchFilterGroups | Optional filter to restrict the groups that the login module will retrieve. By default, all groups under rolesCtxDN are loaded as (&(&(objectClass=group))) . Specify a more restrictive filter if you want only a subset of groups to be loaded |
searchFilterUsers | Optional filter to restrict the users that the login module will retrieve. By default, all users under baseCtxDN are loaded as (&(&(objectClass=user))) . Specify a more restrictive filter if you want only a subset of users to be loaded |
rolesCtxDN | The container where to start searching for roles |
roleFilter | Filter to obtain groups a user belongs to. The users' userDN can be accessed as {1} |
roleAttributeIsDN | Set to FALSE if the user's role attribute doesn't contain the fully distinguished name of the role object |
roleAttributeID | If roleAttributeIsDN is FALSE , specifies the name of the role attribute which corresponds to the name of the role |
roleRecursion | The depth to search for a role in the given role context. Disabled if set to 0 |
allowEmptyPasswords | Set to FALSE if logins without a password should be rejected |
defaultAdminGroup | Name of the Active Directory role to be granted administrative rights on the CData Virtuality Server |
displayUserName | Defines LDAP attribute that is used as CData Virtuality "userName" . Defaults to "displayName" LDAP attribute value. Must be the same attribute as in "baseFilter" . "has" and "stripDomain" functions could be used in the value of this parameter. "displayUserName" could be skipped if all users have "displayName" LDAP attribute set and "baseFilter" has cn={0} or sAMAccountName={0} value and "displayName" for all users equals to "cn" or "sAMAccountName". Examples: Use <module-option name="displayUserName" value="has('userPrincipalName') ? stripDomain(userPrincipalName) : cn"/> when "baseFilter" is set to<module-option name="baseFilter" value="(userPrincipalName={0}@mydomain.local)"/> . "userName" is equal to "userPrincipalName" w ithout the domain part if the user has "userPrincipalName" attribute set and "cn" if not.Use |
displayUserName
parameter default value behaviour was changed in v4.2:
Previously:
- If
displayUserName
was not specified in the dvserver-standalone.xml file, the system defaulted to using the user'sdisplayName
LDAP attribute value asdisplayUserName
. IfdisplayName
was not available, thedistinguishedName
LDAP attribute value was used instead, as every LDAP user has adistinguishedName
; - Similarly, if
displayUserName
was specified but some LDAP users lacked the used LDAP attribute, the system defaulted to thedistinguishedName
LDAP attribute value.
Since v4.2:
- This update removes the automatic use of
distinguishedName
LDAP attribute in cases where the user does not have adisplayName
, or the attribute specified indisplayUserName
is missing. Now, an error will occur when reading CData Virtuality users ifdisplayUserName
is not specified in the config, or if it is set to an attribute that some users do not have (e.g.cn
). Therefore, it is crucial to specifydisplayUserName
accurately.
Сonnecting to the Active Directory server over the TLS protocol requires the following parameter:
<module-option name="java.naming.security.protocol" value="ssl"/>
Please note that the number of domain users visible in the CData Virtuality Studio is limited by the ADWS MaxGroupOrMemberEntries
setting and the Active Directory LDAP default MaxPageSize
setting. Please refer to the ADWS and LDAP documentation for more information.
Users and Roles (or Groups)
Users and Roles lists are loaded from Active Directory. Lists are loaded during the first call to the relevant tables after the server start or restart (SYSADMIN.Users
and SYSADMIN.Roles
) and cached on the server. New users are able to log in to CData Virtuality without refreshing the cache. However, to query new users and roles, the cache must be refreshed using the SYSADMIN.refreshLdapUserCache
procedure.
CALL "SYSADMIN.refreshLdapUserCache"();;
When the CData Virtuality Server is configured to read users, passwords, and roles from an LDAP domain, some features related to these objects can no longer be used in the CData Virtuality Server. In particular, handling users and roles from the CData Virtuality Server is impossible: they should be managed directly in LDAP.
For this reason, the following stored procedures are not available anymore:
SYSADMIN.addRole()
SYSADMIN.renameRole()
SYSADMIN.deleteRole()
SYSADMIN.importUser()
SYSADMIN.addUser()
SYSADMIN.renameUser()
SYSADMIN.deleteUser()
SYSADMIN.changeUserPwd()
SYSADMIN.getEncryptedUserPwd()
SYSADMIN.addUserRole()
SYSADMIN.deleteUserRole()
If they are called, this error message is shown:
UnsupportedLdapUsersHandlerOperationException. This operation is not supported when LDAP is used for the authentication.
Permissions
Permissions are handled and stored locally in dvconfig, and they can be set using the SYSADMIN.setPermissions()
system procedure as with the default dvconfig-based authentication mechanism.
Special Permission to Create Temporary Tables
With the default CData Virtuality Server mechanism, the special permission to create temporary tables is normally assigned to a role, and it is set when a new role is created via SYSADMIN.addRole()
.
Since the CData Virtuality Server with LDAP authentication mechanism cannot handle roles, a procedure to grant or revoke that permission is also provided.
SYSADMIN.setAllowCreateTempTables(IN role_name string NOT NULL, IN allow boolean NOT NULL)
This procedure can be used with both authentication mechanisms and will grant/deny the appropriate permission to the role.
The main difference (but this is only a technical detail) is that if a dvconfig-based authentication is used, the special permission is set to the role and stored in the dv_roles
table, as usual; if LDAP is configured for authenticating users, the special permission is stored in a new and appropriate table called dv_ldap_role_props
.
Adding a Role with Minimal Permissions
Perform the following steps to create a role with minimum permissions for connecting to the CData Virtuality Server. You might not want to have all users granted admin rights.
1. Create a new group in your directory. Ensure that it is within the search scope of the configuration of the login module.
2. If necessary, create a user that shall not be an administrator.
3. Assign the user to the group. Double-check (see the screenshot):
4. Connect to the CData Virtuality Server as an administrator and go to User Management in the Studio. Check that the Server has recognized the users, roles and assignments.
If not, look at the login module configuration and the directory structure. The screenshot depicts the case where everything was correctly read from the directory:
5. Assign the role a minimal set of permissions. You can use the following script (remember to replace custom-connect-role
with your role name):
BEGIN
DECLARE string roleName='custom-connect-role';
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN', "permissions" => 'R') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.getInstalledLicense', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.getServerVersion', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.getServerBuildInfo', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.getCurrentDWH', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.refreshDataSource', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.refreshAllDataSources', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.getDefaultOptionValue', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.getCurrentUser', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.getUserProperty', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.getDataCatalogAttribute', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.reservedwords', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.getCatalogs', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.getViewAndProcCreationOrder', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.refreshSchema', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.getDataLineage', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.setRemark', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.refreshAllSchemas', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSADMIN.md5', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'UTILS.getJobProperties', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'UTILS.getConnectorProperties', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'UTILS.pipes_helper__status', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'INFORMATION_SCHEMA', "permissions" => 'RE') ;
CALL SYSADMIN.setPermissions("role_name" => roleName, "resourceName" => 'SYSLOG', "permissions" => 'R') ;
END ;;
All users that belong to this group in your directory will now be able to connect.