/******************************************************************************* * Copyright (c) 2010 The Eclipse Foundation 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: * The Eclipse Foundation - initial API and implementation *******************************************************************************/ package org.eclipse.epp.internal.mpc.core.service; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URI; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Platform; import org.eclipse.epp.internal.mpc.core.MarketplaceClientCore; import org.eclipse.userstorage.IStorage; import org.eclipse.userstorage.internal.oauth.ui.SWTInternalBrowserFacade; import org.eclipse.userstorage.oauth.EclipseOAuthCredentialsProvider; import org.eclipse.userstorage.oauth.OAuthCredentialsProvider; import org.eclipse.userstorage.spi.ICredentialsProvider; import org.osgi.framework.Bundle; import org.osgi.framework.Version; class USS11OAuthStorageConfigurer extends StorageConfigurer { private static final String CLIENT_ID = "1e8b68d6e5015c2bcf8e03d44dd97fc431f17e394860f08f1a05978c"; //$NON-NLS-1$ private static final String CLIENT_SECRET = "26e9f81e5d02d4a019e042d012a861f34a446c35cb84070e80278d8e"; //$NON-NLS-1$ private static final String CLIENT_KEY = "9d08c11f742f53a2cd6348d373fd1fa0b079694199f760b315a12a"; //$NON-NLS-1$ // FIXME: these should be the uss_project_* alternatives //private static final String[] DEFAULT_MPC_SCOPES = { "profile", "uss_project_retrieve", "uss_project_update", "uss_project_delete" }; private static final String[] DEFAULT_MPC_SCOPES = { "profile", "uss_retrieve", "uss_update" }; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ static class Factory extends StorageConfigurer.Factory { @Override boolean isApplicable() { Version ussVersion = getUSSVersion(); if (ussVersion.getMajor() > 1 || (ussVersion.getMajor() == 1 && ussVersion.getMinor() >= 1)) { Bundle oauthBundle = Platform.getBundle("org.eclipse.userstorage.oauth"); //$NON-NLS-1$ if (oauthBundle == null) { return false; } return true; } return false; } @Override StorageConfigurer doCreate() { return new USS11OAuthStorageConfigurer(); } } @Override public void setShellProvider(IStorage storage, Object value) { ICredentialsProvider credentialsProvider = storage.getCredentialsProvider(); if (credentialsProvider instanceof USS11NonInteractiveOAuthCredentialsProvider) { USS11NonInteractiveOAuthCredentialsProvider p = (USS11NonInteractiveOAuthCredentialsProvider) credentialsProvider; credentialsProvider = p.getDelegate(); } if (!(credentialsProvider instanceof EclipseOAuthCredentialsProvider)) { return; } Method[] methods = EclipseOAuthCredentialsProvider.class.getMethods(); Method shellProviderMethod = null; for (Method method : methods) { if ("setShell".equals(method.getName()) && method.getParameterTypes().length == 1 //$NON-NLS-1$ && (value == null || method.getParameterTypes()[0].isInstance(value))) { shellProviderMethod = method; break; } } if (shellProviderMethod != null) { try { shellProviderMethod.invoke(credentialsProvider, value); } catch (Exception e) { MarketplaceClientCore.error(e); } } } @Override public void configure(IStorage storage) throws CoreException { ICredentialsProvider provider = createCredentialsProvider(); storage.setCredentialsProvider(provider); } ICredentialsProvider createCredentialsProvider() { return new USS11NonInteractiveOAuthCredentialsProvider(createOAuthCredentialsProvider(), URI.create("https://accounts.eclipse.org/"), //$NON-NLS-1$ decrypt(CLIENT_ID, CLIENT_KEY), decrypt(CLIENT_SECRET, CLIENT_KEY), DEFAULT_MPC_SCOPES, URI.create("http://localhost/")); //$NON-NLS-1$ } OAuthCredentialsProvider createOAuthCredentialsProvider() { EclipseOAuthCredentialsProvider oauthProvider = new EclipseOAuthCredentialsProvider(URI.create("https://accounts.eclipse.org/"), //$NON-NLS-1$ decrypt(CLIENT_ID, CLIENT_KEY), decrypt(CLIENT_SECRET, CLIENT_KEY), DEFAULT_MPC_SCOPES, URI.create("http://localhost/")); //$NON-NLS-1$ try { Field uiFacadeField = oauthProvider.getClass().getDeclaredField("uiFacade"); //$NON-NLS-1$ uiFacadeField.setAccessible(true); uiFacadeField.set(oauthProvider, new InternalUIFacade()); } catch (Exception e) { MarketplaceClientCore.error(e); } return oauthProvider; } @Override public Object setInteractive(IStorage storage, boolean interactive) throws CoreException { ICredentialsProvider credentialsProvider = storage.getCredentialsProvider(); boolean oldInteractive = setInteractive(credentialsProvider, interactive); return oldInteractive; } boolean setInteractive(ICredentialsProvider credentialsProvider, boolean interactive) { ToggleInteractive uss11Provider = (ToggleInteractive) credentialsProvider; boolean oldInteractive = uss11Provider.isInteractive(); uss11Provider.setInteractive(interactive); return oldInteractive; } @Override void restoreInteractive(IStorage storage, Object restoreValue) throws CoreException { setInteractive(storage, Boolean.TRUE.equals(restoreValue)); } boolean handleOAuthError(IStatus status) { String message = status.getMessage(); int errorStart = message.indexOf("error="); //$NON-NLS-1$ if (errorStart != -1) { int errorEnd = message.indexOf("&", errorStart); //$NON-NLS-1$ String error = message.substring(errorStart + 6, errorEnd == -1 ? message.length() : errorEnd); return handleOAuthError(status, error); } return false; } boolean handleOAuthError(IStatus status, String error) { if ("consent_required".equals(error)) { throw new OperationCanceledException("No credentials provided"); } return false; } private class InternalUIFacade extends SWTInternalBrowserFacade { @Override public void showError(String title, String description, IStatus status) { if ("org.eclipse.userstorage.oauth".equals(status.getPlugin())) { if (handleOAuthError(status)) { return; } } super.showError(title, description, status); } } private static String decrypt(String str, String key) { byte[] keyBytes = hexToBytes(key); byte[] bytes = hexToBytes(str); byte[] result = new byte[bytes.length - 1]; int j = bytes[result.length] - Byte.MIN_VALUE; crypt(bytes, result, keyBytes, result.length, j); try { return new String(result, "UTF-8"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException(e); } } private static byte[] hexToBytes(String hexStr) { int hexStrLen = hexStr.length(); if ((hexStrLen & 1) == 1) { hexStr = '0' + hexStr; hexStrLen++; } byte[] out = new byte[hexStrLen / 2]; // Safe to assume the string is even length byte b1, b2; for (int i = 0; i < hexStrLen; i += 2) { b1 = (byte) Character.digit(hexStr.charAt(i), 16); b2 = (byte) Character.digit(hexStr.charAt(i + 1), 16); if (b1 < 0 || b2 < 0) { throw new NumberFormatException(hexStr); } out[i / 2] = (byte) (b1 << 4 | b2 & 0xff); } return out; } private static void crypt(byte[] bytes, byte[] result, byte[] key, int length, int j) { for (int i = 0; i < length; i++) { result[i] = (byte) (bytes[i] ^ key[j++ % key.length]); } } static interface ToggleInteractive { void setInteractive(boolean interactive); boolean isInteractive(); } }