/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.datatools.connectivity; import java.security.MessageDigest; import java.sql.Driver; import java.util.Properties; import org.apache.commons.codec.binary.Base64; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.datatools.connectivity.IConnectionProfile; import org.eclipse.datatools.connectivity.ProfileManager; import org.eclipse.datatools.connectivity.drivers.DriverInstance; import org.eclipse.datatools.connectivity.drivers.DriverManager; import org.eclipse.datatools.connectivity.drivers.IDriverMgmtConstants; import org.eclipse.datatools.connectivity.drivers.IPropertySet; import org.eclipse.datatools.connectivity.drivers.PropertySetImpl; import org.eclipse.datatools.connectivity.drivers.jdbc.IJDBCConnectionProfileConstants; import org.eclipse.datatools.connectivity.drivers.jdbc.IJDBCDriverDefinitionConstants; import org.teiid.datatools.connectivity.security.impl.EquinoxSecureStorageProvider; import org.teiid.datatools.connectivity.spi.ISecureStorageProvider; import org.teiid.designer.runtime.registry.TeiidRuntimeRegistry; import org.teiid.designer.runtime.version.spi.ITeiidServerVersion; import org.teiid.designer.runtime.version.spi.TeiidServerVersion; /** * * * @since 8.0 */ public class ConnectivityUtil { private static final String TEIID_PROFILE_PROVIDER_ID = "org.teiid.datatools.connectivity.connectionProfile"; //$NON-NLS-1$ private static final String DRIVER_DEFINITION_ID_KEY = "org.eclipse.datatools.connectivity.driverDefinitionID"; //$NON-NLS-1$ private static final String TEIID_DRIVER_NAME = "org.teiid.jdbc.TeiidDriver"; //$NON-NLS-1$ private static final String TEIID_DATABASE_VENDOR_NAME = "Teiid"; //$NON-NLS-1$ private static final String TEIID_DRIVER_ID_SKELETON = "DriverDefn.org.teiid.MAJOR.MINOR.driver.serverDriverTemplate.Teiid Server JDBC Driver MAJOR.MINOR Default"; //$NON-NLS-1$ /* Property keys for the ad-hoc teiid drivers created if no other suitable teiid driver can be found */ private static final String TEIID_ADHOC_DRIVER_ID_SKELETON = "DriverDefn.org.teiid.MAJOR.MINOR.driver.serverDriverTemplate.Ad-hoc Teiid Server JDBC Driver MAJOR.MINOR Default"; //$NON-NLS-1$ private static final String TEIID_ADHOC_DRIVER_DEFAULT_NAME = "Ad-hoc Teiid Server JDBC Driver"; //$NON-NLS-1$ private static final String TEIID_ADHOC_DRIVER_DEFN_TYPE = "org.teiid.runtime.client.driver.serverDriverTemplate"; //$NON-NLS-1$ /** * Base key for the secure storage node used for holding passwords */ public static final String SECURE_STORAGE_BASEKEY = Activator.PLUGIN_ID.replace('.', IPath.SEPARATOR); /** * Secure storage sub-node for the Admin password property */ public static final String ADMIN_PASSWORD = "admin_password"; //$NON-NLS-1$ /** * Secure storage sub-node for the JDBC password property */ public static final String JDBC_PASSWORD = "jdbc_password"; //$NON-NLS-1$ /** * Prefix for identifying a hash token */ public static final String TOKEN_PREFIX = "TOK##!"; //$NON-NLS-1$ /** * Tries to find the most appropriate driver definition id for the given teiid version. * <p> * Since teiid driver definition's are version specific, it attempts to find the driver * definition based upon the major and minor components of the version. * <p> * Failing that, it iterates through the existing driver definitions and attempts to * determine the most appropriate driver based upon the actual version of the * driver definition. * * @param serverVersion * @return driver definition id or null */ private static String getTeiidDriverDefinitionId(ITeiidServerVersion serverVersion) { String driverDefnId = TEIID_DRIVER_ID_SKELETON .replaceAll("MAJOR", serverVersion.getMajor()) //$NON-NLS-1$ .replaceAll("MINOR", serverVersion.getMinor()); //$NON-NLS-1$ DriverInstance driverInstance = DriverManager.getInstance().getDriverInstanceByID(driverDefnId); if (driverInstance != null && driverInstance.getTemplate() != null) return driverDefnId; // exact match!! /* Need to try harder since no teiid version directly matches a driver */ for (DriverInstance driver : DriverManager.getInstance().getAllDriverInstances()) { String driverClass = driver.getProperty(IJDBCDriverDefinitionConstants.DRIVER_CLASS_PROP_ID); if (! TEIID_DRIVER_NAME.equals(driverClass)) continue; if (driver.getTemplate() == null) // Invalid driver continue; String dbVersion = driver.getProperty(IJDBCDriverDefinitionConstants.DATABASE_VERSION_PROP_ID); ITeiidServerVersion driverVersion = new TeiidServerVersion(dbVersion); ITeiidServerVersion minDriverVersion = driverVersion.getMinimumVersion(); ITeiidServerVersion maxDriverVersion = driverVersion.getMaximumVersion(); if (serverVersion.isLessThan(minDriverVersion) || serverVersion.isGreaterThan(maxDriverVersion)) continue; return driver.getId(); } return null; } private static String createTeiidDriverInstance( ITeiidServerVersion serverVersion, String jarList, String driverURL, String username, String passToken ) { /* Create an ad-hoc version to avoid clashing ids with any built-in teiid runtime drivers */ String driverId = TEIID_ADHOC_DRIVER_ID_SKELETON .replaceAll("MAJOR", serverVersion.getMajor()) //$NON-NLS-1$ .replaceAll("MINOR", serverVersion.getMinor()); //$NON-NLS-1$ // Have we already created an ad-hoc driver for this server version? DriverInstance driverInstance = DriverManager.getInstance().getDriverInstanceByID(driverId); if (driverInstance != null && driverInstance.getTemplate() != null) return driverId; IPropertySet pset = new PropertySetImpl(TEIID_ADHOC_DRIVER_DEFAULT_NAME, driverId); Properties baseProperties = new Properties(); baseProperties.setProperty(IJDBCDriverDefinitionConstants.DRIVER_CLASS_PROP_ID, TEIID_DRIVER_NAME); if(null != driverURL) { baseProperties.setProperty(IJDBCDriverDefinitionConstants.URL_PROP_ID, driverURL); } if (null != username) { baseProperties.setProperty(IJDBCDriverDefinitionConstants.USERNAME_PROP_ID, username); } if (null != passToken) { baseProperties.setProperty(IJDBCDriverDefinitionConstants.PASSWORD_PROP_ID, passToken); } baseProperties.setProperty(IJDBCDriverDefinitionConstants.DATABASE_VENDOR_PROP_ID, TEIID_DATABASE_VENDOR_NAME); baseProperties.setProperty(IJDBCDriverDefinitionConstants.DATABASE_VERSION_PROP_ID, serverVersion.toString()); baseProperties.setProperty(IDriverMgmtConstants.PROP_DEFN_JARLIST, jarList); baseProperties.setProperty(IDriverMgmtConstants.PROP_DEFN_TYPE, TEIID_ADHOC_DRIVER_DEFN_TYPE); baseProperties.setProperty(IDriverMgmtConstants.PROP_DEFN_CLASS, TEIID_DRIVER_NAME); pset.setBaseProperties(baseProperties); DriverManager.getInstance().addDriverInstance(pset); return driverId; } /** * Store the given password against the url using the built-in secure storage * * @param url * @param password */ private static void storeJDBCPassword(String url, String passToken, String password) { if (password == null) return; String urlStorageKey; if (passToken == null) urlStorageKey = ConnectivityUtil.buildSecureStorageKey(TeiidJDBCConnection.class, url); else urlStorageKey = ConnectivityUtil.buildSecureStorageKey(TeiidJDBCConnection.class, url, passToken); try { getSecureStorageProvider().storeInSecureStorage(urlStorageKey, ConnectivityUtil.JDBC_PASSWORD, password); } catch (Exception ex) { Activator.log(ex); } } private static String acquireDriverDefinition(ITeiidServerVersion serverVersion, String driverPath, String connectionURL, String username, String passToken, String password) throws Exception { String driverDefinitionId = getTeiidDriverDefinitionId(serverVersion); DriverInstance mDriver = DriverManager.getInstance().getDriverInstanceByID(driverDefinitionId); if (mDriver == null) { driverDefinitionId = createTeiidDriverInstance(serverVersion, driverPath, connectionURL, username, passToken); } else { // JBIDE-7493 Eclipse updates can break profiles because the driverPath is plugin version specific. String jarList = mDriver.getJarList(); if(jarList != driverPath) { mDriver.getPropertySet().getBaseProperties().put(IDriverMgmtConstants.PROP_DEFN_JARLIST, driverPath); } if (connectionURL != null) mDriver.getPropertySet().getBaseProperties().put(IJDBCDriverDefinitionConstants.URL_PROP_ID, connectionURL); mDriver.getPropertySet().getBaseProperties().put(IJDBCDriverDefinitionConstants.PASSWORD_PROP_ID, passToken); } if (driverDefinitionId == null) throw new Exception(Messages.getString(Messages.ConnectivityUtil.noTeiidDriverDefinitionFound, serverVersion)); storeJDBCPassword(connectionURL, passToken, password); return driverDefinitionId; } private static Properties createDriverProps(ITeiidServerVersion serverVersion, String driverId, String jarList, String driverURL, String username, String passToken, String vdbName ) { Properties baseProperties = new Properties(); baseProperties.setProperty(IDriverMgmtConstants.PROP_DEFN_JARLIST, jarList); baseProperties.setProperty(IJDBCDriverDefinitionConstants.DRIVER_CLASS_PROP_ID, TEIID_DRIVER_NAME); if (driverURL != null) baseProperties.setProperty(IJDBCDriverDefinitionConstants.URL_PROP_ID, driverURL); if(null != username) { baseProperties.setProperty(IJDBCDriverDefinitionConstants.USERNAME_PROP_ID, username); } if (null != passToken) { baseProperties.setProperty(IJDBCDriverDefinitionConstants.PASSWORD_PROP_ID, passToken); } baseProperties.setProperty(IJDBCDriverDefinitionConstants.DATABASE_VENDOR_PROP_ID, TEIID_DATABASE_VENDOR_NAME); baseProperties.setProperty(IJDBCDriverDefinitionConstants.DATABASE_VERSION_PROP_ID, serverVersion.toString()); baseProperties.setProperty(IJDBCDriverDefinitionConstants.DATABASE_NAME_PROP_ID, vdbName); baseProperties.setProperty(IJDBCConnectionProfileConstants.SAVE_PASSWORD_PROP_ID, String.valueOf(true)); // baseProperties.setProperty(IDriverMgmtConstants.PROP_DEFN_JARLIST, // jarList); baseProperties.setProperty(DRIVER_DEFINITION_ID_KEY, driverId); return baseProperties; } /** * @param serverVersion * @param driverPath * @param connectionURL * @param username * @param password * @param vdbName * @return transient Teiid connection profile * @throws CoreException */ public static IConnectionProfile createTransientTeiidProfile( ITeiidServerVersion serverVersion, String driverPath, String connectionURL, String username, String password, String vdbName ) throws CoreException { ProfileManager pm = ProfileManager.getInstance(); try { String passToken = ConnectivityUtil.generateHashToken(connectionURL, password); String driverDefinitionId = acquireDriverDefinition(serverVersion, driverPath, connectionURL, username, passToken, password); return pm.createTransientProfile(TEIID_PROFILE_PROVIDER_ID, createDriverProps(serverVersion, driverDefinitionId, driverPath, connectionURL, username, passToken, vdbName)); } catch (Exception e) { Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, Messages.getString(Messages.ConnectivityUtil.errorGettingProfile), e); throw new CoreException(status); } } /** * @param profile */ public static void deleteTransientTeiidProfile(IConnectionProfile profile) { ProfileManager.getInstance().deleteTransientProfile(profile); } /** * @param serverVersion * @param driverPath * @param connectionURL * @param username * @param password * @param vdbName * @param profileName * @return property set for the given VDB properties * @throws CoreException */ public static Properties createVDBTeiidProfileProperties(ITeiidServerVersion serverVersion, String driverPath, String connectionURL, String username, String password, String vdbName, String profileName ) throws CoreException { String driverDefinitionId; try { String passToken = ConnectivityUtil.generateHashToken(connectionURL, password); driverDefinitionId = acquireDriverDefinition(serverVersion, driverPath, connectionURL, username, passToken, password); return createDriverProps(serverVersion, driverDefinitionId, driverPath, connectionURL, username, passToken, vdbName); } catch (Exception ex) { Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, Messages.getString(Messages.ConnectivityUtil.errorGettingProfileProperties), ex); throw new CoreException(status); } } /** * @param serverVersion * @param driverPath * @param connectionURL * @param username * @param password * @param vdbName * @param profileName * @return connection profile for the given VDB properties * @throws CoreException */ public static IConnectionProfile createVDBTeiidProfile( ITeiidServerVersion serverVersion, String driverPath, String connectionURL, String username, String password, String vdbName, String profileName) throws CoreException { ProfileManager pm = ProfileManager.getInstance(); try { String passToken = ConnectivityUtil.generateHashToken(connectionURL, password); String driverDefinitionId = acquireDriverDefinition(serverVersion, driverPath, connectionURL, username, passToken, password); IConnectionProfile existingCP = pm.getProfileByName(profileName); if (existingCP != null) { return existingCP; } return pm.createProfile(profileName, "", //$NON-NLS-1$ TEIID_PROFILE_PROVIDER_ID, createDriverProps(serverVersion, driverDefinitionId, driverPath, connectionURL, username, passToken, vdbName)); } catch (Exception e) { Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, Messages.getString(Messages.ConnectivityUtil.errorGettingProfile), e); throw new CoreException(status); } } /** * Assemble the secure storage node key based * upon the given class and the set of arguments. * * @param klazz * @param args * * @return fully qualified key used in secure storage */ public static String buildSecureStorageKey(Class<?> klazz, String... args) { StringBuilder secureKeyBuilder = new StringBuilder(SECURE_STORAGE_BASEKEY) .append(IPath.SEPARATOR) .append(klazz.getSimpleName()) .append(IPath.SEPARATOR); if (args != null) { for (String arg : args) { if (arg == null) continue; secureKeyBuilder.append(arg).append(IPath.SEPARATOR); } } return secureKeyBuilder.toString(); } /** * @param args * @return the hash of the given string * @throws Exception */ public static String generateHashToken(String... args) throws Exception { StringBuilder builder = new StringBuilder(); for (String arg : args) { builder.append(arg); builder.append(IPath.SEPARATOR); } MessageDigest sha = MessageDigest.getInstance("SHA-512"); //$NON-NLS-1$ sha.update(builder.toString().getBytes("UTF-8")); //$NON-NLS-1$ byte[] hashedByteArray = sha.digest(); // Use Base64 encoding here --> return TOKEN_PREFIX + Base64.encodeBase64URLSafeString(hashedByteArray); } /** * @param arg * @return true if this given arg is a generated hash token */ public static boolean isPasswordToken(String arg) { return arg != null && arg.startsWith(TOKEN_PREFIX); } /** * Get the Eclipse implementation of the {@link ISecureStorageProvider} interface * * @return implementation of secure storage provider */ public static ISecureStorageProvider getSecureStorageProvider() { return EquinoxSecureStorageProvider.getInstance(); } /** * Find a Teiid {@link Driver} for the given server version. * * The driver class should be provided as a check to ensure the class name * is as expected. * * @param teiidServerVersion * @param driverClass * * @return the Teiid {@link Driver} * @throws Exception */ public static Driver getTeiidDriver(ITeiidServerVersion teiidServerVersion, String driverClass) throws Exception { Driver driver = TeiidRuntimeRegistry.getInstance().getTeiidDriver(teiidServerVersion); if (driver != null && driver.getClass().getName().equals(driverClass)) return driver; String msg = Messages.getString(Messages.ConnectivityUtil.noTeiidDriverFound, driverClass, teiidServerVersion); throw new IllegalStateException(msg); } }