/******************************************************************************* * Copyright (c) 2013 Zend Technologies Ltd. * 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 *******************************************************************************/ package org.zend.sdklib.internal.target; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.Set; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethodBase; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.zend.sdklib.SdkException; import org.zend.sdklib.target.InvalidCredentialsException; /** * @author Wojciech Galanciak, 2012 * */ public abstract class ApiKeyDetector { private static final String DEFAULT_KEY = "ZendStudio"; //$NON-NLS-1$ private static final String NAME = "name"; //$NON-NLS-1$ private static final String PASSWORD = "password"; //$NON-NLS-1$ private static final String USERNAME = "username"; //$NON-NLS-1$ private static final String SESSION_ID = "ZS6SESSID"; //$NON-NLS-1$ private String username; private String password; private String serverUrl; private String name; private String secretKey; public ApiKeyDetector(String username, String password, String serverUrl) { this.username = username; this.password = password; this.serverUrl = serverUrl; } public ApiKeyDetector(String username, String password) { this(username, password, null); } public ApiKeyDetector(String serverUrl) { this(null, null, serverUrl); } public boolean createApiKey(String validationMessage) throws SdkException { try { if (!isBootstrapped()) { throw new NoBootstrapException(); } if (username == null && password == null) { getServerCredentials(serverUrl, validationMessage); if (username == null || password == null) { return false; } } String sessionId = login(); if (sessionId != null) { String exisitngKeys = getApiKeys(sessionId); Map<String, String> keys = ZendTargetAutoDetect.parseApiKey(exisitngKeys); String keyName = name != null ? name : DEFAULT_KEY; if (keys.containsKey(keyName)) { name = keyName; secretKey = keys.get(keyName); return true; } String accessToken = getAccessToken(sessionId); String content = doCreateApiKey(sessionId, accessToken); Map<String, String> values = ZendTargetAutoDetect.parseApiKey(content); if (values.containsKey(keyName)) { name = keyName; secretKey = values.get(keyName); } return true; } String message = MessageFormat.format("Could not connect to Zend Server at {0}.", serverUrl); throw new SdkException(message); } finally { username = null; password = null; } } public String getKey() { return name; } public String getSecretKey() { return secretKey; } public void setKey(String key) { this.name = key; } public void setServerUrl(String serverUrl) { this.serverUrl = serverUrl; } public abstract void getServerCredentials(String serverUrl, String validationMessage); protected void setUsername(String username) { this.username = username; } protected void setPassword(String password) { this.password = password; } protected String getUsername() { return username; } protected String getPassword() { return password; } /** * Returns server access token. Valid for Zend Server 8.5.1 and higher. * * @param sessionId * @return server access token or <code>null</code>. * @throws SdkException */ private String getAccessToken(String sessionId) throws SdkException { Map<String, String> cookies = new HashMap<String, String>(); cookies.put(SESSION_ID, sessionId); String accessToken = null; HttpClient client = new HttpClient(); HttpMethodBase method = new GetMethod(serverUrl); setCookies(method, cookies); try { int statusCode = client.executeMethod(method); if(statusCode != 200){ String message = MessageFormat.format("Could not execute method: '{0}'. Returned status code: '{1}'", method.getURI().toString(), statusCode); throw new SdkException(message); } InputStream responseBody = method.getResponseBodyAsStream(); BufferedReader in = new BufferedReader(new InputStreamReader(responseBody)); // looking for: var csrf = '<128-lenght-combinantion-of-chars>' String line = null; @SuppressWarnings("unused") int index = -1; while ((line = in.readLine()) != null) { if ((index = line.indexOf("var csrf")) == -1) //$NON-NLS-1$ continue; int start = line.indexOf("'"); //$NON-NLS-1$ int end = line.lastIndexOf("'"); //$NON-NLS-1$ return line.substring(start + 1, end); } } catch (IOException e) { throw new SdkException("Could not obtain server access token", e); } finally { method.releaseConnection(); } return accessToken; } private String login() throws SdkException { Map<String, String> params = new HashMap<String, String>(); params.put(USERNAME, username); params.put(PASSWORD, password); String url = getUrl("/Login"); //$NON-NLS-1$ return executeLogin(url, params); } private String doCreateApiKey(String sessionId, String accessToken) throws SdkException { Map<String, String> params = new HashMap<String, String>(); Map<String, String> cookies = new HashMap<String, String>(); cookies.put(SESSION_ID, sessionId); params.put(NAME, name != null ? name : DEFAULT_KEY); params.put(USERNAME, "admin"); //$NON-NLS-1$ return executeAddApiKey(getUrl("/Api/apiKeysAddKey"), params, cookies, accessToken); //$NON-NLS-1$ } private String getApiKeys(String sessionId) throws SdkException { Map<String, String> cookies = new HashMap<String, String>(); cookies.put(SESSION_ID, sessionId); return executeGetApiKeys(getUrl("/Api/apiKeysGetList"), cookies); //$NON-NLS-1$ } private boolean isBootstrapped() throws SdkException { HttpClient client = new HttpClient(); HttpMethodBase method = new GetMethod(getUrl("/Login")); //$NON-NLS-1$ if (method != null) { int statusCode = -1; try { statusCode = client.executeMethod(method); if (statusCode == 200) { String responseContent = new String( method.getResponseBody()); if (!responseContent.contains("BootstrapWizard")) { //$NON-NLS-1$ return true; } } } catch (IOException e) { throw new SdkException(e); } finally { method.releaseConnection(); } } return false; } private String getUrl(String suffix) { return serverUrl + suffix; } private String executeLogin(String url, Map<String, String> params) throws SdkException { HttpClient client = new HttpClient(); HttpMethodBase method = createPostRequest(url, params); if (method != null) { int statusCode = -1; try { statusCode = client.executeMethod(method); if (statusCode == 302) { Header sessionId = method.getResponseHeader("Set-Cookie"); //$NON-NLS-1$ String value = sessionId.getValue(); String[] segments = value.split(";"); //$NON-NLS-1$ String currentValue = null; for (String segment : segments) { String[] parts = segment.split(","); //$NON-NLS-1$ for (String part : parts) { if (part.trim().startsWith(SESSION_ID)) { String[] id = part.split("="); //$NON-NLS-1$ if (id.length > 1) { currentValue = id[1].trim(); } } } } return currentValue; } else if (statusCode == 200) { throw new InvalidCredentialsException(); } } catch (IOException e) { throw new SdkException(e); } finally { method.releaseConnection(); } } return null; } private String executeAddApiKey(String url, Map<String, String> params, Map<String, String> cookies, String accessToken) throws SdkException { HttpClient client = new HttpClient(); HttpMethodBase method = createPostRequest(url, params); setCookies(method, cookies); method.setRequestHeader("X-Accept", //$NON-NLS-1$ "application/vnd.zend.serverapi+json;version=1.3;q=1.0"); //$NON-NLS-1$ method.setRequestHeader("X-Request", "JSON"); //$NON-NLS-1$ //$NON-NLS-2$ if (accessToken != null) { method.setRequestHeader("Access-Token", accessToken); //$NON-NLS-1$ } if (method != null) { int statusCode = -1; try { statusCode = client.executeMethod(method); if (statusCode == 200) { String responseContent = new String( method.getResponseBody()); return responseContent; } else if (statusCode == 500) { String val = params.remove(NAME); params.put(NAME, val + new Random().nextInt()); return executeAddApiKey(url, params, cookies, accessToken); } } catch (IOException e) { throw new SdkException(e); } finally { method.releaseConnection(); } } return null; } private String executeGetApiKeys(String url, Map<String, String> cookies) throws SdkException { HttpClient client = new HttpClient(); HttpMethodBase method = createGetRequest(url, new HashMap<String, String>()); setCookies(method, cookies); method.setRequestHeader("X-Accept", //$NON-NLS-1$ "application/vnd.zend.serverapi+json;version=1.3;q=1.0"); //$NON-NLS-1$ method.setRequestHeader("X-Request", "JSON"); //$NON-NLS-1$ //$NON-NLS-2$ if (method != null) { int statusCode = -1; try { statusCode = client.executeMethod(method); if (statusCode == 200) { String responseContent = new String( method.getResponseBody()); return responseContent; } } catch (IOException e) { throw new SdkException(e); } finally { method.releaseConnection(); } } return null; } private HttpMethodBase createPostRequest(String url, Map<String, String> params) { PostMethod method = new PostMethod(url); Set<String> keyList = params.keySet(); for (String key : keyList) { method.addParameter(key, params.get(key)); } return method; } private HttpMethodBase createGetRequest(String url, Map<String, String> params) { GetMethod method = new GetMethod(url); NameValuePair[] query = new NameValuePair[params.size()]; Set<String> keyList = params.keySet(); int i = 0; for (String key : keyList) { query[i++] = new NameValuePair(key, params.get(key)); } method.setQueryString(query); return method; } private HttpMethodBase setCookies(HttpMethodBase method, Map<String, String> params) { if (params != null) { StringBuilder builder = new StringBuilder(); Set<String> keyList = params.keySet(); for (String key : keyList) { builder.append(key); builder.append("="); //$NON-NLS-1$ builder.append(params.get(key)); builder.append(";"); //$NON-NLS-1$ } String value = builder.toString(); if (value.length() > 0) { value = value.substring(0, value.length() - 1); method.setRequestHeader("Cookie", value); //$NON-NLS-1$ } } return method; } }