Merge branch 'master' into 'master'
Add HTTP Basic Authentication to ApkDownloader In order to download protected files, the APKDownloader must be able to supply HTTP Basic Authentication headers to the server. See merge request !178
This commit is contained in:
commit
07eb633f0f
@ -97,6 +97,7 @@ import org.fdroid.fdroid.net.Downloader;
|
||||
import java.io.File;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.fdroid.fdroid.data.Credentials;
|
||||
|
||||
interface AppDetailsData {
|
||||
App getApp();
|
||||
@ -855,8 +856,20 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
|
||||
return repo.address;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Credentials getRepoCredentials(Apk apk) {
|
||||
final String[] projection = {RepoProvider.DataColumns.USERNAME, RepoProvider.DataColumns.PASSWORD};
|
||||
Repo repo = RepoProvider.Helper.findById(this, apk.repo, projection);
|
||||
if (repo == null || repo.username == null || repo.password == null) {
|
||||
return null;
|
||||
}
|
||||
return repo.getCredentials();
|
||||
}
|
||||
|
||||
private void startDownload(Apk apk, String repoAddress) {
|
||||
downloadHandler = new ApkDownloader(getBaseContext(), app, apk, repoAddress);
|
||||
downloadHandler.setCredentials(getRepoCredentials(apk));
|
||||
|
||||
localBroadcastManager.registerReceiver(downloaderProgressReceiver,
|
||||
new IntentFilter(Downloader.LOCAL_ACTION_PROGRESS));
|
||||
downloadHandler.setProgressListener(this);
|
||||
|
@ -96,8 +96,7 @@ public class RepoUpdater {
|
||||
try {
|
||||
downloader = DownloaderFactory.create(context,
|
||||
getIndexAddress(), File.createTempFile("index-", "-downloaded", context.getCacheDir()),
|
||||
repo.username,
|
||||
repo.password
|
||||
repo.getCredentials()
|
||||
);
|
||||
downloader.setCacheTag(repo.lastetag);
|
||||
downloader.downloadUninterrupted();
|
||||
|
36
F-Droid/src/org/fdroid/fdroid/data/Credentials.java
Normal file
36
F-Droid/src/org/fdroid/fdroid/data/Credentials.java
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Christian Morgner (christian.morgner@structr.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.fdroid.fdroid.data;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
|
||||
/**
|
||||
* Credentials to authenticate HTTP requests. Implementations if this interface
|
||||
* should encapsulate the authentication of an HTTP request in the authenticate
|
||||
* method.
|
||||
*/
|
||||
public interface Credentials {
|
||||
|
||||
/**
|
||||
* Implement this method to provide authentication for the given connection.
|
||||
* @param connection the HTTP connection to authenticate
|
||||
*/
|
||||
void authenticate(final HttpURLConnection connection);
|
||||
}
|
@ -9,6 +9,7 @@ import org.fdroid.fdroid.Utils;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
import org.fdroid.fdroid.net.auth.HttpBasicCredentials;
|
||||
|
||||
public class Repo extends ValueObject {
|
||||
|
||||
@ -148,6 +149,21 @@ public class Repo extends ValueObject {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the credentials for this repo, or null of no authentication
|
||||
* method is configured.
|
||||
* @return the credentials or null
|
||||
*/
|
||||
public Credentials getCredentials() {
|
||||
|
||||
// return the only credentials implementation we have right now
|
||||
if (!TextUtils.isEmpty(username) && !TextUtils.isEmpty(password)) {
|
||||
return new HttpBasicCredentials(username, password);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setValues(ContentValues values) {
|
||||
|
||||
if (values.containsKey(RepoProvider.DataColumns._ID)) {
|
||||
|
@ -39,6 +39,7 @@ import org.fdroid.fdroid.data.SanitizedFile;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import org.fdroid.fdroid.data.Credentials;
|
||||
|
||||
/**
|
||||
* Downloads and verifies (against the Apk.hash) the apk file.
|
||||
@ -77,6 +78,7 @@ public class ApkDownloader implements AsyncDownloader.Listener {
|
||||
|
||||
private ProgressListener listener;
|
||||
private AsyncDownloader dlWrapper;
|
||||
private Credentials credentials;
|
||||
private boolean isComplete;
|
||||
|
||||
private final long id = ++downloadIdCounter;
|
||||
@ -195,7 +197,7 @@ public class ApkDownloader implements AsyncDownloader.Listener {
|
||||
Utils.debugLog(TAG, "Downloading apk from " + remoteAddress + " to " + localFile);
|
||||
|
||||
try {
|
||||
dlWrapper = DownloaderFactory.createAsync(context, remoteAddress, localFile, app.name + " " + curApk.version, curApk.id, this);
|
||||
dlWrapper = DownloaderFactory.createAsync(context, remoteAddress, localFile, app.name + " " + curApk.version, curApk.id, credentials, this);
|
||||
dlWrapper.download();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
@ -292,4 +294,8 @@ public class ApkDownloader implements AsyncDownloader.Listener {
|
||||
public int getTotalBytes() {
|
||||
return dlWrapper != null ? dlWrapper.getTotalBytes() : 0;
|
||||
}
|
||||
|
||||
public void setCredentials(final Credentials credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import org.fdroid.fdroid.Utils;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import org.fdroid.fdroid.data.Credentials;
|
||||
|
||||
public class DownloaderFactory {
|
||||
|
||||
@ -45,10 +46,10 @@ public class DownloaderFactory {
|
||||
|
||||
public static Downloader create(Context context, URL url, File destFile)
|
||||
throws IOException {
|
||||
return create(context, url, destFile, null, null);
|
||||
return create(context, url, destFile, null);
|
||||
}
|
||||
|
||||
public static Downloader create(Context context, URL url, File destFile, final String username, final String password)
|
||||
public static Downloader create(Context context, URL url, File destFile, Credentials credentials)
|
||||
throws IOException {
|
||||
if (isBluetoothAddress(url)) {
|
||||
String macAddress = url.getHost().replace("-", ":");
|
||||
@ -60,7 +61,7 @@ public class DownloaderFactory {
|
||||
if (isLocalFile(url)) {
|
||||
return new LocalFileDownloader(context, url, destFile);
|
||||
}
|
||||
return new HttpDownloader(context, url, destFile, username, password);
|
||||
return new HttpDownloader(context, url, destFile, credentials);
|
||||
}
|
||||
|
||||
private static boolean isBluetoothAddress(URL url) {
|
||||
@ -71,11 +72,11 @@ public class DownloaderFactory {
|
||||
return "file".equalsIgnoreCase(url.getProtocol());
|
||||
}
|
||||
|
||||
public static AsyncDownloader createAsync(Context context, String urlString, File destFile, String title, String id, AsyncDownloader.Listener listener) throws IOException {
|
||||
return createAsync(context, new URL(urlString), destFile, title, id, listener);
|
||||
public static AsyncDownloader createAsync(Context context, String urlString, File destFile, String title, String id, Credentials credentials, AsyncDownloader.Listener listener) throws IOException {
|
||||
return createAsync(context, new URL(urlString), destFile, title, id, credentials, listener);
|
||||
}
|
||||
|
||||
public static AsyncDownloader createAsync(Context context, URL url, File destFile, String title, String id, AsyncDownloader.Listener listener)
|
||||
public static AsyncDownloader createAsync(Context context, URL url, File destFile, String title, String id, Credentials credentials, AsyncDownloader.Listener listener)
|
||||
throws IOException {
|
||||
// To re-enable, fix the following:
|
||||
// * https://gitlab.com/fdroid/fdroidclient/issues/445
|
||||
@ -85,7 +86,7 @@ public class DownloaderFactory {
|
||||
return new AsyncDownloaderFromAndroid(context, listener, title, id, url.toString(), destFile);
|
||||
}
|
||||
Utils.debugLog(TAG, "Using AsyncDownloadWrapper");
|
||||
return new AsyncDownloadWrapper(create(context, url, destFile), listener);
|
||||
return new AsyncDownloadWrapper(create(context, url, destFile, credentials), listener);
|
||||
}
|
||||
|
||||
static boolean isOnionAddress(URL url) {
|
||||
|
@ -1,10 +1,9 @@
|
||||
package org.fdroid.fdroid.net;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
|
||||
|
||||
import org.apache.commons.net.util.Base64;
|
||||
import org.fdroid.fdroid.FDroidApp;
|
||||
import org.fdroid.fdroid.Preferences;
|
||||
import org.fdroid.fdroid.Utils;
|
||||
@ -22,6 +21,7 @@ import java.net.SocketAddress;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import org.fdroid.fdroid.data.Credentials;
|
||||
|
||||
public class HttpDownloader extends Downloader {
|
||||
private static final String TAG = "HttpDownloader";
|
||||
@ -30,21 +30,19 @@ public class HttpDownloader extends Downloader {
|
||||
protected static final String HEADER_FIELD_ETAG = "ETag";
|
||||
|
||||
protected HttpURLConnection connection;
|
||||
private final String username;
|
||||
private final String password;
|
||||
private Credentials credentials;
|
||||
private int statusCode = -1;
|
||||
|
||||
HttpDownloader(Context context, URL url, File destFile)
|
||||
throws FileNotFoundException, MalformedURLException {
|
||||
this(context, url, destFile, null, null);
|
||||
this(context, url, destFile, null);
|
||||
}
|
||||
|
||||
HttpDownloader(Context context, URL url, File destFile, final String username, final String password)
|
||||
HttpDownloader(Context context, URL url, File destFile, final Credentials credentials)
|
||||
throws FileNotFoundException, MalformedURLException {
|
||||
super(context, url, destFile);
|
||||
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,9 +98,8 @@ public class HttpDownloader extends Downloader {
|
||||
} else {
|
||||
|
||||
connection = (HttpURLConnection) sourceUrl.openConnection();
|
||||
if (!TextUtils.isEmpty(username) && !TextUtils.isEmpty(password)) {
|
||||
// add authorization header from username / password if set
|
||||
connection.setRequestProperty("Authorization", "Basic " + Base64.encodeBase64String((username + ":" + password).getBytes()));
|
||||
if (credentials != null) {
|
||||
credentials.authenticate((HttpURLConnection) connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Christian Morgner (christian.morgner@structr.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.fdroid.fdroid.net.auth;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import java.net.HttpURLConnection;
|
||||
import org.apache.commons.net.util.Base64;
|
||||
import org.fdroid.fdroid.data.Credentials;
|
||||
|
||||
/**
|
||||
* Credentials implementation for HTTP Basic Authentication.
|
||||
*/
|
||||
public class HttpBasicCredentials implements Credentials {
|
||||
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
public HttpBasicCredentials(final String username, final String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authenticate(final HttpURLConnection connection) {
|
||||
|
||||
if (!TextUtils.isEmpty(username) && !TextUtils.isEmpty(password)) {
|
||||
|
||||
// add authorization header from username / password if set
|
||||
connection.setRequestProperty("Authorization", "Basic " + Base64.encodeBase64String((username + ":" + password).getBytes()));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user