/*******************************************************************************
* Copyright (c) 2012 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.openshift.express.internal.core.connection;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.Status;
import org.eclipse.osgi.util.NLS;
import org.jboss.tools.openshift.common.core.ICredentialsPrompter;
import org.jboss.tools.openshift.common.core.connection.AbstractConnection;
import org.jboss.tools.openshift.common.core.connection.ConnectionType;
import org.jboss.tools.openshift.common.core.connection.IConnection;
import org.jboss.tools.openshift.common.core.utils.StringUtils;
import org.jboss.tools.openshift.common.core.utils.UrlUtils;
import org.jboss.tools.openshift.express.client.ClientSystemProperties;
import org.jboss.tools.openshift.express.core.util.ExpressConnectionUtils;
import org.jboss.tools.openshift.express.internal.core.ExpressCoreActivator;
import org.jboss.tools.openshift.express.internal.core.preferences.ExpressCorePreferences;
import org.jboss.tools.openshift.express.internal.core.util.ExpressResourceLabelUtils;
import org.jboss.tools.openshift.internal.common.core.UsageStats;
import org.jboss.tools.openshift.internal.common.core.security.OpenShiftSecureStorageKey;
import org.jboss.tools.openshift.internal.common.core.security.SecureStore;
import org.jboss.tools.openshift.internal.common.core.security.SecureStoreException;
import com.openshift.client.ApplicationScale;
import com.openshift.client.ConnectionBuilder;
import com.openshift.client.IApplication;
import com.openshift.client.IDomain;
import com.openshift.client.IGearProfile;
import com.openshift.client.IHttpClient.ISSLCertificateCallback;
import com.openshift.client.IOpenShiftSSHKey;
import com.openshift.client.IQuickstart;
import com.openshift.client.ISSHPublicKey;
import com.openshift.client.IUser;
import com.openshift.client.OpenShiftException;
import com.openshift.client.OpenShiftUnknonwSSHKeyTypeException;
import com.openshift.client.cartridge.ICartridge;
import com.openshift.client.cartridge.IEmbeddableCartridge;
import com.openshift.client.cartridge.IStandaloneCartridge;
import com.openshift.internal.client.utils.StreamUtils;
/**
* @author Rob Stryker
* @author Xavier Coulon
* @author Andre Dietisheim
*/
public class ExpressConnection extends AbstractConnection {
private static final String SECURE_STORAGE_PASSWORD = "pass";
/*
* Hard-code the openshift UI activator id, due to backwards compatability issues
*/
private static final String SECURE_STORAGE_BASEKEY = "org.jboss.tools.openshift.express.ui";
private String username;
private String password;
private IUser user;
private boolean isDomainLoaded;
private boolean rememberPassword;
private boolean promptPasswordEnabled = true;
private boolean didPromptForPassword;
private boolean passwordLoaded;
private ICredentialsPrompter passwordPrompter;
private ISSLCertificateCallback sslCallback;
public ExpressConnection(String host, ISSLCertificateCallback callback) {
this(null, null, UrlUtils.getScheme(host), UrlUtils.cutScheme(host), false, null, callback);
}
public ExpressConnection(String username, String host) {
this(username, null, host, false, null);
}
public ExpressConnection(String username, String host, ICredentialsPrompter prompter, ISSLCertificateCallback sslCallback) {
this(username, null, UrlUtils.getScheme(host), UrlUtils.cutScheme(host), false, null, sslCallback);
this.passwordPrompter = prompter;
}
public ExpressConnection(String username, String password, String host, boolean rememberPassword, ISSLCertificateCallback sslCallback) {
this(username, password, UrlUtils.getScheme(host), UrlUtils.cutScheme(host), rememberPassword, null, sslCallback);
}
protected ExpressConnection(String username, String password, String scheme, String host, boolean rememberPassword, IUser user, ISSLCertificateCallback sslCallback) {
super(scheme, host);
this.username = username;
this.password = password;
this.rememberPassword = rememberPassword;
this.sslCallback = sslCallback;
setUser(user);
}
protected void setUser(IUser user) {
this.user = user;
}
@Override
public String getUsername() {
return username;
}
@Override
public void setUsername(String username) {
firePropertyChange(PROPERTY_USERNAME, this.username, this.username = username);
clearUser();
}
@Override
public String getPassword() {
loadPassword();
return password;
}
@Override
public void setPassword(String password) {
firePropertyChange(PROPERTY_PASSWORD, this.password, this.password = password);
this.passwordLoaded = true;
clearUser();
}
@Override
public String getHost() {
if (isDefaultHost()) {
return ExpressConnectionUtils.getDefaultHostUrl();
}
return super.getHost();
}
@Override
public String getScheme() {
if (isDefaultHost()) {
return UrlUtils.getScheme(ExpressConnectionUtils.getDefaultHostUrl());
}
return UrlUtils.getScheme(super.getHost());
}
@Override
public boolean isDefaultHost() {
return isDefaultHost(super.getHost());
}
private boolean isDefaultHost(String host) {
return host == null
|| UrlUtils.cutScheme(host).isEmpty();
}
@Override
public boolean isRememberPassword() {
return rememberPassword;
}
@Override
public final void setRememberPassword(boolean rememberPassword) {
firePropertyChange(PROPERTY_REMEMBER_PASSWORD, this.rememberPassword, this.rememberPassword = rememberPassword);
}
public boolean canPromptForPassword() {
return this.didPromptForPassword == false
&& promptPasswordEnabled;
}
@Override
public void enablePromptCredentials(boolean enable) {
this.promptPasswordEnabled = enable;
}
@Override
public boolean isEnablePromptCredentials() {
return promptPasswordEnabled;
}
public void setSSLCertificateCallback(ISSLCertificateCallback callback) {
this.sslCallback = callback;
}
/**
* Connects to OpenShift. Will do nothing if this user is already
* connected.
*
* @return <code>true</code> if connect succeeed, <code>false</code>
* otherwise
* @throws OpenShiftException
*/
@Override
public boolean connect() throws OpenShiftException {
if (isConnected()) {
save();
return true;
}
if (createUser()) {
/* JBIDE-15847: user may get rewritten in case of kerberos */
updateUsername(user);
save();
return true;
} else {
return false;
}
}
/**
* Creates an OpenShift user instance for this user. Prompts for
* credentials if needed.
*
* @return <code>true</code> if user could get created, <code>false</code>
* otherwise.
*/
protected boolean createUser() {
loadPassword();
if (password == null) {
return promptForCredentials();
} else {
setClientTimeout();
IUser user = doCreateUser();
setUser(user);
return true;
}
}
protected IUser doCreateUser() {
final String userId = ExpressCoreActivator.PLUGIN_ID + " " + ExpressCoreActivator.getDefault().getBundle().getVersion();
try {
return new ConnectionBuilder(getHost())
.credentials(username, password)
.clientId(userId)
.sslCertificateCallback(sslCallback)
.create()
.getUser();
} catch (IOException e) {
throw new OpenShiftException("Could not connect for user {0} - {1}", username, getHost());
}
}
private void setClientTimeout() {
int timeout = ExpressCorePreferences.INSTANCE
.getClientReadTimeout(ClientSystemProperties.getReadTimeoutSeconds());
ClientSystemProperties.setReadTimeoutSeconds(timeout);
}
/**
* Attempts to load the password from the secure storage, only at first time
* it is called.
*/
private void loadPassword() {
if (StringUtils.isEmpty(password)
&& !passwordLoaded) {
this.password = getPassword(getSecureStore(getHost(), getUsername()));
this.passwordLoaded = true;
this.rememberPassword = (password != null);
}
}
private boolean hasUser() {
return user != null;
}
protected void clearUser() {
this.user = null;
}
private String updateUsername(IUser user) {
if (!user.getRhlogin().equals(username)) {
ExpressCoreActivator.getDefault().getLog().log(
new Status(Status.WARNING, ExpressCoreActivator.PLUGIN_ID,
NLS.bind("User {0} was logged in as {1}", username, user.getRhlogin())));
}
this.username = user.getRhlogin();
return username;
}
private boolean promptForCredentials() {
if (passwordPrompter == null) {
return false;
}
try {
passwordPrompter.promptAndAuthenticate(this, null);
didPromptForPassword = true;
} catch (Exception e) {
ExpressCoreActivator.pluginLog().logError("Failed to retrieve User's password", e);
}
return hasUser();
}
public IApplication createApplication(final String applicationName, final IStandaloneCartridge standaloneCartridge,
final ApplicationScale scale, final IGearProfile gearProfile, final IDomain domain)
throws OpenShiftException {
if (connect()) {
return domain.createApplication(applicationName, standaloneCartridge, scale, gearProfile);
} else {
return null;
}
}
/**
* Creates a new domain with the given id
*
* @param id
* the domain id
* @return the created domain
* @throws OpenShiftException
* @throws SocketTimeoutException
*/
public IDomain createDomain(String id) throws OpenShiftException {
if (connect()) {
return user.createDomain(id);
} else {
return null;
}
}
public List<IStandaloneCartridge> getStandaloneCartridges() throws OpenShiftException {
if (connect()) {
return user.getConnection().getStandaloneCartridges();
} else {
return null;
}
}
public List<IEmbeddableCartridge> getEmbeddableCartridges() throws OpenShiftException {
if (connect()) {
return user.getConnection().getEmbeddableCartridges();
} else {
return null;
}
}
public List<ICartridge> getCartridges() throws OpenShiftException {
if (connect()) {
return user.getConnection().getCartridges();
} else {
return null;
}
}
public List<IQuickstart> getQuickstarts() throws OpenShiftException {
if (connect()) {
return user.getConnection().getQuickstarts();
} else {
return null;
}
}
public void load() {
getDomains();
}
public IApplication getApplication(String name, IDomain domain) throws OpenShiftException {
if (domain == null) {
return null;
}
return domain.getApplicationByName(name);
}
public boolean hasApplication(String name, IDomain domain) throws OpenShiftException {
return getApplication(name, domain) != null;
}
public IDomain getDefaultDomain() throws OpenShiftException {
if (connect()) {
return user.getDefaultDomain();
} else {
return null;
}
}
public IDomain getDomain(String id) throws OpenShiftException {
if (StringUtils.isEmpty(id)) {
return null;
}
// trigger authentication, domain loading
getDomains();
//if authentication failed, user is null
if(!isConnected()) {
return null;
}
return user.getDomain(id);
}
public IDomain getFirstDomain() throws OpenShiftException {
if (!connect()) {
return null;
} else {
List<IDomain> domains = getDomains();
if (domains == null
|| domains.isEmpty()) {
return null;
}
return domains.get(0);
}
}
public List<IDomain> getDomains() throws OpenShiftException {
if (!connect()) {
return Collections.emptyList();
} else {
List<IDomain> domains = user.getDomains();
isDomainLoaded = true;
return domains;
}
}
public void destroy(IDomain domain, boolean force) {
if (connect()) {
domain.destroy(force);
}
}
public boolean isLoaded() throws OpenShiftException {
return isDomainLoaded;
}
public boolean hasApplicationOfType(IStandaloneCartridge type) throws OpenShiftException {
if (hasDomain()) {
return user.getDefaultDomain().hasApplicationByCartridge(type);
}
return false;
}
public boolean hasDomain() throws OpenShiftException {
if (connect()) {
return user.hasDomain();
} else {
return false;
}
}
public boolean hasSSHKeys() throws OpenShiftException {
if (connect()) {
return !user.getSSHKeys().isEmpty();
} else {
return false;
}
}
@Override
public void refresh() throws OpenShiftException {
isDomainLoaded = false;
if (connect()) {
user.refresh();
}
}
@Override
public boolean isConnected() {
return hasUser();
}
public List<IOpenShiftSSHKey> getSSHKeys() throws OpenShiftException {
if (connect()) {
return user.getSSHKeys();
} else {
return Collections.emptyList();
}
}
public IOpenShiftSSHKey getSSHKeyByPublicKey(String publicKey) throws OpenShiftUnknonwSSHKeyTypeException,
OpenShiftException {
if (connect()) {
return user.getSSHKeyByPublicKey(publicKey);
} else {
return null;
}
}
public IOpenShiftSSHKey putSSHKey(String name, ISSHPublicKey key) throws OpenShiftException {
if (connect()) {
return user.putSSHKey(name, key);
} else {
return null;
}
}
public boolean hasSSHKeyName(String name) throws OpenShiftException {
if (connect()) {
return user.hasSSHKeyName(name);
} else {
return false;
}
}
public boolean hasSSHPublicKey(String publicKey) {
if (connect()) {
return user.hasSSHPublicKey(publicKey);
} else {
return false;
}
}
public void save() {
String username = getUsername();
if (!StringUtils.isEmpty(username)) {
ExpressCorePreferences.INSTANCE.saveLastUsername(username);
saveOrClearPassword(username, getHost(), getPassword());
}
}
private void saveOrClearPassword(String username, String host, String password) {
SecureStore store = getSecureStore(host, username);
if (store != null
&& !StringUtils.isEmpty(username)) {
try {
if (isRememberPassword()
&& !StringUtils.isEmpty(password)) {
store.put(SECURE_STORAGE_PASSWORD, password);
} else {
store.remove(SECURE_STORAGE_PASSWORD);
}
} catch (SecureStoreException e) {
firePropertyChange(SecureStoreException.ID, null, e);
//ExpressCoreActivator.pluginLog().logError(e.getMessage(), e);
}
}
}
private String getPassword(SecureStore store) {
String password = null;
if (store != null
&& !StringUtils.isEmpty(getUsername())) {
try {
password = store.get(SECURE_STORAGE_PASSWORD);
} catch (SecureStoreException e) {
ExpressCoreActivator.pluginLog().logError(e.getMessage(), e);
}
}
return password;
}
/**
* Returns a secure store for the current host and username
*/
private SecureStore getSecureStore(final String host, final String username) {
return new SecureStore(new OpenShiftSecureStorageKey(SECURE_STORAGE_BASEKEY, host, username));
}
public void removeSecureStoreData() {
SecureStore store = getSecureStore(getHost(), getUsername());
if (store != null) {
try {
store.removeNode();
} catch (SecureStoreException e) {
firePropertyChange(SecureStoreException.ID, null, e);
ExpressCoreActivator.pluginLog().logWarning(e.getMessage(), e);
}
}
}
public String getId() {
StringBuilder builder = new StringBuilder(username);
builder
.append(" at ")
.append(getHost());
if (isDefaultHost()) {
builder.append(" (default)");
}
return builder.toString();
}
@Override
public boolean canConnect() throws IOException {
// TODO: move to client library
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) new URL(getHost() + "/broker/rest/api").openConnection();
connection.setConnectTimeout(2 * 1000);
connection.setInstanceFollowRedirects(true);
connection.setRequestProperty("Accept", "application/json");
InputStream in = connection.getInputStream();
StreamUtils.readToString(in);
return connection.getResponseCode() == 200;
} catch (MalformedURLException e) {
return false;
} catch (SocketTimeoutException e) {
throw e;
} catch (IOException e) {
if (connection != null
// can throw IOException (ex. UnknownHostException)
&& connection.getResponseCode() != -1) {
return false;
} else {
throw e;
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
@Override
public IConnection clone() {
return new ExpressConnection(getUsername(), getPassword(), getScheme(), getHost(), isRememberPassword(), null, sslCallback);
}
@Override
public void update(IConnection connection) {
Assert.isLegal(connection instanceof ExpressConnection);
ExpressConnection otherConnection = (ExpressConnection) connection;
setUsername(otherConnection.getUsername());
setPassword(otherConnection.getPassword());
setRememberPassword(otherConnection.isRememberPassword());
setUser(otherConnection.user);
this.sslCallback = otherConnection.sslCallback;
}
@Override
public ConnectionType getType() {
return ConnectionType.Express;
}
@Override
public void notifyUsage() {
UsageStats.getInstance().newV2Connection(getHost());
}
@Override
public String toString() {
return ExpressResourceLabelUtils.toString(this);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (!(obj instanceof ExpressConnection))
return false;
ExpressConnection other = (ExpressConnection) obj;
if (password == null) {
if (other.password != null)
return false;
} else if (!password.equals(other.password))
return false;
if (username == null) {
if (other.username != null)
return false;
} else if (!username.equals(other.username))
return false;
return true;
}
}