Skip to main content
Skip table of contents

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:

SQL
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:

CODE
{
	"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:

CODE
<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:

CODE
<custom-realm class-name="com.datavirtuality.dv.core.teiid.users.DVLoginModule" module="com.datavirtuality.dv" name="DataVirtualityRealm"/>

with a snippet like this:

CODE
<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

CODE
<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:

CODE
<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:

CODE
<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:

CODE
<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:

SQL
SELECT UTILS.encrypt('password');;

2. Set encoded values for java.naming.security.credentials and bindCredential:

SQL
<module-option name="java.naming.security.credentials" value="ZjyMp28QJE0D47Rld0LOFw=="/>
<module-option name="bindCredential" value="ZjyMp28QJE0D47Rld0LOFw=="/>

3. Set dv.encrypted.credentials to TRUE:

SQL
<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:

BASH
<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:

BASH
<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

ParameterDescription
java.naming.provider.url

Hostname or IP address of directory server. Can use ldaps://  instead of ldap:// for secure connections

java.naming.security.principalUser account that has permissions to read users and groups from the directory
java.naming.security.credentialsCredentials of the user account above
bindDNSame as java.naming.security.principal. For technical reasons, the same credentials need to be supplied twice
bindCredentialSame as java.naming.security.credentials
baseCtxDNThe container where to start searching for roles
baseFilterWhen 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}))
searchFilterGroupsOptional 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
searchFilterUsersOptional 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
rolesCtxDNThe container where to start searching for roles
roleFilterFilter to obtain groups a user belongs to. The users' userDN can be accessed as {1}
roleAttributeIsDNSet 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
roleRecursionThe depth to search for a role in the given role context. Disabled if set to 0
allowEmptyPasswordsSet to FALSE if logins without a password should be rejected
defaultAdminGroupName of the Active Directory role to be granted administrative rights on the CData Virtuality Server
displayUserNameDefines 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" without the domain part if the user has "userPrincipalName" attribute set and "cn" if not.

Use <module-option name="displayUserName" value="cn"/> if "baseFilter" is <module-option name="baseFilter" value="CN={0}"/>. "userName" is "cn" in this case.

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's displayName LDAP attribute value as displayUserName. If displayName was not available, the distinguishedName LDAP attribute value was used instead, as every LDAP user has a distinguishedName;
  • Similarly, if displayUserName was specified but some LDAP users lacked the used LDAP attribute, the system defaulted to the distinguishedName LDAP attribute value.

Since v4.2:

  • This update removes the automatic use of distinguishedName LDAP attribute in cases where the user does not have a displayName, or the attribute specified in displayUserName is missing. Now, an error will occur when reading CData Virtuality users if displayUserName 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 specify displayUserName accurately.

Сonnecting to the Active Directory server over the TLS protocol requires the following parameter:

BASH
<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.

SQL
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.

SQL
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):

SQL
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.


JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.