[app] Remove Provisioner as it isn't used/needed anymore

This commit is contained in:
Torsten Grote
2022-05-02 08:59:55 -03:00
committed by Hans-Christoph Steiner
parent 9f7b160986
commit 3f88fdb8cb
3 changed files with 0 additions and 435 deletions

View File

@@ -387,9 +387,6 @@ public class FDroidApp extends Application implements androidx.work.Configuratio
grantUriPermission(packageName, InstallHistoryService.LOG_URI, modeFlags);
}
// find and process provisions if any.
Provisioner.scanAndProcess(getApplicationContext());
// if the underlying OS version has changed, then fully rebuild the database
SharedPreferences atStartTime = getAtStartTimeSharedPreferences();
if (Build.VERSION.SDK_INT != atStartTime.getInt("build-version", Build.VERSION.SDK_INT)) {

View File

@@ -1,320 +0,0 @@
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.Collections;
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.
* <p>
* 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
*/
static void scanAndProcess(Context context) {
File externalFilesDir = context.getExternalFilesDir(null);
if (externalFilesDir == null) {
return;
}
File provisionDir = new File(externalFilesDir.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.isEmpty()) {
Utils.debugLog(TAG, "Provision dir is empty: '" + 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 {
if (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).");
}
}
}
private List<File> findProvisionFiles(Context context) {
File externalFilesDir = context.getExternalFilesDir(null);
if (externalFilesDir == null) {
return Collections.emptyList();
}
File provisionDir = new File(externalFilesDir.getAbsolutePath(), NEW_PROVISIONS_DIR);
return findProvisionFilesInDir(provisionDir);
}
List<File> findProvisionFilesInDir(File file) {
if (file == null || !file.isDirectory()) {
return Collections.emptyList();
}
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();
}
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 "";
}
}
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;
while ((zipEntry = in.getNextEntry()) != null) { // NOPMD Avoid assignments in operands
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;
}
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;
}
static class ProvisionPlaintext {
private String provisionPath;
private String repositoryProvision;
String getProvisionPath() {
return provisionPath;
}
void setProvisionPath(String provisionPath) {
this.provisionPath = provisionPath;
}
String getRepositoryProvision() {
return repositoryProvision;
}
void setRepositoryProvision(String repositoryProvision) {
this.repositoryProvision = repositoryProvision;
}
}
static class Provision {
private String provisonPath;
private RepositoryProvision repositoryProvision;
String getProvisonPath() {
return provisonPath;
}
void setProvisonPath(String provisonPath) {
this.provisonPath = provisonPath;
}
RepositoryProvision getRepositoryProvision() {
return repositoryProvision;
}
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;
}
}
}

View File

@@ -1,112 +0,0 @@
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 java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* @author Michael Poehn (michael.poehn@fsfe.org)
*/
@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());
}
}