mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-04-20 22:09:12 +00:00
Add Proton Pass importer
This commit is contained in:
parent
2050d29236
commit
34550fff35
5 changed files with 171 additions and 0 deletions
|
@ -44,6 +44,7 @@ public abstract class DatabaseImporter {
|
|||
_importers.add(new Definition("Google Authenticator", GoogleAuthImporter.class, R.string.importer_help_google_authenticator, true));
|
||||
_importers.add(new Definition("Microsoft Authenticator", MicrosoftAuthImporter.class, R.string.importer_help_microsoft_authenticator, true));
|
||||
_importers.add(new Definition("Plain text", GoogleAuthUriImporter.class, R.string.importer_help_plain_text, false));
|
||||
_importers.add(new Definition("Proton Pass", ProtonPassImporter.class, R.string.importer_help_proton_pass, true));
|
||||
_importers.add(new Definition("Steam", SteamImporter.class, R.string.importer_help_steam, true));
|
||||
_importers.add(new Definition("TOTP Authenticator", TotpAuthenticatorImporter.class, R.string.importer_help_totp_authenticator, true));
|
||||
_importers.add(new Definition("WinAuth", WinAuthImporter.class, R.string.importer_help_winauth, false));
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
package com.beemdevelopment.aegis.importers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.beemdevelopment.aegis.otp.GoogleAuthInfo;
|
||||
import com.beemdevelopment.aegis.otp.GoogleAuthInfoException;
|
||||
import com.beemdevelopment.aegis.vault.VaultEntry;
|
||||
import com.beemdevelopment.aegis.vault.VaultGroup;
|
||||
import com.topjohnwu.superuser.io.SuFile;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONTokener;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Iterator;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class ProtonPassImporter extends DatabaseImporter {
|
||||
public ProtonPassImporter(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SuFile getAppPath() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected State read(InputStream stream, boolean isInternal) throws DatabaseImporterException {
|
||||
// Unzip
|
||||
ZipInputStream zis = new ZipInputStream(stream);
|
||||
|
||||
// Read file from zip
|
||||
ZipEntry zipEntry;
|
||||
try {
|
||||
while((zipEntry = zis.getNextEntry()) != null)
|
||||
{
|
||||
if(!zipEntry.getName().equals("Proton Pass/data.json"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read file
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(zis));
|
||||
StringBuilder json = new StringBuilder();
|
||||
String line;
|
||||
while((line = br.readLine()) != null){
|
||||
json.append(line);
|
||||
}
|
||||
br.close();
|
||||
|
||||
// Parse JSON
|
||||
JSONTokener tokener = new JSONTokener(json.toString());
|
||||
JSONObject jsonObject = new JSONObject(tokener);
|
||||
|
||||
return new State(jsonObject);
|
||||
}
|
||||
}catch (IOException | JSONException e)
|
||||
{
|
||||
throw new DatabaseImporterException(e);
|
||||
}
|
||||
|
||||
//Json not found
|
||||
throw new DatabaseImporterException("Invalid proton zip file");
|
||||
}
|
||||
|
||||
public static class State extends DatabaseImporter.State {
|
||||
private JSONObject _jsonObject;
|
||||
|
||||
private State(JSONObject jsonObject)
|
||||
{
|
||||
super(false);
|
||||
_jsonObject = jsonObject;
|
||||
}
|
||||
|
||||
public Result convert() throws DatabaseImporterException {
|
||||
Result result = new Result();
|
||||
|
||||
try {
|
||||
JSONObject vaults = this._jsonObject.getJSONObject("vaults");
|
||||
Iterator<String> keys = vaults.keys();
|
||||
|
||||
// Iterate over vaults
|
||||
while (keys.hasNext())
|
||||
{
|
||||
JSONObject vault = vaults.getJSONObject(keys.next());
|
||||
JSONArray items = vault.getJSONArray("items");
|
||||
|
||||
//Create a new group
|
||||
VaultGroup group = new VaultGroup(vault.getString("name"));
|
||||
result.addGroup(group);
|
||||
|
||||
// Iterate over items on the vault
|
||||
for(int j = 0; j < items.length(); j++)
|
||||
{
|
||||
JSONObject item = items.getJSONObject(j);
|
||||
|
||||
try{
|
||||
VaultEntry entry = this.fromItem(item);
|
||||
|
||||
if(entry == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.addGroup(group.getUUID());
|
||||
result.addEntry(entry);
|
||||
}catch (JSONException | GoogleAuthInfoException e)
|
||||
{
|
||||
result.addError(new DatabaseImporterEntryException(e, "Can't import " + item.getString("itemId")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}catch (JSONException e)
|
||||
{
|
||||
throw new DatabaseImporterException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public VaultEntry fromItem(JSONObject item) throws JSONException, GoogleAuthInfoException {
|
||||
JSONObject data = item.getJSONObject("data");
|
||||
JSONObject metadata = data.getJSONObject("metadata");
|
||||
JSONObject content = data.getJSONObject("content");
|
||||
|
||||
//Only login items
|
||||
if(!data.getString("type").equals("login"))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
String uri = content.getString("totpUri");
|
||||
if(uri.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Uri toptURI = Uri.parse(content.getString("totpUri"));
|
||||
|
||||
GoogleAuthInfo entry = GoogleAuthInfo.parseUri(toptURI);
|
||||
|
||||
return new VaultEntry(entry.getOtpInfo(), metadata.getString("name"), entry.getIssuer());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -506,6 +506,7 @@
|
|||
<string name="importer_help_authy">Supply a copy of <b>/data/data/com.authy.authy/shared_prefs/com.authy.storage.tokens.authenticator.xml</b>, located in the internal storage directory of Authy.</string>
|
||||
<string name="importer_help_andotp">Supply an andOTP export/backup file.</string>
|
||||
<string name="importer_help_bitwarden">Supply a Bitwarden export/backup file. Encrypted files are not supported.</string>
|
||||
<string name="importer_help_proton_pass">Supply a Proton pass export/backup zip. Encrypted files are not supported.</string>
|
||||
<string name="importer_help_battle_net_authenticator">Supply a copy of <b>/data/data/com.blizzard.messenger/shared_prefs/com.blizzard.messenger.authenticator_preferences.xml</b>, located in the internal storage directory of Battle.net Authenticator.</string>
|
||||
<string name="importer_help_duo">Supply a copy of <b>/data/data/com.duosecurity.duomobile/files/duokit/accounts.json</b>, located in the internal storage directory of DUO.</string>
|
||||
<string name="importer_help_freeotp">Supply a copy of <b>/data/data/org.fedorahosted.freeotp/shared_prefs/tokens.xml</b>, located in the internal storage directory of FreeOTP (1.x).</string>
|
||||
|
|
|
@ -209,6 +209,12 @@ public class DatabaseImporterTest {
|
|||
checkImportedBitwardenEntries(entries);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportProtonPassZip() throws DatabaseImporterException, IOException, OtpInfoException {
|
||||
List<VaultEntry> entries = importPlain(ProtonPassImporter.class, "proton_pass.zip");
|
||||
checkImportedProtonPassEntries(entries);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportFreeOtp() throws IOException, DatabaseImporterException, OtpInfoException {
|
||||
List<VaultEntry> entries = importPlain(FreeOtpImporter.class, "freeotp.xml");
|
||||
|
@ -441,6 +447,14 @@ public class DatabaseImporterTest {
|
|||
}
|
||||
}
|
||||
|
||||
private void checkImportedProtonPassEntries(List<VaultEntry> entries) throws OtpInfoException {
|
||||
for (VaultEntry entry : entries)
|
||||
{
|
||||
entry.getGroups().clear();
|
||||
checkImportedEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkImportedEntries(List<VaultEntry> entries) throws OtpInfoException {
|
||||
for (VaultEntry entry : entries) {
|
||||
checkImportedEntry(entry);
|
||||
|
|
Binary file not shown.
Loading…
Add table
Reference in a new issue