/******************************************************************************** * Copyright (c) 2007, 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 * * Contributors: * David Dykstal (IBM) - 168977: refactoring IConnectorService * Martin Oberhuber (Wind River) - [175262] IHost.getSystemType() should return IRSESystemType * Martin Oberhuber (Wind River) - [184095] Replace systemTypeName by IRSESystemType * Martin Oberhuber (Wind River) - [186748] Move ISubSystemConfigurationAdapter from UI/rse.core.subsystems.util * David Dykstal (IBM) - [210474] Deny save password function missing * David Dykstal (IBM) - [225089][ssh][shells][api] Canceling connection leads to exception * Martin Oberhuber (Wind River) - [218304] Improve deferred adapter loading ********************************************************************************/ package org.eclipse.rse.ui.subsystems; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Platform; import org.eclipse.rse.core.IRSESystemType; import org.eclipse.rse.core.PasswordPersistenceManager; import org.eclipse.rse.core.model.IHost; import org.eclipse.rse.core.model.SystemSignonInformation; import org.eclipse.rse.core.subsystems.AbstractCredentialsProvider; import org.eclipse.rse.core.subsystems.IConnectorService; import org.eclipse.rse.core.subsystems.ICredentials; import org.eclipse.rse.core.subsystems.ISubSystem; import org.eclipse.rse.core.subsystems.ISubSystemConfiguration; import org.eclipse.rse.logging.Logger; import org.eclipse.rse.logging.LoggerFactory; import org.eclipse.rse.services.clientserver.messages.SystemMessage; import org.eclipse.rse.ui.ISystemMessages; import org.eclipse.rse.ui.RSEUIPlugin; import org.eclipse.rse.ui.dialogs.ICredentialsValidator; import org.eclipse.rse.ui.dialogs.ISystemPasswordPromptDialog; import org.eclipse.rse.ui.dialogs.SystemChangePasswordDialog; import org.eclipse.rse.ui.dialogs.SystemPasswordPromptDialog; import org.eclipse.rse.ui.messages.SystemMessageDialog; import org.eclipse.rse.ui.validators.ISystemValidator; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; /** * The {@link StandardCredentialsProvider} is an extension of * {@link AbstractCredentialsProvider} that provides for the prompting of a userid * and password. *

* It uses a {@link PasswordPersistenceManager} to store the passwords in the * keychain keyed by {@link IHost} and possibly by {@link ISubSystemConfiguration}. *

* This is suitable for use by subclasses of {@link StandardConnectorService} * that wish to provide prompting and persistence for userids and passwords when * connecting. *

* This class is may be subclassed. Typically to provide connector service * specific prompting. */ public class StandardCredentialsProvider extends AbstractCredentialsProvider { /** * A runnable that will actually perform the prompting. */ private class PromptForCredentials implements Runnable { private boolean cancelled = false; /** * @return true if prompting was cancelled. */ public boolean isCancelled() { return cancelled; } /* (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { Shell shell = getShell(); if (shell != null) { ISystemPasswordPromptDialog dialog = getPasswordPromptDialog(shell); dialog.setSystemInput(getConnectorService()); dialog.setForceToUpperCase(forcePasswordToUpperCase()); dialog.setSignonValidator(getSignonValidator()); if (supportsUserId()) { dialog.setUserIdValidator(getUserIdValidator()); } if (supportsPassword()) { dialog.setSavePassword(savePassword); dialog.setPassword(password); dialog.setPasswordValidator(getPasswordValidator()); } try { dialog.open(); } catch (Exception e) { logException(e); } cancelled = dialog.wasCancelled(); if (!cancelled) { userId = dialog.getUserId(); password = dialog.getPassword(); saveUserId = dialog.getIsUserIdChangePermanent(); savePassword = dialog.getIsSavePassword(); } } else { cancelled = true; } } } /** * A runnable that will prompt for a new password. Typically used when * a password has expired. */ private class PromptForNewPassword implements Runnable { private SystemMessage message; private boolean cancelled = false; public PromptForNewPassword(SystemMessage message) { this.message = message; } public boolean isCancelled() { return cancelled; } public void run() { Shell shell = getShell(); if (shell != null) { SystemChangePasswordDialog dlg = new SystemChangePasswordDialog(shell, getConnectorService().getHostName(), getUserId(), message); dlg.setSavePassword(savePassword); dlg.open(); cancelled = dlg.wasCancelled(); if (!cancelled) { password = dlg.getNewPassword(); savePassword = dlg.getIsSavePassword(); } } else { cancelled = true; } } } private String userId = null; private String password = null; private boolean savePassword = false; private boolean saveUserId = false; private boolean acquiring = false; /** * Creates a standard credentials provider for a connector service. * @param connectorService the connector service associated with this * provider. * @see IConnectorService */ public StandardCredentialsProvider(IConnectorService connectorService) { super(connectorService); } /** * Acquires the credentials (userid and password) for this connector * service. The search order for the password is as follows: *

*
    *
  1. First check if the password is already known by this connector * service and that it is still valid. *
  2. If password not known then look in the password store and verify that * it is still valid. *
  3. If a valid password is not found or is to be re-acquired then prompt * the user *
* * @param reacquire if true then present the prompt even if the password was * found and is valid. * @throws OperationCanceledException if user is prompted and user cancels * that prompt or if {@link #isSuppressed()} is true. Before RSE 3.0, * the method would have thrown {@link InterruptedException} in that * case. * @since 3.0 */ public final void acquireCredentials(boolean reacquire) throws OperationCanceledException { if (isSuppressed()) { throw new OperationCanceledException(); } ISubSystem subsystem = getPrimarySubSystem(); IHost host = subsystem.getHost(); String hostName = host.getHostName(); IRSESystemType systemType = host.getSystemType(); savePassword = false; if (supportsUserId()) { boolean sameHost = hostName.equalsIgnoreCase(getConnectorService().getHostName()); if (!sameHost) { clearCredentials(); } getUserId(); } if (supportsPassword()) { if (password == null) { PasswordPersistenceManager ppm = PasswordPersistenceManager.getInstance(); SystemSignonInformation savedSignonInformation = ppm.find(systemType, hostName, userId); if (savedSignonInformation != null) { password = savedSignonInformation.getPassword(); savePassword = true; } } } ICredentialsValidator validator = getSignonValidator(); boolean signonValid = true; ICredentials credentials = getCredentials(); if (validator != null) { SystemMessage m = validator.validate(credentials); // update the password incase an expired password was changed in validate () - ry password = credentials.getPassword(); signonValid = (m == null); if (!signonValid) { // If we ran into an invalid stored password we need to tell the user. Shell shell = getShell(); if (shell != null) { SystemMessage msg = RSEUIPlugin.getPluginMessage(ISystemMessages.MSG_COMM_PWD_INVALID); msg.makeSubstitution(userId, getConnectorService().getHostName()); SystemMessageDialog dialog = new SystemMessageDialog(shell, msg); dialog.open(); } } } if (supportsPassword() || supportsUserId()) { boolean passwordNeeded = supportsPassword() && password == null; boolean userIdNeeded = supportsUserId() && userId == null; if (passwordNeeded || userIdNeeded || reacquire) { promptForCredentials(); acquiring = true; if (savePassword) { getConnectorService().savePassword(); } else { getConnectorService().removePassword(); } if (saveUserId) { getConnectorService().saveUserId(); } acquiring = false; } } } /* (non-Javadoc) * @see org.eclipse.rse.core.subsystems.ICredentialsProvider#clearCredentials() */ public final void clearCredentials() { if (!acquiring) { password = null; userId = null; } } /* (non-Javadoc) * @see org.eclipse.rse.core.subsystems.ICredentialsProvider#clearPassword() */ final public void clearPassword() { if (!acquiring) { password = null; } } /* (non-Javadoc) * @see org.eclipse.rse.core.subsystems.ICredentialsProvider#getCredentials() */ public final ICredentials getCredentials() { IHost host = getConnectorService().getHost(); String hostName = host.getHostName(); IRSESystemType systemType = host.getSystemType(); SystemSignonInformation result = new SystemSignonInformation(hostName, userId, password, systemType); return result; } /* (non-Javadoc) * @see org.eclipse.rse.core.subsystems.ICredentialsProvider#getUserId() */ final public String getUserId() { if (supportsUserId()) { if (userId == null) { userId = getSubSystemUserId(); } } return userId; } /* (non-Javadoc) * @see org.eclipse.rse.core.subsystems.ICredentialsProvider#repairCredentials(org.eclipse.rse.services.clientserver.messages.SystemMessage) */ public final void repairCredentials(SystemMessage prompt) throws OperationCanceledException { promptForNewPassword(prompt); } /* (non-Javadoc) * @see org.eclipse.rse.core.subsystems.ICredentialsProvider#setPassword(java.lang.String) */ public final void setPassword(String password) { this.password = password; } /** * Useful utility method. Fully implemented, no need to override.
* Set the password if you got it from somewhere * @param matchingUserId the user for which to set the password * @param password the password to set for this userid * @param persist true if the password is to be persisted as well */ public final void setPassword(String matchingUserId, String password, boolean persist) { if (getPrimarySubSystem().forceUserIdToUpperCase()) { matchingUserId = matchingUserId.toUpperCase(); } IConnectorService cs = getConnectorService(); IRSESystemType systemType = cs.getHost().getSystemType(); String hostName = cs.getHostName(); SystemSignonInformation signonInformation = new SystemSignonInformation(hostName, matchingUserId, password, systemType); setSignonInformation(signonInformation); if (persist) { // if password should be persisted, then add to disk PasswordPersistenceManager.getInstance().add(signonInformation, true, false); } else { // otherwise, remove from both memory and disk PasswordPersistenceManager.getInstance().remove(systemType, hostName, userId); } } /* (non-Javadoc) * @see org.eclipse.rse.core.subsystems.IConnectorService#setUserId(java.lang.String) */ final public void setUserId(String newId) { if (userId == null || !userId.equals(newId)) { userId = newId; } } /** * A default implementation is supplied, but can be overridden if desired.
* Instantiates and returns the dialog to prompt for the userId and password. * @return An instance of a dialog class that implements {@link ISystemPasswordPromptDialog} */ protected ISystemPasswordPromptDialog getPasswordPromptDialog(Shell shell) { ISystemPasswordPromptDialog dlg = new SystemPasswordPromptDialog(shell, requiresUserId(), requiresPassword()); return dlg; } /** * Optionally overridable, not implemented by default.
* Get the credentails validator to use to validate the credentials as entered * in the dialog. This should only do a local validation. * @return null indicating that no signon validator exists. */ protected ICredentialsValidator getSignonValidator() { return null; } private boolean forcePasswordToUpperCase() { return getPrimarySubSystem().forceUserIdToUpperCase(); } private ISystemValidator getPasswordValidator() { ISubSystemConfiguration subsystemConfiguration = getPrimarySubSystem().getSubSystemConfiguration(); ISubSystemConfigurationAdapter adapter = (ISubSystemConfigurationAdapter) Platform.getAdapterManager().getAdapter(subsystemConfiguration, ISubSystemConfigurationAdapter.class); return adapter.getPasswordValidator(subsystemConfiguration); } private ISubSystem getPrimarySubSystem() { return getConnectorService().getPrimarySubSystem(); } private Shell getShell() { Shell shell = null; IWorkbench workbench = PlatformUI.getWorkbench(); if (workbench != null) { IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); if (window != null) { shell = window.getShell(); } } return shell; } private String getSubSystemUserId() { ISubSystem ss = getPrimarySubSystem(); String result = ss.getUserId(); return result; } private ISystemValidator getUserIdValidator() { ISubSystemConfiguration subsystemConfiguration = getPrimarySubSystem().getSubSystemConfiguration(); ISubSystemConfigurationAdapter adapter = (ISubSystemConfigurationAdapter) subsystemConfiguration.getAdapter(ISubSystemConfigurationAdapter.class); // TODO This typically runs in the UI thread. It should probably be // moved into the promptForCredentials() method which typically runs in // a Job, or even into {@link SubSystem#promptForPassword()}. See // https://bugs.eclipse.org/bugs/show_bug.cgi?id=218304 if (adapter == null) { Platform.getAdapterManager().loadAdapter(subsystemConfiguration, ISubSystemConfigurationAdapter.class.getName()); adapter = (ISubSystemConfigurationAdapter) subsystemConfiguration.getAdapter(ISubSystemConfigurationAdapter.class); } ISystemValidator validator = adapter.getUserIdValidator(subsystemConfiguration); return validator; } private void logException(Throwable t) { Logger log = LoggerFactory.getLogger(RSEUIPlugin.getDefault()); log.logError("Unexpected exception", t); //$NON-NLS-1$ } private void promptForCredentials() throws OperationCanceledException { PromptForCredentials runnable = new PromptForCredentials(); Display.getDefault().syncExec(runnable); if (runnable.isCancelled()) { throw new OperationCanceledException(); } } private void promptForNewPassword(SystemMessage prompt) throws OperationCanceledException { PromptForNewPassword runnable = new PromptForNewPassword(prompt); Display.getDefault().syncExec(runnable); if (runnable.isCancelled()) { throw new OperationCanceledException(); } } private void setSignonInformation(SystemSignonInformation signonInfo) { password = signonInfo.getPassword(); userId = signonInfo.getUserId(); } }