Added support for proxy auth.

This also makes the use of a Proxy instance explicit in the Getdown code,
rather than trying to thread things through the proxy-related system
properties.

It introduces a ProxyAuth SPI which allows an app installation to save proxy
credentials in whatever way it deems fit. Otherwise the credentials are unsaved
and the user effectively has to enter them every time Getdown attempts to
access the internet.

This is obviously a terrible design, but there's no good (cross-platform) way
to securely store a username and password. So anyone using this feature is
likely going to need to provide an implementation of ProxyAuth that does
whatever auth storage they deem appropriate.

Adapted from PR from @ThomasG-AI, thanks!
This commit is contained in:
Michael Bayne
2019-01-15 12:02:10 -08:00
parent dc768515d5
commit ae129fad6f
16 changed files with 409 additions and 214 deletions
@@ -8,6 +8,7 @@ package com.threerings.getdown.data;
import java.io.*;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
@@ -231,6 +232,11 @@ public class Application
}
}
/** The proxy that should be used to do HTTP downloads. This must be configured prior to using
* the application instance. Yes this is a public mutable field, no I'm not going to create a
* getter and setter just to pretend like that's not the case. */
public Proxy proxy = Proxy.NO_PROXY;
/**
* Creates an application instance which records the location of the <code>getdown.txt</code>
* configuration file from the supplied application directory.
@@ -1208,7 +1214,7 @@ public class Application
}
if (_latest != null) {
try (InputStream in = ConnectionUtil.open(_latest, 0, 0).getInputStream();
try (InputStream in = ConnectionUtil.open(proxy, _latest, 0, 0).getInputStream();
InputStreamReader reader = new InputStreamReader(in, UTF_8);
BufferedReader bin = new BufferedReader(reader)) {
for (String[] pair : Config.parsePairs(bin, Config.createOpts(false))) {
@@ -1592,7 +1598,7 @@ public class Application
log.info("Attempting to refetch '" + path + "' from '" + targetURL + "'.");
// stream the URL into our temporary file
URLConnection uconn = ConnectionUtil.open(targetURL, 0, 0);
URLConnection uconn = ConnectionUtil.open(proxy, targetURL, 0, 0);
// we have to tell Java not to use caches here, otherwise it will cache any request for
// same URL for the lifetime of this JVM (based on the URL string, not the URL object);
// if the getdown.txt file, for example, changes in the meanwhile, we would never hear
@@ -10,6 +10,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.Channels;
@@ -25,9 +26,14 @@ import static com.threerings.getdown.Log.log;
*/
public class HTTPDownloader extends Downloader
{
public HTTPDownloader (Proxy proxy)
{
_proxy = proxy;
}
@Override protected long checkSize (Resource rsrc) throws IOException
{
URLConnection conn = ConnectionUtil.open(rsrc.getRemote(), 0, 0);
URLConnection conn = ConnectionUtil.open(_proxy, rsrc.getRemote(), 0, 0);
try {
// if we're accessing our data via HTTP, we only need a HEAD request
if (conn instanceof HttpURLConnection) {
@@ -54,7 +60,7 @@ public class HTTPDownloader extends Downloader
// system property
if (true) {
// download the resource from the specified URL
URLConnection conn = ConnectionUtil.open(rsrc.getRemote(), 0, 0);
URLConnection conn = ConnectionUtil.open(_proxy, rsrc.getRemote(), 0, 0);
conn.connect();
// make sure we got a satisfactory response code
@@ -65,7 +71,6 @@ public class HTTPDownloader extends Downloader
hcon.getResponseCode());
}
}
long actualSize = conn.getContentLength();
log.info("Downloading resource", "url", rsrc.getRemote(), "size", actualSize);
long currentSize = 0L;
@@ -105,4 +110,6 @@ public class HTTPDownloader extends Downloader
}
}
}
protected final Proxy _proxy;
}
@@ -0,0 +1,32 @@
//
// Getdown - application installer, patcher and launcher
// Copyright (C) 2004-2018 Getdown authors
// https://github.com/threerings/getdown/blob/master/LICENSE
package com.threerings.getdown.spi;
/**
* A service provider interface that handles the storage of proxy credentials.
*/
public interface ProxyAuth
{
/** Credentials for a proxy server. */
public static class Credentials {
public final String username;
public final String password;
public Credentials (String username, String password) {
this.username = username;
this.password = password;
}
}
/**
* Loads the credentials for the app installed in {@code appDir}.
*/
public Credentials loadCredentials (String appDir);
/**
* Encrypts and saves the credentials for the app installed in {@code appDir}.
*/
public void saveCredentials (String appDir, String username, String password);
}
@@ -7,11 +7,12 @@ package com.threerings.getdown.util;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import com.threerings.getdown.data.SysProps;
import com.threerings.getdown.util.Base64;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -19,6 +20,7 @@ public class ConnectionUtil
{
/**
* Opens a connection to a URL, setting the authentication header if user info is present.
* @param proxy the proxy via which to perform HTTP connections.
* @param url the URL to which to open a connection.
* @param connectTimeout if {@code > 0} then a timeout, in seconds, to use when opening the
* connection. If {@code 0} is supplied, the connection timeout specified via system properties
@@ -27,10 +29,10 @@ public class ConnectionUtil
* the connection. If {@code 0} is supplied, the read timeout specified via system properties
* will be used instead.
*/
public static URLConnection open (URL url, int connectTimeout, int readTimeout)
public static URLConnection open (Proxy proxy, URL url, int connectTimeout, int readTimeout)
throws IOException
{
URLConnection conn = url.openConnection();
URLConnection conn = url.openConnection(proxy);
// configure a connect timeout, if requested
int ctimeout = connectTimeout > 0 ? connectTimeout : SysProps.connectTimeout();
@@ -63,9 +65,9 @@ public class ConnectionUtil
* present. Throws a class cast exception if the connection returned is not the right type. See
* {@link #open} for parameter documentation.
*/
public static HttpURLConnection openHttp (URL url, int connectTimeout, int readTimeout)
throws IOException
public static HttpURLConnection openHttp (
Proxy proxy, URL url, int connectTimeout, int readTimeout) throws IOException
{
return (HttpURLConnection)open(url, connectTimeout, readTimeout);
return (HttpURLConnection)open(proxy, url, connectTimeout, readTimeout);
}
}