/*******************************************************************************
* Copyright (c) 2013 Cloud Bees, 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:
* Cloud Bees, Inc. - initial API and implementation
*******************************************************************************/
package com.cloudbees.eclipse.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import com.cloudbees.eclipse.core.domain.JenkinsInstance;
import com.cloudbees.eclipse.core.gc.api.AccountNameRequest;
import com.cloudbees.eclipse.core.gc.api.AccountNameResponse;
import com.cloudbees.eclipse.core.gc.api.AccountNamesRequest;
import com.cloudbees.eclipse.core.gc.api.AccountNamesResponse;
import com.cloudbees.eclipse.core.gc.api.AccountServiceStatusRequest;
import com.cloudbees.eclipse.core.gc.api.AccountServiceStatusResponse;
import com.cloudbees.eclipse.core.gc.api.KeysUsingAuthRequest;
import com.cloudbees.eclipse.core.gc.api.KeysUsingAuthResponse;
import com.cloudbees.eclipse.core.util.Utils;
import com.google.gson.Gson;
/**
* Central access to CloudBees Grand Central API
*
* @author ahtik
*/
public class GrandCentralService {
public static final String HOST = System.getProperty("cloudbees.host", "cloudbees.com");
// private static final String BASE_URL =
// "https://grandcentral.cloudbees.com/api/";
private static final String BASE_URL = "https://grandcentral." + HOST + "/api/";
public static final String GC_BASE_URL = "https://grandcentral." + HOST;
private String email = null;
private String password = null;
private String activeAccount = null;
private String[] accountsCache = null;
private AuthInfo authInfo = null;
public GrandCentralService() {
}
public void setAuthInfo(final String email, final String password) {
this.email = email;
this.password = password;
activeAccount = null;
accountsCache = null;
}
/**
* Validates user credential against CloudBees SSO authentication server. TODO Refactor to json-based validation
*
* @param email
* @param password
* @param monitor
* @return
* @throws CloudBeesException
*/
public boolean validateUser(final IProgressMonitor monitor) throws CloudBeesException {
if (!hasAuthInfo()) {
return false;
}
AuthInfo auth = getAuthInfo(monitor);
return auth.getAuth().api_key.length() > 0 && auth.getAuth().secret_key.length() > 0
&& auth.getAuth().uid.length() > 0;
}
public boolean hasAuthInfo() {
return this.email != null && this.email.trim().length() > 0 && this.password != null
&& this.password.trim().length() > 0;
}
public AuthInfo getCachedAuthInfo(final boolean refresh, IProgressMonitor monitor) throws CloudBeesException {
//authInfo = null;
if (refresh || authInfo == null) {
authInfo = getAuthInfo(monitor);
}
return authInfo;
}
@Deprecated
private String getCachedPrimaryUser(final boolean refresh) throws CloudBeesException {
String user = null;
if (refresh || user == null) {
user = getPrimaryAccount(null);
}
return user;
}
private AuthInfo getAuthInfo(final IProgressMonitor monitor) throws CloudBeesException {
StringBuffer errMsg = new StringBuffer();
try {
String url = BASE_URL + "user/keys_using_auth";
HttpClient httpclient = Utils.getAPIClient(url);
KeysUsingAuthRequest req = new KeysUsingAuthRequest();
req.email = this.email;
req.password = this.password;
HttpPost post = Utils.jsonRequest(url, req);
HttpResponse resp = httpclient.execute(post);
String bodyResponse = Utils.getResponseBody(resp);
Gson g = Utils.createGson();
KeysUsingAuthResponse services = g.fromJson(bodyResponse, KeysUsingAuthResponse.class);
if (services.message != null && services.message.length() > 0) {
errMsg.append(services.message);
}
Utils.checkResponseCode(resp);
return new AuthInfo(services);
} catch (Exception e) {
throw cbthrow(errMsg.toString(), e);
}
}
private CloudBeesException cbthrow(String errMsg, Exception e) {
String extra = "";
if (e.getCause() != null) {
extra = e.getCause().getMessage();
}
return new CloudBeesException("Failed to get account services info."
+ (errMsg.length() > 0 ? "\n" + errMsg + "; " + extra : "\n" + extra), e);
}
/*
* private boolean webValidateUser(String email, String password) throws
* CloudBeesException { String url =
* "https://sso.cloudbees.com/sso-gateway/signon/usernamePasswordLogin.do";
*
* HttpClient httpclient = getAPIClient();
*
* HttpPost post = new HttpPost(url);
*
* List<NameValuePair> formparams = new ArrayList<NameValuePair>();
* formparams.add(new BasicNameValuePair("josso_cmd", "login"));
* formparams.add(new BasicNameValuePair("josso_username", email));
* formparams.add(new BasicNameValuePair("josso_password", password));
*
* try { UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams,
* "UTF-8"); post.setEntity(entity);
*
* HttpResponse resp = httpclient.execute(post); int code =
* resp.getStatusLine().getStatusCode(); if (code == 302 || code == 301) {
* // TODO Temporarily until json API becomes available, validate // user by
* assuming redirect means successful login return true; }
*
* return false; // wrong response code means invalid user or //
* unrecognized response page
*
* } catch (ClientProtocolException e) { throw new
* CloudBeesException("Failed to validate user", e); } catch (IOException e)
* { throw new CloudBeesException("Failed to validate user", e); } }
*/
/*
* public String remoteGetDomainId() throws CloudBeesException {
*
* try { HttpClient httpclient = Utils.getAPIClient();
*
* KeysUsingAuthRequest req = new KeysUsingAuthRequest(); req.email = email;
* req.password = password;
*
* Gson g = Utils.createGson(); String json = g.toJson(req);
*
* String url = BASE_URL + "api_key"; HttpPost post = new HttpPost(url);
* post.getParams().setParameter("json", json); HttpResponse resp =
* httpclient.execute(post);
*
* Utils.checkResponseCode(resp); String bodyResponse =
* Utils.getResponseBody(resp);
*
* KeysUsingAuthResponse domainId = g.fromJson(bodyResponse,
* KeysUsingAuthResponse.class);
*
* return domainId.api_key;
*
* } catch (Exception e) { throw new
* CloudBeesException("Failed to get api_key", e); } }
*/public void start() {
// Do nothing for now.
}
public void stop() {
// Do nothing for now.
}
private AccountServiceStatusResponse loadAccountServices(final String account) throws CloudBeesException {
StringBuffer errMsg = new StringBuffer();
try {
String url = BASE_URL + "account/service_status";
HttpClient httpclient = Utils.getAPIClient(url);
AccountServiceStatusRequest req = new AccountServiceStatusRequest();
req.account = account;
AuthInfo auth = getAuthInfo(null);
req.uid = auth.getAuth().uid;
//System.out.println("URL: " + url);
HttpPost post = Utils.jsonRequest(url, req);
HttpResponse resp = httpclient.execute(post);
String bodyResponse = Utils.getResponseBody(resp);
//System.out.println("Received: " + bodyResponse);
Gson g = Utils.createGson();
AccountServiceStatusResponse services = g.fromJson(bodyResponse, AccountServiceStatusResponse.class);
if (services.message != null && services.message.length() > 0) {
errMsg.append(services.message);
}
Utils.checkResponseCode(resp);
return services;
} catch (Exception e) {
throw new CloudBeesException("Failed to get account services info"
+ (errMsg.length() > 0 ? " (" + errMsg + ")" : ""), e);
}
}
public List<JenkinsInstance> loadDevAtCloudInstances(final IProgressMonitor monitor) throws CloudBeesException {
if (!hasAuthInfo()) {
return new ArrayList<JenkinsInstance>();
}
StringBuffer errMsg = new StringBuffer();
try {
String account = getActiveAccountName();
if (account == null) {
return new ArrayList<JenkinsInstance>();
}
List<JenkinsInstance> instances = new ArrayList<JenkinsInstance>();
String url = "https://" + account + ".ci." + HOST;
JenkinsInstance inst = new JenkinsInstance(account, url, this.email, this.password, true, true);
instances.add(inst);
return instances;
} catch (Exception e) {
throw new CloudBeesException("Failed to get remote DEV@cloud Jenkins instances"
+ (errMsg.length() > 0 ? " (" + errMsg + ")" : ""), e);
}
}
/**
* Returns currently active account. The contract is that if it returns <code>null</code> then it doesn't exist.
*
* @param monitor
* @return
*/
public String getActiveAccountName() {
return activeAccount;
/* if (accountSelectionActive) {
monitor.beginTask("Waiting for the account selection to finish", 1000);
long wait = 0;
//Wait max for 10sec
while (!accountSelectionActive && wait <= 100) {
try {
Thread.currentThread().wait(100);
} catch (InterruptedException e) {
monitor.setCanceled(true);
break;
}
wait++;
monitor.worked(10);
}
}
if (activeAccount != null) {
return activeAccount;
}
*/
//throw new CloudBeesException("Active account not selected!");
}
/**
* Returns all accounts for the user. IMPORTANT! All user functionality should work only with the active/selected user
* account. Make sure UX does not depend on the whole list of accounts. To get the active account use
* #getActiveAccounName()
*
* @param monitor
* @return
* @throws CloudBeesException
*/
public String[] getAccounts(final IProgressMonitor monitor) throws CloudBeesException {
if (!hasAuthInfo()) {
return new String[0];
}
KeysUsingAuthResponse auth = getAuthInfo(monitor).getAuth();
StringBuffer errMsg = new StringBuffer();
try {
String url = BASE_URL + "account/names";
HttpClient httpclient = Utils.getAPIClient(url);
AccountNamesRequest req = new AccountNamesRequest();
req.uid = auth.uid;
HttpPost post = Utils.jsonRequest(url, req);
HttpResponse resp = httpclient.execute(post);
String bodyResponse = Utils.getResponseBody(resp);
Gson g = Utils.createGson();
AccountNamesResponse services = g.fromJson(bodyResponse, AccountNamesResponse.class);
if (services.message != null && services.message.length() > 0) {
errMsg.append(services.message);
}
Utils.checkResponseCode(resp);
Arrays.sort(services.accounts);
accountsCache = services.accounts;
return services.accounts;
} catch (Exception e) {
throw cbthrow(errMsg.toString(), e);
}
}
@Deprecated
private String getPrimaryAccount(final IProgressMonitor monitor) throws CloudBeesException {
if (!hasAuthInfo()) {
return "";
}
KeysUsingAuthResponse auth = getAuthInfo(monitor).getAuth();
StringBuffer errMsg = new StringBuffer();
try {
String url = BASE_URL + "account/name";
HttpClient httpclient = Utils.getAPIClient(url);
AccountNameRequest req = new AccountNameRequest();
req.uid = auth.uid;
HttpPost post = Utils.jsonRequest(url, req);
HttpResponse resp = httpclient.execute(post);
String bodyResponse = Utils.getResponseBody(resp);
Gson g = Utils.createGson();
AccountNameResponse account = g.fromJson(bodyResponse, AccountNameResponse.class);
if (account.message != null && account.message.length() > 0) {
errMsg.append(account.message);
}
Utils.checkResponseCode(resp);
return account.account;
} catch (Exception e) {
throw cbthrow(errMsg.toString(), e);
}
}
public static class AuthInfo {
private final KeysUsingAuthResponse auth;
public AuthInfo(final KeysUsingAuthResponse services) {
this.auth = services;
}
public KeysUsingAuthResponse getAuth() {
return this.auth;
}
}
public String getCurrentUsername(final IProgressMonitor monitor) throws CloudBeesException {
String acc = getActiveAccountName();
if (acc == null) {
return null;
}
AccountServiceStatusResponse services = loadAccountServices(acc);
return services.username;
}
public String getEmail() {
return email;
}
public void setActiveAccount(String newname) {
activeAccount = newname;
}
public String[] getCachedAccounts() {
return accountsCache;
}
public AuthInfo getCachedAuthInfo(boolean b) throws CloudBeesException {
return getCachedAuthInfo(b, new NullProgressMonitor());
}
}