diff --git a/rse/plugins/org.eclipse.rse.core/src/org/eclipse/rse/core/PasswordPersistenceManager.java b/rse/plugins/org.eclipse.rse.core/src/org/eclipse/rse/core/PasswordPersistenceManager.java index 589dc860468..47801c72720 100644 --- a/rse/plugins/org.eclipse.rse.core/src/org/eclipse/rse/core/PasswordPersistenceManager.java +++ b/rse/plugins/org.eclipse.rse.core/src/org/eclipse/rse/core/PasswordPersistenceManager.java @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2002, 2008 IBM Corporation and others. All rights reserved. + * Copyright (c) 2002, 2012 IBM Corporation and others. All rights reserved. * This program and the accompanying materials are made available under the terms * of the Eclipse Public License v1.0 which accompanies this distribution, and is * available at http://www.eclipse.org/legal/epl-v10.html @@ -19,6 +19,7 @@ * Martin Oberhuber (Wind River) - [218655][api] Provide SystemType enablement info in non-UI * Martin Oberhuber (Wind River) - [cleanup] Add API "since" Javadoc tags * David Dykstal (IBM) - [210474] Deny save password function missing + * David Dykstal (IBM) - [225320] Use equinox secure storage for passwords ********************************************************************************/ package org.eclipse.rse.core; @@ -29,6 +30,7 @@ import java.net.URL; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -44,14 +46,62 @@ import org.eclipse.rse.internal.core.RSECoreMessages; import org.osgi.framework.Bundle; /** - * PasswordPersistenceManager manages the saving and retrieving of user ID / - * passwords to the Eclipse keyring for registered system types. + * PasswordPersistenceManager manages the saving and retrieving of user IDs / + * passwords to Equinox secure storage for registered system types. * * @noextend This class is not intended to be subclassed by clients. * @noinstantiate This class is not intended to be instantiated by clients. Use * the {@link #getInstance()} method to get the singleton * instance. */ + +/* + * Passwords are stored in a node that is selected by system type id. + * Each password has a key that consists of a host name and a user id pair. + * The key is a string and looks like //. + * Host names may be symbolic or they be IPv4 or IPv6 addresses. + * The current design treats these the same. + * + * In addition to the registered system types, there is a "default" system type + * that can be searched if a password is not found for a registered system type. + * The API allows for setting this default along with a particular system type. + * + * The current implementation uses Equinox secure storage. The + * API for this looks like that for a preferences store. This is arranged as a tree + * of nodes, each node holding a set of keyed values. + * In the case of this password store value reside only at the lowest + * nodes in the tree. There is one node for each system type, which is then used to + * store passwords for a particular host name, user ID pair. + * + * This is similar to the previous implementation which used the Eclipse platform key ring. + * A particular key ring node was selected using the system type id and a Map object + * was retrieved or stored at this node. The map was keyed by the same host name, user ID + * pair that is used in the current implementation. + * + * Migration from the old implementation to the new one is done when accessing a node for + * the first time. At that point the map entries present in the old implementation are copied + * to the preferences node in the new implementation. + * + * The old key ring values can be migrated only if they can be accessed using the + * compatibility API found in the bundle org.eclipse.core.runtime.compatability.auth. + * This can be installed by the user, but is not included in the standard + * packaging for Eclipse 4.2 and subsequent releases. + * + * The nodes in the old implementation were rooted in the URL file://rse where + * was the name of the java user.name system property. Note that this user.name property may be, + * and probably is, different that the user ID used on a target system. The new secure preferences are rooted + * in the default secure preferences node which is kept in a location associated with + * the current user. Thus, there is no need to additionally qualify the secure + * storage location with the user.name system property. + * + * Lookup is based on an exact match followed by a fuzzy match on host names. An exact match + * uses the host name argument as is and is case sensitive when matching the host name of the + * stored keys. If not found, then a fuzzy match is employed that allows for case insensitivity. + * If one host name is a prefix of the other and they both resolve to the name network entity they + * are considered to be matching. Network name resolution is very expensive and is employed only if their + * is enough extra similar information to justify a match. Thus, for example, hobbiton could + * match HOBBITON.EXAMPLE.COM, but could never match an IP address. + */ public class PasswordPersistenceManager { /** @@ -123,11 +173,11 @@ public class PasswordPersistenceManager { * the API. * @return true if the API is installed. */ - private static boolean isAuthorizationCompatabilityInstalled() { + private static boolean isAuthorizationCompatibilityInstalled() { boolean result = false; - Bundle authorizationBundle = Platform.getBundle("org.eclipse.core.runtime.compatability.auth"); //$NON-NLS-1$ + Bundle authorizationBundle = Platform.getBundle("org.eclipse.core.runtime.compatibility.auth"); //$NON-NLS-1$ if (authorizationBundle == null) { - IStatus status = new Status(IStatus.INFO, RSECorePlugin.PLUGIN_ID, "Saved passwords are not available for migration to secure storage. Deprecated authorization classes (org.eclipse.core.runtime.compatability.auth) are not installed."); //$NON-NLS-1$ + IStatus status = new Status(IStatus.INFO, RSECorePlugin.PLUGIN_ID, "Saved passwords are not available for migration to secure storage. Deprecated authorization classes (org.eclipse.core.runtime.compatibility.auth) are not installed."); //$NON-NLS-1$ RSECorePlugin.getDefault().getLog().log(status); } else { result = true; @@ -198,16 +248,16 @@ public class PasswordPersistenceManager { String keyUserId = getUserIdFromKey(key); boolean match = (userId == null || (respectCase ? userId.equals(keyUserId) : userId.equalsIgnoreCase(keyUserId))); if (match) { - if (fuzzy) { - if (hostName.startsWith(keyHostName) || keyHostName.startsWith(hostName)) { - String khn = RSECorePlugin.getQualifiedHostName(keyHostName); - String phn = RSECorePlugin.getQualifiedHostName(hostName); + match = hostName.equals(keyHostName); + if (!match && fuzzy) { + String phn = hostName.toUpperCase(Locale.US); + String khn = keyHostName.toUpperCase(Locale.US); + match = phn.equals(khn); + if (!match && (phn.startsWith(khn) || khn.startsWith(phn))) { + khn = RSECorePlugin.getQualifiedHostName(khn); + phn = RSECorePlugin.getQualifiedHostName(phn); match = khn.equalsIgnoreCase(phn); - } else { - match = false; } - } else { - match = hostName.equals(keyHostName); } } if (match) { @@ -261,7 +311,7 @@ public class PasswordPersistenceManager { if (userName == null) { userName = DEFAULT_USER_NAME; } - if (isAuthorizationCompatabilityInstalled()) { + if (isAuthorizationCompatibilityInstalled()) { mapLocation = SERVER_URL + userName; } } @@ -300,7 +350,7 @@ public class PasswordPersistenceManager { } } } catch (MalformedURLException e) { - RSECorePlugin.getDefault().getLogger().logError("PasswordPersistenceManager.getPasswordMap", e); //$NON-NLS-1$ + RSECorePlugin.getDefault().getLogger().logError("PasswordPersistenceManager.getMap", e); //$NON-NLS-1$ } } return passwordMap; @@ -480,6 +530,21 @@ public class PasswordPersistenceManager { basicSave(passwords); } + /** + * Resets a given system type. This clears the storage for this system + * type and allows it to be re-migrated. + * This is not API. + * @noreference This method is not intended to be referenced by clients. + * @param systemType the system type to reset + * @since org.eclipse.rse.core 3.4 + */ + public void reset(IRSESystemType systemType) { + ISecurePreferences systemTypeNode = getNode(systemType); + if (systemTypeNode != null) { + systemTypeNode.removeNode(); + } + } + /** * Add a password to the password database. * This will not update the entry for the default system type @@ -583,19 +648,19 @@ public class PasswordPersistenceManager { */ public SystemSignonInformation find(IRSESystemType systemType, String hostName, String userId, boolean checkDefault) { SystemSignonInformation result = null; - if (userId != null && !isUserIDCaseSensitive(systemType)) { - userId = userId.toUpperCase(); - } - if (result == null) { + if (!(systemType == null || hostName == null || userId == null)) { + if (!isUserIDCaseSensitive(systemType)) { + userId = userId.toUpperCase(); + } String password = findPassword(systemType, hostName, userId); if (password != null) { result = new SystemSignonInformation(hostName, userId, password, systemType); } - } - if (result == null && checkDefault && !systemType.equals(DEFAULT_SYSTEM_TYPE)) { - String password = findPassword(DEFAULT_SYSTEM_TYPE, hostName, userId); - if (password != null) { - result = new SystemSignonInformation(hostName, userId, password, DEFAULT_SYSTEM_TYPE); + if (result == null && checkDefault && !systemType.equals(DEFAULT_SYSTEM_TYPE)) { + password = findPassword(DEFAULT_SYSTEM_TYPE, hostName, userId); + if (password != null) { + result = new SystemSignonInformation(hostName, userId, password, DEFAULT_SYSTEM_TYPE); + } } } return result; @@ -608,7 +673,7 @@ public class PasswordPersistenceManager { public void remove(SystemSignonInformation info) { remove(info.getSystemType(), info.getHostname(), info.getUserId()); } - + /** * Removes all passwords for a host name for a given system type. * This does not remove entries for the default system type. diff --git a/rse/tests/org.eclipse.rse.tests/src/org/eclipse/rse/tests/core/passwords/OriginalPasswordPersistenceManager.java b/rse/tests/org.eclipse.rse.tests/src/org/eclipse/rse/tests/core/passwords/OriginalPasswordPersistenceManager.java new file mode 100644 index 00000000000..9202c13fc1b --- /dev/null +++ b/rse/tests/org.eclipse.rse.tests/src/org/eclipse/rse/tests/core/passwords/OriginalPasswordPersistenceManager.java @@ -0,0 +1,675 @@ +/* + * This class copied from org.eclipse.rse.core to use to populate the old password store for + * migration testing. No source changes have been made other than those + * required to compile in a different package. Class was also renamed to prevent + * confusion in the testcase. + */ + +/******************************************************************************** + * Copyright (c) 2002, 2008 IBM Corporation and others. All rights reserved. + * This program and the accompanying materials are made available under the terms + * of the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Initial Contributors: + * The following IBM employees contributed to the Remote System Explorer + * component that contains this file: David McKnight, Kushal Munir, + * Michael Berger, David Dykstal, Phil Coulthard, Don Yantzi, Eric Simpson, + * Emily Bruner, Mazen Faraj, Adrian Storisteanu, Li Ding, and Kent Hawley. + * + * Contributors: + * David Dykstal (IBM) - moved from core package in the UI plugin + * - updated to use new RSEPreferencesManager + * Martin Oberhuber (Wind River) - [184095] Replace systemTypeName by IRSESystemType + * Martin Oberhuber (Wind River) - [177523] Unify singleton getter methods + * Martin Oberhuber (Wind River) - [186640] Add IRSESystemType.testProperty() + * Martin Oberhuber (Wind River) - [218655][api] Provide SystemType enablement info in non-UI + * Martin Oberhuber (Wind River) - [cleanup] Add API "since" Javadoc tags + * David Dykstal (IBM) - [210474] Deny save password function missing + ********************************************************************************/ + +package org.eclipse.rse.tests.core.passwords; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.rse.core.AbstractRSESystemType; +import org.eclipse.rse.core.IRSESystemType; +import org.eclipse.rse.core.RSECorePlugin; +import org.eclipse.rse.core.RSEPreferencesManager; +import org.eclipse.rse.core.model.SystemSignonInformation; +import org.eclipse.rse.internal.core.RSECoreMessages; +import org.osgi.framework.Bundle; + + +/** + * PasswordPersistenceManager manages the saving and retrieving of user ID / + * passwords to the Eclipse keyring for registered system types. + * + * @noextend This class is not intended to be subclassed by clients. + * @noinstantiate This class is not intended to be instantiated by clients. Use + * the {@link #getInstance()} method to get the singleton + * instance. + */ +public class OriginalPasswordPersistenceManager { + + // Keys used for using the Platform authorization methods + // The server url is generic so we can lookup all registered user IDs / passwords + // to display to the user in the password information preference page + private static final String SERVER_URL = "file://rse"; //$NON-NLS-1$ + + private static final String AUTH_SCHEME = ""; // no authorization scheme specified for apis //$NON-NLS-1$ + + // Add return codes + public static final int RC_OK = 0; + public static final int RC_ALREADY_EXISTS = 1; + /** @since org.eclipse.rse.core 3.0 */ + public static final int RC_DENIED = 2; + public static final int RC_ERROR = -1; + + // Default System Type, on a lookup if the specified system type and hostname is not found + // then the call will automatically lookup the default system type and hostname + public static final IRSESystemType DEFAULT_SYSTEM_TYPE = new DefaultSystemType(); + + // Default user name + public static final String DEFAULT_USER_NAME = "DEFAULT_USER"; //$NON-NLS-1$ + + // New URL to store password map + private String newURL = null; + + /* + * Singleton instance + */ + private static OriginalPasswordPersistenceManager _instance; + + /* + * Instance variables + */ + private RegisteredSystemType[] systemTypes; + + /** + * Default System Type + */ + private static class DefaultSystemType extends AbstractRSESystemType implements IRSESystemType + { + private static final String DEFAULT_ID = "DEFAULT"; //$NON-NLS-1$ + private DefaultSystemType() { + super(DEFAULT_ID, DEFAULT_ID, RSECoreMessages.DefaultSystemType_Label, null, null); + } + public String getId() { + //TODO consider a space character at the beginning to ensure uniqueness + return DEFAULT_ID; + } + public String[] getSubsystemConfigurationIds() { + return null; + } + public Object getAdapter(Class adapter) { + return null; + } + public boolean isEnabled() { + return true; + } + } + + /** + * Inner class used for storing registered system types + */ + private class RegisteredSystemType + { + private IRSESystemType _systemType; + private boolean _userIDCaseSensitive; + + protected RegisteredSystemType(IRSESystemType systemType, boolean caseSensitive) + { + _systemType = systemType; + _userIDCaseSensitive = caseSensitive; + } + + /** + * Returns the system type. + * @return the system type. + */ + public IRSESystemType getSystemType() { + return _systemType; + } + + /** + * Returns whether the user ID is case sensitive. + * @return true if the user ID is case sensitive, false otherwise. + */ + public boolean isUserIDCaseSensitive() { + return _userIDCaseSensitive; + } + } + + /** + * Singleton so private constructor + */ + private OriginalPasswordPersistenceManager(){ + String userName = System.getProperty("user.name"); //$NON-NLS-1$ + + if (userName == null) { + userName = DEFAULT_USER_NAME; + } + + newURL = SERVER_URL + userName; + } + + /** + * Tests the existence of the Eclipse authorization API by looking for installation of the bundle containing the API. + * This method was added for testing purposes. + * @return true if the API is installed. + * @since org.eclipse.rse.core 3.4 + */ + public static boolean isActive() { + Bundle authorizationBundle = Platform.getBundle("org.eclipse.core.runtime.compatibility.auth"); //$NON-NLS-1$ + boolean result = (authorizationBundle != null); + return result; + } + + /** + * Retrieve the singleton instance of the PasswordPersistenceManger + */ + public static final synchronized OriginalPasswordPersistenceManager getInstance() + { + if (_instance == null) + { + _instance = new OriginalPasswordPersistenceManager(); + _instance.initExtensions(); + } + return _instance; + } + + /* + * initialization - register system types + */ + private void initExtensions() + { + IRSESystemType[] sysTypes = RSECorePlugin.getTheCoreRegistry().getSystemTypes(); + systemTypes = new RegisteredSystemType[sysTypes.length]; + + for (int i = 0; i < sysTypes.length; i++) { + systemTypes[i] = new RegisteredSystemType(sysTypes[i], true); + } + } + + /** + * Remove the entry from the keyring that matches the systemtype, hostname and + * user ID from the SystemSignonInfo parameter. + */ + public void remove(SystemSignonInformation info) + { + remove(info.getSystemType(), info.getHostname(), info.getUserId()); + } + + /** + * Removes all passwords for a host name for a given system type. Use the + * default system type explicitly to remove those entries. + * + * @param systemType The system type of the host + * @param hostName The IP address of the host in canonical format + * @return the number of passwords removed from the keyring + * @since org.eclipse.rse.core 3.0 + */ + public int remove(IRSESystemType systemType, String hostName) { + Map passwords = getPasswordMap(systemType); + int numberRemoved = 0; + if (passwords != null) { + String hostPrefix = hostName + "//"; //$NON-NLS-1$ + Set keys = passwords.keySet(); + for (Iterator z = keys.iterator(); z.hasNext();) { + String key = (String) z.next(); + if (key.startsWith(hostPrefix)) { + z.remove(); // safely removes the key and the entry from the map + numberRemoved++; + } + } + if (numberRemoved > 0) { + savePasswordMap(systemType.getId(), passwords); + } + } + return numberRemoved; + } + + /** + * Removes all entries from the keyring that match the hostname, userid, and system type. + * Use the default system type explicitly to remove those entries. + * @param systemType the systemType + * @param hostName the connection name + * @param userid the user id + */ + public void remove(IRSESystemType systemType, String hostName, String userid) { + String hostname = hostName;//RSEUIPlugin.getQualifiedHostName(hname); + // Convert userid to upper case if required + if (!isUserIDCaseSensitive(systemType)) { + userid = userid.toUpperCase(); + } + Map passwords = getPasswordMap(systemType); + if (passwords != null) { + if (removePassword(passwords, hostname, userid)) { + savePasswordMap(systemType.getId(), passwords); + } + } + } + + /** + * Check if a password entry exists for the specified system type, hostname + * and userid. + */ + public boolean passwordExists(IRSESystemType systemtype, String hostname, String userid) + { + + return passwordExists(systemtype, hostname, userid, true); + } + + /** + * Check if a password entry exists for the specified system type, hostname + * and userid. + * + * @param systemtype The system type to check for. + * @param hname The hostname to check for. + * @param userid The user ID to check for. + * @param checkDefault Whether or not to check for a default system type if the specified system type is not found. + */ + public boolean passwordExists(IRSESystemType systemtype, String hname, String userid, boolean checkDefault) + { + String hostname = hname;//RSEUIPlugin.getQualifiedHostName(hname); + return (find(systemtype, hostname, userid) != null); + } + + /** + * Add a password to the password database. + * This will not update the entry for the default system type + * @param info The signon information to store + * @param overwrite Whether to overwrite any existing entry + * @return + * RC_OK if the password was successfully stored + * RC_ALREADY_EXISTS if the password already exists and overwrite was false + */ + public int add(SystemSignonInformation info, boolean overwrite) { + return add(info, overwrite, false); + } + + /** + * Add a password to the password database. + * @param info The signon information to store + * @param overwrite If true then overwrite the existing entry for this systemtype, hostname, and userid. + * @param updateDefault if true then set the entry for the default systemtype, hostname, and user ID, according to the overwrite setting. + * @return + * RC_OK if the password was successfully stored. + * RC_ALREADY_EXISTS if the password already exists and overwrite was false + * RC_DENIED if passwords may not be saved for this system type and host + */ + public int add(SystemSignonInformation info, boolean overwrite, boolean updateDefault) { + int result = RC_OK; + IRSESystemType systemType = info.getSystemType(); + String hostName = info.getHostname(); + String userId = info.getUserId(); + String newPassword = info.getPassword(); + boolean deny = RSEPreferencesManager.getDenyPasswordSave(systemType, hostName); + if (!deny) { + if (!isUserIDCaseSensitive(systemType)) { + userId = userId.toUpperCase(); + info.setUserId(userId); + } + if (updateDefault) { + if (systemType != DEFAULT_SYSTEM_TYPE) { + SystemSignonInformation newInfo = new SystemSignonInformation(hostName, userId, newPassword, DEFAULT_SYSTEM_TYPE); + result = add(newInfo, overwrite, false); + } + } + Map passwords = getPasswordMap(systemType); + if (passwords == null) { + passwords = new HashMap(5); + } + String oldPassword = getPassword(passwords, hostName, userId); + if (oldPassword != null) { + if (overwrite) { + removePassword(passwords, hostName, userId); + } else { + result = RC_ALREADY_EXISTS; + } + } + if (result == RC_OK) { + String passwordKey = getPasswordKey(hostName, userId); + passwords.put(passwordKey, newPassword); + savePasswordMap(systemType.getId(), passwords); + } + } else { + result = RC_DENIED; + } + return result; + } + + /* + * Retrieve the password map from the keyring for the specified system type + */ + private Map getPasswordMap(IRSESystemType systemType) + { + Map passwords = null; + String systemTypeId = systemType.getId(); + + try + { + URL serverURL = new URL(newURL); + passwords = Platform.getAuthorizationInfo(serverURL, systemTypeId, AUTH_SCHEME); + + // if no passwords found with new URL, check old URL + if (passwords == null) { + + URL oldServerURL1 = new URL(SERVER_URL + ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString()); + passwords = Platform.getAuthorizationInfo(oldServerURL1, systemTypeId, AUTH_SCHEME); + + // passwords found, so migrate to using new URL + if (passwords != null) { + savePasswordMap(systemTypeId, passwords); + } + // if still no passwords found, check with even older URL + else { + URL oldServerURL2 = new URL(SERVER_URL); + passwords = Platform.getAuthorizationInfo(oldServerURL2, systemTypeId, AUTH_SCHEME); + + // passwords found, so migrate to using new URL + if (passwords != null) { + savePasswordMap(systemTypeId, passwords); + } + } + } + } + catch (MalformedURLException e) { + RSECorePlugin.getDefault().getLogger().logError("PasswordPersistenceManager.getPasswordMap", e); //$NON-NLS-1$ + } + + return passwords; + } + + /* + * Retrieve the password map from the keyring for the specified system type + */ + private void savePasswordMap(String systemTypeId, Map passwords) + { + try + { + URL serverURL = new URL(newURL); + Platform.flushAuthorizationInfo(serverURL, systemTypeId, AUTH_SCHEME); + Platform.addAuthorizationInfo(serverURL, systemTypeId, AUTH_SCHEME, passwords); + } + catch (MalformedURLException e) { + RSECorePlugin.getDefault().getLogger().logError("PasswordPersistenceManager.savePasswordMap", e); //$NON-NLS-1$ + } + catch (CoreException e) { + RSECorePlugin.getDefault().getLogger().logError("PasswordPersistenceManager.savePasswordMap", e); //$NON-NLS-1$ + } + } + + /** + * Find the password for the specified systemtype, hostname and userid. + * If one is not found then the default system type is used. + * The system type in the signon information returned may not be the same as the system type + * specfied in the argument. + */ + public SystemSignonInformation find(IRSESystemType systemtype, String hostname, String userid) + { + return find(systemtype, hostname, userid, true); + } + + + private boolean removePassword(Map passwords, String hostname, String userid) + { + boolean removed = false; + String password = null; + + String passwordKey = getPasswordKey(hostname, userid); + password =(String) passwords.get(passwordKey); + if (password != null) + { + passwords.remove(passwordKey); + removed = true; + } + else + { + String phostname = hostname.toUpperCase(); + + // DKM - fallback for different case uids, hostnames or qualified/unqualified hostnames + Iterator keys = passwords.keySet().iterator(); + while (keys.hasNext() && password == null) + { + String key = (String)keys.next(); + if (key.equalsIgnoreCase(passwordKey)) + { + password = (String) passwords.get(key); + } + else + { + String khostname = getHostnameFromPasswordKey(key).toUpperCase(); + String kuid = getUserIdFromPasswordKey(key); + if (kuid.equalsIgnoreCase(userid)) + { + // uid matches, check if hosts are the same + if (khostname.startsWith(phostname) || phostname.startsWith(khostname)) + { + String qkhost = RSECorePlugin.getQualifiedHostName(khostname); + String qphost = RSECorePlugin.getQualifiedHostName(phostname); + if (qkhost.equals(qphost)) + { + password = (String)passwords.get(key); + } + } + } + } + if (password != null) + { + passwords.remove(key); + removed = true; + + } + } + } + return removed; + + } + + private String getPassword(Map passwords, String hostname, String userid) + { + String password = null; + + String passwordKey = getPasswordKey(hostname, userid); + password =(String) passwords.get(passwordKey); + if (password != null) + return password; + + String phostname = hostname.toUpperCase(); + + // DKM - fallback for different case uids, hostnames or qualified/unqualified hostnames + Iterator keys = passwords.keySet().iterator(); + while (keys.hasNext() && password == null) + { + String key = (String)keys.next(); + if (key.equalsIgnoreCase(passwordKey)) + { + password = (String) passwords.get(key); + } + else + { + String khostname = getHostnameFromPasswordKey(key).toUpperCase(); + String kuid = getUserIdFromPasswordKey(key); + if (kuid.equalsIgnoreCase(userid)) + { + // uid matches, check if hosts are the same + if (khostname.startsWith(phostname) || phostname.startsWith(khostname)) + { + String qkhost = RSECorePlugin.getQualifiedHostName(khostname); + String qphost = RSECorePlugin.getQualifiedHostName(phostname); + if (qkhost.equals(qphost)) + { + password = (String)passwords.get(key); + } + } + } + } + } + + return password; + + } + + /** + * Find the persisted password for the specified systemtype, hostname and userid. + * + * @param systemtype The system type to check for. + * @param hname The hostname to check for. + * @param userid The user ID to check for. + * @param checkDefault Whether or not to check for a default system type if the specified system type is not found. + */ + public SystemSignonInformation find(IRSESystemType systemtype, String hname, String userid, boolean checkDefault) + { + String hostname = hname;//RSEUIPlugin.getQualifiedHostName(hname); + // Convert userid to upper case if required + if (!isUserIDCaseSensitive(systemtype) && userid != null) + { + userid = userid.toUpperCase(); + } + + Map passwords = getPasswordMap(systemtype); + + if (passwords != null) + { + String password = getPassword(passwords, hostname, userid); + + if (password != null) + { + return new SystemSignonInformation(hostname, userid, password, systemtype); + } + } + + // yantzi: RSE6.2 check for default system type entry with this hostname and user ID + if (checkDefault && !DEFAULT_SYSTEM_TYPE.equals(systemtype)) + { + return find(DEFAULT_SYSTEM_TYPE, hostname, userid, false); + } + + return null; + } + + /** + * Helper class for building the key to lookup the password for a specific + * userid and hostname in the Map + */ + private String getPasswordKey(String hname, String userid) + { + String hostname = hname;//RSEUIPlugin.getQualifiedHostName(hname); + StringBuffer buffer = new StringBuffer(hostname); + buffer.append("//"); //$NON-NLS-1$ + buffer.append(userid); + return buffer.toString(); + } + + private String getHostnameFromPasswordKey(String passwordKey) + { + int sepIndex = passwordKey.indexOf("//"); //$NON-NLS-1$ + return passwordKey.substring(0,sepIndex); + } + + private String getUserIdFromPasswordKey(String passwordKey) + { + int sepIndex = passwordKey.indexOf("//"); //$NON-NLS-1$ + return passwordKey.substring(sepIndex + 2, passwordKey.length()); + } + + /** + * Helper method for determining if system type uses case sensitive user IDs + */ + public boolean isUserIDCaseSensitive(IRSESystemType systemType) + { + // First find the correct provider + for (int i = 0; i < systemTypes.length; i++) + { + + if (systemTypes[i].getSystemType().equals(systemType)) + { + return systemTypes[i].isUserIDCaseSensitive(); + } + } + + //Not found: Default system type is case sensitive + return true; + } + + /** + * Retrieve the list of registered system types + */ + public IRSESystemType[] getRegisteredSystemTypes() + { + // yantzi: artemis 6.2, added default system type to list + IRSESystemType[] types = new IRSESystemType[systemTypes.length + 1]; + + types[0] = DEFAULT_SYSTEM_TYPE; + + for (int i = 0; i < systemTypes.length; i++) + { + types[i + 1] = systemTypes[i].getSystemType(); + } + + return types; + } + + /** + * Retrieve a list of the stored user IDs. + * + * @return List A list of the stored user IDs as SystemSignonInformation instances + * without the saved passwords. + */ + public List getSavedUserIDs() + { + List savedUserIDs = new ArrayList(); + Map passwords; + String key; + int separator; + + for (int i = 0; i < systemTypes.length; i++) + { + passwords = getPasswordMap(systemTypes[i].getSystemType()); + if (passwords != null) + { + Iterator keys = passwords.keySet().iterator(); + while (keys.hasNext()) + { + key = (String) keys.next(); + separator = key.indexOf("//"); //$NON-NLS-1$ + savedUserIDs.add(new SystemSignonInformation(key.substring(0, separator), // hostname + key.substring(separator + 2), // userid + systemTypes[i].getSystemType())); // system type + } + } + } + + // yantzi: RSE 6.2 Get DEFAULT system types too + passwords = getPasswordMap(DEFAULT_SYSTEM_TYPE); + if (passwords != null) + { + Iterator keys = passwords.keySet().iterator(); + while (keys.hasNext()) + { + key = (String) keys.next(); + separator = key.indexOf("//"); //$NON-NLS-1$ + savedUserIDs.add(new SystemSignonInformation(key.substring(0, separator), // hostname + key.substring(separator + 2), // userid + DEFAULT_SYSTEM_TYPE)); // system type + } + } + + return savedUserIDs; + } + +} \ No newline at end of file diff --git a/rse/tests/org.eclipse.rse.tests/src/org/eclipse/rse/tests/core/passwords/PasswordsTest.java b/rse/tests/org.eclipse.rse.tests/src/org/eclipse/rse/tests/core/passwords/PasswordsTest.java index 8f5ff871aa9..89e1d3ed92b 100644 --- a/rse/tests/org.eclipse.rse.tests/src/org/eclipse/rse/tests/core/passwords/PasswordsTest.java +++ b/rse/tests/org.eclipse.rse.tests/src/org/eclipse/rse/tests/core/passwords/PasswordsTest.java @@ -134,4 +134,66 @@ public class PasswordsTest extends RSECoreTestCase { assertNull("signon info was found but should not be", returnedInfo); } + public void testMigration() { + //-test-author-:DavidDykstal + if (isTestDisabled()) + return; + + // Setup + IRSESystemType systemType = RSECoreRegistry.getInstance().getSystemType(IRSESystemType.SYSTEMTYPE_LOCAL_ID); + PasswordPersistenceManager newPPM = PasswordPersistenceManager.getInstance(); + if (OriginalPasswordPersistenceManager.isActive()) { + OriginalPasswordPersistenceManager oldPPM = OriginalPasswordPersistenceManager.getInstance(); + + // Clear the new manager entries for those system types. + newPPM.reset(systemType); + + // Populate the old manager with some entries. + oldPPM.add(new SystemSignonInformation("myhost.mycompany.com", "me", "password", systemType), true, false); + oldPPM.add(new SystemSignonInformation("yourhost.yourcompany.com", "you", "xxyyzz", systemType), true, false); + oldPPM.add(new SystemSignonInformation("LOUDHOST.mycompany.com", "thatguy", "abc", systemType), true, false); + + // Reference the new manager for the entries, these should migrate automatically. + SystemSignonInformation foundInfo = null; + foundInfo = newPPM.find(systemType, "myhost.mycompany.com", "me"); + assertNotNull(foundInfo); + assertEquals(foundInfo.getPassword(), "password"); + foundInfo = newPPM.find(systemType, "yourhost.yourcompany.com", "you"); + assertNotNull(foundInfo); + assertEquals(foundInfo.getPassword(), "xxyyzz"); + foundInfo = newPPM.find(systemType, "LOUDHOST.mycompany.com", "thatguy"); + assertNotNull(foundInfo); + assertEquals(foundInfo.getPassword(), "abc"); + } + + } + + public void testAliasing() { + //-test-author-:DavidDykstal + if (isTestDisabled()) + return; + IRSESystemType systemType = RSECoreRegistry.getInstance().getSystemType(IRSESystemType.SYSTEMTYPE_LOCAL_ID); + PasswordPersistenceManager ppm = PasswordPersistenceManager.getInstance(); + ppm.add(new SystemSignonInformation("LOUDHOST.mycompany.com", "thatguy", "abc", systemType), true, false); + SystemSignonInformation foundInfo = ppm.find(systemType, "LOUDHOST.mycompany.com", "thatguy"); + assertNotNull(foundInfo); + assertEquals(foundInfo.getPassword(), "abc"); + foundInfo = ppm.find(systemType, "loudhost.mycompany.com", "thatguy"); + assertNotNull(foundInfo); + assertEquals(foundInfo.getPassword(), "abc"); + foundInfo = ppm.find(systemType, "loudhost.MyCompany.com", "thatguy"); + assertNotNull(foundInfo); + assertEquals(foundInfo.getPassword(), "abc"); + } + + public void testBadArgs() { + if (isTestDisabled()) + return; + IRSESystemType systemType = RSECoreRegistry.getInstance().getSystemType(IRSESystemType.SYSTEMTYPE_LOCAL_ID); + PasswordPersistenceManager ppm = PasswordPersistenceManager.getInstance(); + ppm.add(new SystemSignonInformation("myhost.mycompany.com", "me", "password", systemType), true, false); + SystemSignonInformation info = ppm.find(systemType, "myhost.mycompany.com", null); + assertNull(info); + } + }