Merge branch 'provisioning' into 'master'
file based provisioning + authentication support in add repo Intents See merge request fdroid/fdroidclient!630
This commit is contained in:
commit
6fe1fa581f
@ -410,6 +410,9 @@ public class FDroidApp extends Application {
|
|||||||
}
|
}
|
||||||
grantUriPermission(packageName, InstallHistoryService.LOG_URI, modeFlags);
|
grantUriPermission(packageName, InstallHistoryService.LOG_URI, modeFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// find and process provisions if any.
|
||||||
|
Provisioner.scanAndProcess(getApplicationContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
309
app/src/main/java/org/fdroid/fdroid/Provisioner.java
Normal file
309
app/src/main/java/org/fdroid/fdroid/Provisioner.java
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
package org.fdroid.fdroid;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Base64;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.fdroid.fdroid.data.Repo;
|
||||||
|
import org.fdroid.fdroid.data.RepoProvider;
|
||||||
|
import org.fdroid.fdroid.views.ManageReposActivity;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael Pöhn (michael.poehn@fsfe.org)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("LineLength")
|
||||||
|
public class Provisioner {
|
||||||
|
|
||||||
|
public static final String TAG = "Provisioner";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the name of the subfolder in the file directory of this app
|
||||||
|
* where {@link Provisioner} looks for new provisions.
|
||||||
|
*
|
||||||
|
* eg. in the Emulator (API level 24): /data/user/0/org.fdroid.fdroid.debug/files/provisions
|
||||||
|
*/
|
||||||
|
private static final String NEW_PROVISIONS_DIR = "provisions";
|
||||||
|
|
||||||
|
protected Provisioner() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* search for provision files and process them
|
||||||
|
*/
|
||||||
|
public static void scanAndProcess(Context context) {
|
||||||
|
|
||||||
|
File provisionDir = new File(context.getExternalFilesDir(null).getAbsolutePath(), NEW_PROVISIONS_DIR);
|
||||||
|
|
||||||
|
if (!provisionDir.isDirectory()) {
|
||||||
|
Utils.debugLog(TAG, "Provisions dir does not exists: '" + provisionDir.getAbsolutePath() + "' moving on ...");
|
||||||
|
} else if (provisionDir.list().length == 0) {
|
||||||
|
Utils.debugLog(TAG, "Provisions dir is empty: '" + provisionDir.getAbsolutePath() + "' moving on ...");
|
||||||
|
} else {
|
||||||
|
|
||||||
|
Provisioner p = new Provisioner();
|
||||||
|
List<File> files = p.findProvisionFiles(context);
|
||||||
|
List<ProvisionPlaintext> plaintexts = p.extractProvisionsPlaintext(files);
|
||||||
|
List<Provision> provisions = p.parseProvisions(plaintexts);
|
||||||
|
|
||||||
|
if (provisions == null || provisions.size() == 0) {
|
||||||
|
Utils.debugLog(TAG, "Provision dir does not contain any provisions: '" + provisionDir.getAbsolutePath() + "' moving on ...");
|
||||||
|
} else {
|
||||||
|
int cleanupCounter = 0;
|
||||||
|
for (Provision provision : provisions) {
|
||||||
|
if (provision.getRepositoryProvision() != null) {
|
||||||
|
RepositoryProvision repo = provision.getRepositoryProvision();
|
||||||
|
|
||||||
|
Repo storedRepo = RepoProvider.Helper.findByAddress(context, repo.getUrl());
|
||||||
|
if (storedRepo != null) {
|
||||||
|
Utils.debugLog(TAG, "Provision contains a repo which is already added: '" + provision.getProvisonPath() + "' ignoring ...");
|
||||||
|
} else {
|
||||||
|
// Note: only the last started activity will visible to users.
|
||||||
|
// All other prompting attempts will be lost.
|
||||||
|
Uri origUrl = Uri.parse(repo.getUrl());
|
||||||
|
Uri.Builder data = new Uri.Builder();
|
||||||
|
data.scheme(origUrl.getScheme());
|
||||||
|
data.encodedAuthority(Uri.encode(repo.getUsername()) + ":" + Uri.encode(repo.getPassword()) + "@" + Uri.encode(origUrl.getAuthority()));
|
||||||
|
data.path(origUrl.getPath());
|
||||||
|
data.appendQueryParameter("fingerprint", repo.getSigfp());
|
||||||
|
Intent i = new Intent(context, ManageReposActivity.class);
|
||||||
|
i.setData(data.build());
|
||||||
|
context.startActivity(i);
|
||||||
|
Utils.debugLog(TAG, "Provision processed: '" + provision.getProvisonPath() + "' prompted user ...");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove provision file
|
||||||
|
try {
|
||||||
|
new File(provision.getProvisonPath()).delete();
|
||||||
|
cleanupCounter++;
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
// ignore this exception
|
||||||
|
Utils.debugLog(TAG, "Removing provision not possible: " + e.getMessage() + " ()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Utils.debugLog(TAG, "Provisions done, removed " + cleanupCounter + " provision(s).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<File> findProvisionFiles(Context context) {
|
||||||
|
String provisionDirPath = context.getExternalFilesDir(null).getAbsolutePath() + File.separator + NEW_PROVISIONS_DIR;
|
||||||
|
return findProvisionFilesInDir(new File(provisionDirPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<File> findProvisionFilesInDir(File file) {
|
||||||
|
if (file == null || !file.isDirectory()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
File[] files = file.listFiles(new FilenameFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File dir, String name) {
|
||||||
|
if (name != null && name.endsWith(".fdrp")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return files != null ? Arrays.asList(files) : null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Utils.debugLog(TAG, "can not search for provisions, can not access: " + file.getAbsolutePath(), e);
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String rot13(String text) {
|
||||||
|
StringBuilder sb = new StringBuilder(text.length());
|
||||||
|
for (int i = 0; i < text.length(); i++) {
|
||||||
|
char c = text.charAt(i);
|
||||||
|
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M')) {
|
||||||
|
sb.append((char) (c + 13));
|
||||||
|
} else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z')) {
|
||||||
|
sb.append((char) (c - 13));
|
||||||
|
} else {
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String deobfuscate(String obfuscated) {
|
||||||
|
try {
|
||||||
|
return new String(Base64.decode(rot13(obfuscated), Base64.DEFAULT), "UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
// encoding is defined to be utf8, continue gracefully if this magically fails.
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<ProvisionPlaintext> extractProvisionsPlaintext(List<File> files) {
|
||||||
|
List<ProvisionPlaintext> result = new ArrayList<>();
|
||||||
|
if (files != null) {
|
||||||
|
for (File file : files) {
|
||||||
|
ProvisionPlaintext plain = new ProvisionPlaintext();
|
||||||
|
plain.setProvisionPath(file.getAbsolutePath());
|
||||||
|
ZipInputStream in = null;
|
||||||
|
try {
|
||||||
|
in = new ZipInputStream(new FileInputStream(file));
|
||||||
|
ZipEntry zipEntry = null;
|
||||||
|
while ((zipEntry = in.getNextEntry()) != null) {
|
||||||
|
String name = zipEntry.getName();
|
||||||
|
if ("repo_provision.json".equals(name)) {
|
||||||
|
if (plain.getRepositoryProvision() != null) {
|
||||||
|
throw new IOException("provision malformed: contains more than one repo provision file.");
|
||||||
|
}
|
||||||
|
plain.setRepositoryProvision(IOUtils.toString(in, Charset.forName("UTF-8")));
|
||||||
|
} else if ("repo_provision.ojson".equals(name)) {
|
||||||
|
if (plain.getRepositoryProvision() != null) {
|
||||||
|
throw new IOException("provision malformed: contains more than one repo provision file.");
|
||||||
|
}
|
||||||
|
plain.setRepositoryProvision(deobfuscate(IOUtils.toString(in, Charset.forName("UTF-8"))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Utils.debugLog(TAG, String.format("finding provision '%s' failed", file.getPath()), e);
|
||||||
|
continue;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Utils.debugLog(TAG, String.format("reading provision '%s' failed", file.getPath()), e);
|
||||||
|
continue;
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(plain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Provision> parseProvisions(List<ProvisionPlaintext> provisionPlaintexts) {
|
||||||
|
|
||||||
|
List<Provision> provisions = new ArrayList<>();
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
if (provisionPlaintexts != null) {
|
||||||
|
for (ProvisionPlaintext provisionPlaintext : provisionPlaintexts) {
|
||||||
|
Provision provision = new Provision();
|
||||||
|
provision.setProvisonPath(provisionPlaintext.getProvisionPath());
|
||||||
|
try {
|
||||||
|
provision.setRepositoryProvision(
|
||||||
|
mapper.readValue(provisionPlaintext.getRepositoryProvision(), RepositoryProvision.class));
|
||||||
|
provisions.add(provision);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Utils.debugLog(TAG, "could not parse repository provision", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return provisions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ProvisionPlaintext {
|
||||||
|
private String provisionPath;
|
||||||
|
private String repositoryProvision;
|
||||||
|
|
||||||
|
public String getProvisionPath() {
|
||||||
|
return provisionPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProvisionPath(String provisionPath) {
|
||||||
|
this.provisionPath = provisionPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRepositoryProvision() {
|
||||||
|
return repositoryProvision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRepositoryProvision(String repositoryProvision) {
|
||||||
|
this.repositoryProvision = repositoryProvision;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Provision {
|
||||||
|
private String provisonPath;
|
||||||
|
private RepositoryProvision repositoryProvision;
|
||||||
|
|
||||||
|
public String getProvisonPath() {
|
||||||
|
return provisonPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProvisonPath(String provisonPath) {
|
||||||
|
this.provisonPath = provisonPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RepositoryProvision getRepositoryProvision() {
|
||||||
|
return repositoryProvision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRepositoryProvision(RepositoryProvision repositoryProvision) {
|
||||||
|
this.repositoryProvision = repositoryProvision;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RepositoryProvision {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private String url;
|
||||||
|
private String sigfp;
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSigfp() {
|
||||||
|
return sigfp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSigfp(String sigfp) {
|
||||||
|
this.sigfp = sigfp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,8 @@ public class NewRepoConfig {
|
|||||||
private String uriString;
|
private String uriString;
|
||||||
private String host;
|
private String host;
|
||||||
private int port = -1;
|
private int port = -1;
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
private String fingerprint;
|
private String fingerprint;
|
||||||
private String bssid;
|
private String bssid;
|
||||||
private String ssid;
|
private String ssid;
|
||||||
@ -91,6 +93,18 @@ public class NewRepoConfig {
|
|||||||
|
|
||||||
boolean isFdroidScheme = TextUtils.equals("fdroidrepo", scheme) || TextUtils.equals("fdroidrepos", scheme);
|
boolean isFdroidScheme = TextUtils.equals("fdroidrepo", scheme) || TextUtils.equals("fdroidrepos", scheme);
|
||||||
|
|
||||||
|
String userInfo = uri.getUserInfo();
|
||||||
|
if (userInfo != null) {
|
||||||
|
String[] userInfoTokens = userInfo.split(":");
|
||||||
|
if (userInfoTokens != null && userInfoTokens.length >= 2) {
|
||||||
|
username = userInfoTokens[0];
|
||||||
|
password = userInfoTokens[1];
|
||||||
|
for (int i = 2; i < userInfoTokens.length; i++) {
|
||||||
|
password += ":" + userInfoTokens[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fingerprint = uri.getQueryParameter("fingerprint");
|
fingerprint = uri.getQueryParameter("fingerprint");
|
||||||
bssid = uri.getQueryParameter("bssid");
|
bssid = uri.getQueryParameter("bssid");
|
||||||
ssid = uri.getQueryParameter("ssid");
|
ssid = uri.getQueryParameter("ssid");
|
||||||
@ -133,6 +147,14 @@ public class NewRepoConfig {
|
|||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
public String getFingerprint() {
|
public String getFingerprint() {
|
||||||
return fingerprint;
|
return fingerprint;
|
||||||
}
|
}
|
||||||
@ -157,9 +179,11 @@ public class NewRepoConfig {
|
|||||||
public static String sanitizeRepoUri(Uri uri) {
|
public static String sanitizeRepoUri(Uri uri) {
|
||||||
String scheme = uri.getScheme();
|
String scheme = uri.getScheme();
|
||||||
String host = uri.getHost();
|
String host = uri.getHost();
|
||||||
|
String userInfo = uri.getUserInfo();
|
||||||
return uri.toString()
|
return uri.toString()
|
||||||
.replaceAll("\\?.*$", "") // remove the whole query
|
.replaceAll("\\?.*$", "") // remove the whole query
|
||||||
.replaceAll("/*$", "") // remove all trailing slashes
|
.replaceAll("/*$", "") // remove all trailing slashes
|
||||||
|
.replace(userInfo + "@", "") // remove user authentication
|
||||||
.replace(host, host.toLowerCase(Locale.ENGLISH))
|
.replace(host, host.toLowerCase(Locale.ENGLISH))
|
||||||
.replace(scheme, scheme.toLowerCase(Locale.ENGLISH))
|
.replace(scheme, scheme.toLowerCase(Locale.ENGLISH))
|
||||||
.replace("fdroidrepo", "http") // proper repo address
|
.replace("fdroidrepo", "http") // proper repo address
|
||||||
|
@ -162,6 +162,8 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana
|
|||||||
ClipboardCompat clipboard = ClipboardCompat.create(this);
|
ClipboardCompat clipboard = ClipboardCompat.create(this);
|
||||||
String text = clipboard.getText();
|
String text = clipboard.getText();
|
||||||
String fingerprint = null;
|
String fingerprint = null;
|
||||||
|
String username = null;
|
||||||
|
String password = null;
|
||||||
if (!TextUtils.isEmpty(text)) {
|
if (!TextUtils.isEmpty(text)) {
|
||||||
try {
|
try {
|
||||||
new URL(text);
|
new URL(text);
|
||||||
@ -171,6 +173,19 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana
|
|||||||
if (TextUtils.isEmpty(fingerprint)) {
|
if (TextUtils.isEmpty(fingerprint)) {
|
||||||
fingerprint = uri.getQueryParameter("FINGERPRINT");
|
fingerprint = uri.getQueryParameter("FINGERPRINT");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String userInfo = uri.getUserInfo();
|
||||||
|
if (userInfo != null) {
|
||||||
|
String[] userInfoTokens = userInfo.split(":");
|
||||||
|
if (userInfoTokens.length >= 2) {
|
||||||
|
username = userInfoTokens[0];
|
||||||
|
password = userInfoTokens[1];
|
||||||
|
for (int i = 2; i < userInfoTokens.length; i++) {
|
||||||
|
password += ":" + userInfoTokens[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
text = NewRepoConfig.sanitizeRepoUri(uri);
|
text = NewRepoConfig.sanitizeRepoUri(uri);
|
||||||
} catch (MalformedURLException e) {
|
} catch (MalformedURLException e) {
|
||||||
text = null;
|
text = null;
|
||||||
@ -180,11 +195,11 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana
|
|||||||
if (TextUtils.isEmpty(text)) {
|
if (TextUtils.isEmpty(text)) {
|
||||||
text = DEFAULT_NEW_REPO_TEXT;
|
text = DEFAULT_NEW_REPO_TEXT;
|
||||||
}
|
}
|
||||||
showAddRepo(text, fingerprint);
|
showAddRepo(text, fingerprint, username, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showAddRepo(String newAddress, String newFingerprint) {
|
private void showAddRepo(String newAddress, String newFingerprint, String username, String password) {
|
||||||
new AddRepo(newAddress, newFingerprint);
|
new AddRepo(newAddress, newFingerprint, username, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -206,7 +221,7 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana
|
|||||||
|
|
||||||
private AddRepoState addRepoState;
|
private AddRepoState addRepoState;
|
||||||
|
|
||||||
AddRepo(String newAddress, String newFingerprint) {
|
AddRepo(String newAddress, String newFingerprint, final String username, final String password) {
|
||||||
|
|
||||||
context = ManageReposActivity.this;
|
context = ManageReposActivity.this;
|
||||||
|
|
||||||
@ -270,14 +285,14 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana
|
|||||||
|
|
||||||
switch (addRepoState) {
|
switch (addRepoState) {
|
||||||
case DOESNT_EXIST:
|
case DOESNT_EXIST:
|
||||||
prepareToCreateNewRepo(url, fp);
|
prepareToCreateNewRepo(url, fp, username, password);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IS_SWAP:
|
case IS_SWAP:
|
||||||
Utils.debugLog(TAG, "Removing existing swap repo " + url + " before adding new repo.");
|
Utils.debugLog(TAG, "Removing existing swap repo " + url + " before adding new repo.");
|
||||||
Repo repo = RepoProvider.Helper.findByAddress(context, url);
|
Repo repo = RepoProvider.Helper.findByAddress(context, url);
|
||||||
RepoProvider.Helper.remove(context, repo.getId());
|
RepoProvider.Helper.remove(context, repo.getId());
|
||||||
prepareToCreateNewRepo(url, fp);
|
prepareToCreateNewRepo(url, fp, username, password);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EXISTS_DISABLED:
|
case EXISTS_DISABLED:
|
||||||
@ -430,7 +445,7 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana
|
|||||||
/**
|
/**
|
||||||
* Adds a new repo to the database.
|
* Adds a new repo to the database.
|
||||||
*/
|
*/
|
||||||
private void prepareToCreateNewRepo(final String originalAddress, final String fingerprint) {
|
private void prepareToCreateNewRepo(final String originalAddress, final String fingerprint, final String username, final String password) {
|
||||||
|
|
||||||
addRepoDialog.findViewById(R.id.add_repo_form).setVisibility(View.GONE);
|
addRepoDialog.findViewById(R.id.add_repo_form).setVisibility(View.GONE);
|
||||||
addRepoDialog.getButton(AlertDialog.BUTTON_POSITIVE).setVisibility(View.GONE);
|
addRepoDialog.getButton(AlertDialog.BUTTON_POSITIVE).setVisibility(View.GONE);
|
||||||
@ -504,6 +519,13 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana
|
|||||||
final EditText nameInput = (EditText) view.findViewById(R.id.edit_name);
|
final EditText nameInput = (EditText) view.findViewById(R.id.edit_name);
|
||||||
final EditText passwordInput = (EditText) view.findViewById(R.id.edit_password);
|
final EditText passwordInput = (EditText) view.findViewById(R.id.edit_password);
|
||||||
|
|
||||||
|
if (username != null) {
|
||||||
|
nameInput.setText(username);
|
||||||
|
}
|
||||||
|
if (password != null) {
|
||||||
|
passwordInput.setText(password);
|
||||||
|
}
|
||||||
|
|
||||||
credentialsDialog.setTitle(R.string.login_title);
|
credentialsDialog.setTitle(R.string.login_title);
|
||||||
credentialsDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
|
credentialsDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
|
||||||
getString(R.string.cancel),
|
getString(R.string.cancel),
|
||||||
@ -661,7 +683,7 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana
|
|||||||
NewRepoConfig newRepoConfig = new NewRepoConfig(this, intent);
|
NewRepoConfig newRepoConfig = new NewRepoConfig(this, intent);
|
||||||
if (newRepoConfig.isValidRepo()) {
|
if (newRepoConfig.isValidRepo()) {
|
||||||
isImportingRepo = true;
|
isImportingRepo = true;
|
||||||
showAddRepo(newRepoConfig.getRepoUriString(), newRepoConfig.getFingerprint());
|
showAddRepo(newRepoConfig.getRepoUriString(), newRepoConfig.getFingerprint(), newRepoConfig.getUsername(), newRepoConfig.getPassword());
|
||||||
checkIfNewRepoOnSameWifi(newRepoConfig);
|
checkIfNewRepoOnSameWifi(newRepoConfig);
|
||||||
} else if (newRepoConfig.getErrorMessage() != null) {
|
} else if (newRepoConfig.getErrorMessage() != null) {
|
||||||
Toast.makeText(this, newRepoConfig.getErrorMessage(), Toast.LENGTH_LONG).show();
|
Toast.makeText(this, newRepoConfig.getErrorMessage(), Toast.LENGTH_LONG).show();
|
||||||
|
114
app/src/test/java/org/fdroid/fdroid/ProvisionerTest.java
Normal file
114
app/src/test/java/org/fdroid/fdroid/ProvisionerTest.java
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
package org.fdroid.fdroid;
|
||||||
|
|
||||||
|
import org.fdroid.fdroid.shadows.ShadowLog;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Michael Poehn (michael.poehn@fsfe.org)
|
||||||
|
*/
|
||||||
|
@Config(constants = BuildConfig.class, sdk = 24)
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@SuppressWarnings("LineLength")
|
||||||
|
public class ProvisionerTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
ShadowLog.stream = System.out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void provisionLookup() throws IOException {
|
||||||
|
// wired hack for getting resource dir path ...
|
||||||
|
String resourceDir = getResourceFile(
|
||||||
|
"demo_credentials_user1.fdrp").getParent();
|
||||||
|
|
||||||
|
Provisioner p = new Provisioner();
|
||||||
|
List<File> files = p.findProvisionFilesInDir(new File(resourceDir));
|
||||||
|
|
||||||
|
List<String> expectedFilenames = Arrays.asList(
|
||||||
|
"demo_credentials_user1.fdrp",
|
||||||
|
"demo_credentials_user2.fdrp");
|
||||||
|
|
||||||
|
Assert.assertEquals(2, files.size());
|
||||||
|
for (File f : files) {
|
||||||
|
Assert.assertTrue("unexpected file name " + f.getName(), expectedFilenames.contains(f.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rot13() {
|
||||||
|
Provisioner p = new Provisioner();
|
||||||
|
String result = p.rot13("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890{}\"':=");
|
||||||
|
Assert.assertEquals("nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM1234567890{}\"':=", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void deobfuscate() {
|
||||||
|
Provisioner p = new Provisioner();
|
||||||
|
String result = p.deobfuscate("rlWVMKWuL2kcqUImVwbaGz90nTyhMlOyozE1pzImVTW1qPOwnTShM2HhWljXVPNtVPq3nTIhWmbtJlWuLz91qPN1ZQNtDv5QYvWqsD==");
|
||||||
|
Assert.assertEquals("{\"Heraclitus\":'Nothing endures but change.',\n 'when': [\"about 500 B.C.\"]}", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void extractProvisionsPlaintextUnobfuscated() throws IOException {
|
||||||
|
Provisioner p = new Provisioner();
|
||||||
|
List<File> files = Arrays.asList(getResourceFile("demo_credentials_user2.fdrp"));
|
||||||
|
List<Provisioner.ProvisionPlaintext> result = p.extractProvisionsPlaintext(files);
|
||||||
|
|
||||||
|
Assert.assertEquals(result.size(), 1);
|
||||||
|
Assert.assertEquals("{\"username\": \"user2\", \"password\": \"other secret\", \"name\": \"Example Repo\", \"url\": \"https://example.com/fdroid/repo\", \"sigfp\": \"1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff\"}", result.get(0).getRepositoryProvision());
|
||||||
|
Assert.assertTrue(String.valueOf(result.get(0).getProvisionPath()).endsWith("demo_credentials_user2.fdrp"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void extractProvisionsPlaintextObfuscated() throws IOException {
|
||||||
|
Provisioner p = new Provisioner();
|
||||||
|
List<File> files = Arrays.asList(getResourceFile("demo_credentials_user1.fdrp"));
|
||||||
|
List<Provisioner.ProvisionPlaintext> result = p.extractProvisionsPlaintext(files);
|
||||||
|
|
||||||
|
Assert.assertEquals(result.size(), 1);
|
||||||
|
Assert.assertEquals("{\"sigfp\": \"1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff\", \"name\": \"Example Repo\", \"password\": \"secret1\", \"url\": \"https://example.com/fdroid/repo\", \"username\": \"user1\"}", result.get(0).getRepositoryProvision());
|
||||||
|
Assert.assertTrue(String.valueOf(result.get(0).getProvisionPath()).endsWith("demo_credentials_user1.fdrp"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseProvisions() {
|
||||||
|
|
||||||
|
List<Provisioner.ProvisionPlaintext> plaintexts = Arrays.asList(new Provisioner.ProvisionPlaintext(), new Provisioner.ProvisionPlaintext());
|
||||||
|
plaintexts.get(0).setProvisionPath("/some/dir/abc.fdrp");
|
||||||
|
plaintexts.get(0).setRepositoryProvision("{\"username\": \"user1\", \"password\": \"secret1\", \"name\": \"test repo a\", \"url\": \"https://example.com/fdroid/repo\", \"sigfp\": \"1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff\"}");
|
||||||
|
plaintexts.get(1).setProvisionPath("/some/dir/def.fdrp");
|
||||||
|
plaintexts.get(1).setRepositoryProvision("{\"username\": \"user2\", \"name\": \"test repo a\", \"password\": \"other secret\", \"url\": \"https://example.com/fdroid/repo\", \"sigfp\": \"1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff\"}");
|
||||||
|
|
||||||
|
Provisioner p = new Provisioner();
|
||||||
|
List<Provisioner.Provision> result = p.parseProvisions(plaintexts);
|
||||||
|
|
||||||
|
Assert.assertEquals("/some/dir/abc.fdrp", result.get(0).getProvisonPath());
|
||||||
|
Assert.assertEquals("test repo a", result.get(0).getRepositoryProvision().getName());
|
||||||
|
Assert.assertEquals("https://example.com/fdroid/repo", result.get(0).getRepositoryProvision().getUrl());
|
||||||
|
Assert.assertEquals("1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff", result.get(0).getRepositoryProvision().getSigfp());
|
||||||
|
Assert.assertEquals("user1", result.get(0).getRepositoryProvision().getUsername());
|
||||||
|
Assert.assertEquals("secret1", result.get(0).getRepositoryProvision().getPassword());
|
||||||
|
|
||||||
|
Assert.assertEquals("/some/dir/def.fdrp", result.get(1).getProvisonPath());
|
||||||
|
Assert.assertEquals("test repo a", result.get(1).getRepositoryProvision().getName());
|
||||||
|
Assert.assertEquals("https://example.com/fdroid/repo", result.get(1).getRepositoryProvision().getUrl());
|
||||||
|
Assert.assertEquals("1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff", result.get(1).getRepositoryProvision().getSigfp());
|
||||||
|
Assert.assertEquals("user2", result.get(1).getRepositoryProvision().getUsername());
|
||||||
|
Assert.assertEquals("other secret", result.get(1).getRepositoryProvision().getPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getResourceFile(String resourceFileName) {
|
||||||
|
return new File(getClass().getClassLoader().getResource(resourceFileName).getPath());
|
||||||
|
}
|
||||||
|
}
|
BIN
app/src/test/resources/demo_credentials_user1.fdrp
Normal file
BIN
app/src/test/resources/demo_credentials_user1.fdrp
Normal file
Binary file not shown.
BIN
app/src/test/resources/demo_credentials_user2.fdrp
Normal file
BIN
app/src/test/resources/demo_credentials_user2.fdrp
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user