licensing: connection via authenticated proxy supported (IDEA-149370)
authorEugene Zhuravlev <jeka@intellij.com>
Wed, 4 May 2016 12:23:47 +0000 (14:23 +0200)
committerEugene Zhuravlev <jeka@intellij.com>
Wed, 4 May 2016 12:26:04 +0000 (14:26 +0200)
platform/platform-api/src/com/intellij/util/net/HttpConfigurable.java
platform/platform-api/src/com/intellij/util/proxy/CommonProxy.java
platform/platform-api/src/com/intellij/util/proxy/SharedProxyConfig.java [new file with mode: 0644]

index e19bc46abb53b24546e7db3bfca5ae31d75a8f00..c5e28131e8b416ddec076d5f49b148eafce60153 100644 (file)
@@ -40,6 +40,8 @@ import com.intellij.util.WaitForProgressToShow;
 import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.proxy.CommonProxy;
 import com.intellij.util.proxy.JavaProxyProperty;
+import com.intellij.util.proxy.SharedProxyConfig;
+import com.intellij.util.xmlb.SkipDefaultsSerializationFilter;
 import com.intellij.util.xmlb.XmlSerializer;
 import com.intellij.util.xmlb.XmlSerializerUtil;
 import com.intellij.util.xmlb.annotations.Transient;
@@ -129,6 +131,32 @@ public class HttpConfigurable implements PersistentStateComponent<HttpConfigurab
 
   @Override
   public void initComponent() {
+
+    final HttpConfigurable currentState = getState();
+    if (currentState != null) {
+      final Element serialized = XmlSerializer.serializeIfNotDefault(currentState, new SkipDefaultsSerializationFilter());
+      if (serialized == null) {
+        // all settings are defaults
+        // trying user's proxy configuration entered while obtaining the license
+        final SharedProxyConfig.ProxyParameters cfg = SharedProxyConfig.load();
+        if (cfg != null) {
+          SharedProxyConfig.clear();
+          if (cfg.host != null) {
+            USE_HTTP_PROXY = true;
+            PROXY_HOST = cfg.host;
+            PROXY_PORT = cfg.port;
+            if (cfg.login != null) {
+              setPlainProxyPassword(new String(cfg.password));
+              PROXY_LOGIN = cfg.login;
+              PROXY_AUTHENTICATION = true;
+              KEEP_PROXY_PASSWORD = true;
+            }
+          }
+        }
+      }
+    }
+
+
     mySelector = new IdeaWideProxySelector(this);
     String name = getClass().getName();
     CommonProxy.getInstance().setCustom(name, mySelector);
index b7b7899bc80877efaf56ac4168e933226619890f..a6bd720489ede4439649e8159ce47e37c60993b8 100644 (file)
@@ -377,4 +377,8 @@ public class CommonProxy extends ProxySelector {
       return result;
     }
   }
+
+
+
+
 }
diff --git a/platform/platform-api/src/com/intellij/util/proxy/SharedProxyConfig.java b/platform/platform-api/src/com/intellij/util/proxy/SharedProxyConfig.java
new file mode 100644 (file)
index 0000000..4de4831
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.util.proxy;
+
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.util.io.FileUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.security.Key;
+import java.security.SecureRandom;
+import java.util.Properties;
+
+public class SharedProxyConfig {
+  private static final File CONFIG_FILE = new File(PathManager.getConfigPath(), "proxy_config");
+  private static final String HOST = "host";
+  private static final String PORT = "port";
+  private static final String LOGIN = "login";
+  private static final String PASSWORD = "password";
+  private static final Key ENCRYPTION_KEY;  // the key is valid for the same session only
+  static {
+    final byte[] bytes = new byte[16];
+    new SecureRandom().nextBytes(bytes);
+    ENCRYPTION_KEY = new SecretKeySpec(bytes, "AES");
+  }
+
+  public static final class ProxyParameters {
+    @Nullable
+    public final String host;
+    public final int port;
+    @Nullable
+    public final String login;
+    @NotNull
+    public final char[] password;
+
+    public ProxyParameters(@Nullable String host, int port) {
+      this(host, port, null, new char[0]);
+    }
+
+    public ProxyParameters(@Nullable String host, int port, @Nullable String login, @NotNull char[] password) {
+      this.host = host;
+      this.port = port;
+      this.login = login;
+      this.password = password;
+    }
+  }
+
+  public static boolean clear() {
+    return FileUtil.delete(CONFIG_FILE);
+  }
+
+  @Nullable
+  public static ProxyParameters load() {
+    try {
+      final byte[] bytes = decrypt(FileUtil.loadFileBytes(CONFIG_FILE));
+      final Properties props = new Properties();
+      props.load(new ByteArrayInputStream(bytes));
+      final String password = props.getProperty(PASSWORD, "");
+      return new ProxyParameters(
+        props.getProperty(HOST, null),
+        Integer.parseInt(props.getProperty(PORT, "0")),
+        props.getProperty(LOGIN, null),
+        password.toCharArray()
+      );
+    }
+    catch (Exception ignored) {
+    }
+    return null;
+  }
+
+  public static boolean store(@NotNull ProxyParameters params) {
+    if (params.host != null) {
+      try {
+        final Properties props = new Properties();
+        props.setProperty(HOST, params.host);
+        props.setProperty(PORT, String.valueOf(params.port));
+        if (params.login != null) {
+          props.setProperty(LOGIN, params.login);
+          props.setProperty(PASSWORD, new String(params.password));
+        }
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        props.store(out, "Proxy Configuration");
+        out.close();
+        FileUtil.writeToFile(CONFIG_FILE, encrypt(out.toByteArray()));
+        return true;
+      }
+      catch (Exception ignored) {
+      }
+    }
+    else {
+      FileUtil.delete(CONFIG_FILE);
+    }
+    return false;
+  }
+
+  private static byte[] encrypt(byte[] bytes) throws Exception {
+    return encrypt(bytes, ENCRYPTION_KEY);
+  }
+
+  private static byte[] decrypt(byte[] bytes) throws Exception {
+    return decrypt(bytes, ENCRYPTION_KEY);
+  }
+
+  private static byte[] encrypt(byte[] msgBytes, Key key) throws Exception {
+    final Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
+    ciph.init(Cipher.ENCRYPT_MODE, key);
+    final byte[] body = ciph.doFinal(msgBytes);
+    final byte[] iv = ciph.getIV();
+
+    final byte[] data = new byte[4 + iv.length + body.length];
+
+    final int length = body.length;
+    data[0] = (byte)((length >> 24)& 0xFF);
+    data[1] = (byte)((length >> 16)& 0xFF);
+    data[2] = (byte)((length >> 8)& 0xFF);
+    data[3] = (byte)(length & 0xFF);
+
+    System.arraycopy(iv, 0, data, 4, iv.length);
+    System.arraycopy(body, 0, data, 4 + iv.length, body.length);
+    return data;
+  }
+
+  private static byte[] decrypt(byte[] data, Key key) throws Exception {
+    int bodyLength = data[0] & 0xFF;
+    bodyLength = (bodyLength << 8) + data[1] & 0xFF;
+    bodyLength = (bodyLength << 8) + data[2] & 0xFF;
+    bodyLength = (bodyLength << 8) + data[3] & 0xFF;
+
+    final int ivlength = data.length - 4 - bodyLength;
+
+    final Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
+    ciph.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(data, 4, ivlength));
+    return ciph.doFinal(data, 4 + ivlength, bodyLength);
+  }
+
+}