Skip to content

Commit

Permalink
Introducing class MyAccount
Browse files Browse the repository at this point in the history
Avoid use of class Authenticator throughout the app. Also a good
preparation step for #1294 and #65

Signed-off-by: Daniele Ricci <[email protected]>
  • Loading branch information
daniele-athome committed Apr 5, 2020
1 parent ceb3c79 commit 33d0119
Show file tree
Hide file tree
Showing 35 changed files with 302 additions and 260 deletions.
68 changes: 25 additions & 43 deletions app/src/main/java/org/kontalk/Kontalk.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,14 @@
import android.net.ConnectivityManager;
import android.os.Build;
import android.os.Handler;

import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;
import androidx.annotation.RequiresApi;
import androidx.multidex.MultiDexApplication;

import org.kontalk.authenticator.Authenticator;
import org.kontalk.authenticator.MyAccount;
import org.kontalk.crypto.PGP;
import org.kontalk.crypto.PersonalKey;
import org.kontalk.data.Contact;
Expand Down Expand Up @@ -82,17 +85,7 @@ public class Kontalk extends MultiDexApplication {
*/
private static Kontalk sInstance;

private PersonalKey mDefaultKey;

/**
* Passphrase to decrypt the personal private key.
* This should be asked to the user and stored in memory - otherwise use
* a dummy password if user doesn't want to remember it (or optionally do
* not encrypt the private key).
* For the moment, this is random-generated and stored as the account
* password in Android Account Manager.
*/
private String mKeyPassphrase;
private MyAccount mDefaultAccount;

/**
* Keep-alive reference counter.
Expand Down Expand Up @@ -131,16 +124,12 @@ else if ("pref_reporting".equals(key)) {
}
}
// actions requiring an account
else if (Authenticator.getDefaultAccount(Kontalk.this) != null) {
else if (getDefaultAccount() != null) {
// manual server address
if ("pref_network_uri".equals(key)) {
// temporary measure for users coming from old betas
// this is triggered because manual server address is cleared
if (Authenticator.getDefaultServer(Kontalk.this) != null) {
// just restart the message center for now
Log.w(TAG, "network address changed");
MessageCenterService.restart(Kontalk.this);
}
// just restart the message center for now
Log.w(TAG, "network address changed");
MessageCenterService.restart(Kontalk.this);
}
// hide presence flag / encrypt user data flag
else if ("pref_hide_presence".equals(key) || "pref_encrypt_userdata".equals(key)) {
Expand Down Expand Up @@ -211,7 +200,7 @@ public void onCreate() {
// TODO listen for changes to phone numbers

AccountManager am = AccountManager.get(this);
Account account = Authenticator.getDefaultAccount(am);
MyAccount account = getDefaultAccount();
if (account != null) {
// register account change listener
final OnAccountsUpdateListener listener = new OnAccountsUpdateListener() {
Expand Down Expand Up @@ -248,8 +237,9 @@ public void onAccountsUpdated(Account[] accounts) {
am.addOnAccountsUpdatedListener(listener, null, true);

// TODO remove after a few release iterations
if (Authenticator.getServiceTermsURL(am, account) == null) {
am.setUserData(account, Authenticator.DATA_SERVICE_TERMS_URL,
if (Authenticator.getDefaultServiceTermsURL(this) == null) {
am.setUserData(account.getSystemAccount(),
Authenticator.DATA_SERVICE_TERMS_URL,
getString(R.string.help_default_KPN_service_terms_url));
}
}
Expand All @@ -271,42 +261,34 @@ public void onAccountsUpdated(Account[] accounts) {
*/
@Deprecated
private void disableHintsForUpgrade() {
if (Authenticator.getDefaultAccount(this) != null) {
if (getDefaultAccount() != null) {
Showcase.disableAllHints();
}
}

@Nullable
public MyAccount getDefaultAccount() {
if (mDefaultAccount == null) {
mDefaultAccount = Authenticator.getDefaultAccount(this);
}
return mDefaultAccount;
}

public PersonalKey getPersonalKey() throws PGPException, IOException, CertificateException {
if (mDefaultKey == null)
mDefaultKey = Authenticator.loadDefaultPersonalKey(this, getCachedPassphrase());
return mDefaultKey;
MyAccount account = getDefaultAccount();
return account != null ? account.getPersonalKey() : null;
}

public void exportPersonalKey(OutputStream out, String exportPassphrase)
throws CertificateException, PGPException, IOException,
KeyStoreException, NoSuchAlgorithmException {

Authenticator.exportDefaultPersonalKey(this, out, getCachedPassphrase(), exportPassphrase, true);
Authenticator.exportDefaultPersonalKey(this, out, exportPassphrase, true);
}

/** Invalidates the cached personal key. */
public void invalidatePersonalKey() {
mDefaultKey = null;
mKeyPassphrase = null;
}

private void ensureCachedPassphrase() {
if (mKeyPassphrase == null) {
AccountManager am = AccountManager.get(this);
Account account = Authenticator.getDefaultAccount(am);
// cache passphrase from account
mKeyPassphrase = am.getPassword(account);
}
}

public String getCachedPassphrase() {
ensureCachedPassphrase();
return mKeyPassphrase;
mDefaultAccount = null;
}

private void initMessagesController() {
Expand Down
5 changes: 2 additions & 3 deletions app/src/main/java/org/kontalk/MessagesController.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
import android.text.TextUtils;
import android.widget.Toast;

import org.kontalk.authenticator.Authenticator;
import org.kontalk.crypto.Coder;
import org.kontalk.data.Conversation;
import org.kontalk.message.AttachmentComponent;
Expand Down Expand Up @@ -297,7 +296,7 @@ private void addMembersInternal(GroupCommandComponent group, String[] members) {

for (String member : members) {
// do not add ourselves...
if (Authenticator.isSelfJID(mContext, member)) {
if (Kontalk.get().getDefaultAccount().isSelfJID(member)) {
// ...but mark our membership
MessagesProviderClient.setGroupMembership(mContext,
group.getContent().getJid().toString(), MyMessages.Groups.MEMBERSHIP_MEMBER);
Expand Down Expand Up @@ -401,7 +400,7 @@ else if (group.isAddOrRemoveCommand()) {
removed, false);
// set our membership to parted if we were removed from the group
for (String removedJid : removed) {
if (Authenticator.isSelfJID(mContext, removedJid)) {
if (Kontalk.get().getDefaultAccount().isSelfJID(removedJid)) {
MessagesProviderClient.setGroupMembership(mContext,
group.getContent().getJid().toString(), MyMessages.Groups.MEMBERSHIP_KICKED);
break;
Expand Down
99 changes: 34 additions & 65 deletions app/src/main/java/org/kontalk/authenticator/Authenticator.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.jxmpp.jid.BareJid;
import org.bouncycastle.openpgp.PGPException;

import android.accounts.AbstractAccountAuthenticator;
Expand Down Expand Up @@ -57,7 +56,6 @@
import org.kontalk.provider.Keyring;
import org.kontalk.ui.MainActivity;
import org.kontalk.ui.NumberValidation;
import org.kontalk.util.XMPPUtils;


/**
Expand Down Expand Up @@ -86,101 +84,72 @@ public Authenticator(Context context) {
mHandler = new Handler(Looper.getMainLooper());
}

public static Account getDefaultAccount(Context ctx) {
return getDefaultAccount(AccountManager.get(ctx));
@Nullable
public static MyAccount getDefaultAccount(Context context) {
AccountManager am = AccountManager.get(context);
Account account = getDefaultSystemAccount(am);
return account != null ? new MyAccount(account, am) : null;
}

@Nullable
public static Account getDefaultAccount(AccountManager m) {
private static Account getDefaultSystemAccount(AccountManager m) {
Account[] accs = m.getAccountsByType(ACCOUNT_TYPE);
return (accs.length > 0) ? accs[0] : null;
}

@Nullable
public static String getDefaultAccountName(Context ctx) {
Account acc = getDefaultAccount(ctx);
return (acc != null) ? acc.name : null;
}

@Nullable
public static String getSelfJID(Context ctx) {
String name = getDefaultAccountName(ctx);
return (name != null) ?
XMPPUtils.createLocalJID(ctx, XMPPUtils.createLocalpart(name)) : null;
}

public static boolean isSelfJID(Context ctx, BareJid jid) {
String self = getSelfJID(ctx);
return self != null && jid.equals(self);
}

public static boolean isSelfJID(Context ctx, String bareJid) {
String jid = getSelfJID(ctx);
return jid != null && jid.equalsIgnoreCase(bareJid);
}

public static String getDefaultDisplayName(Context context) {
AccountManager am = AccountManager.get(context);
Account account = getDefaultAccount(am);
return getDisplayName(am, account);
}

public static String getDisplayName(AccountManager am, Account account) {
static String getDisplayName(AccountManager am, Account account) {
return account != null ? am.getUserData(account, DATA_NAME) : null;
}

public static EndpointServer getDefaultServer(Context context) {
AccountManager am = AccountManager.get(context);
Account account = getDefaultAccount(am);
return getServer(am, account);
}

public static EndpointServer getServer(AccountManager am, Account account) {
static EndpointServer getServer(AccountManager am, Account account) {
if (account != null) {
String uri = am.getUserData(account, DATA_SERVER_URI);
return uri != null ? new EndpointServer(uri) : null;
}
return null;
}

/** @deprecated Still used in {@link org.kontalk.Kontalk#onCreate()}. */
@Deprecated
public static String getDefaultServiceTermsURL(Context context) {
AccountManager am = AccountManager.get(context);
Account account = getDefaultAccount(am);
Account account = getDefaultSystemAccount(am);
return getServiceTermsURL(am, account);
}

public static String getServiceTermsURL(AccountManager am, Account account) {
static String getServiceTermsURL(AccountManager am, Account account) {
return account != null ?
am.getUserData(account, Authenticator.DATA_SERVICE_TERMS_URL) : null;
}

public static PersonalKey loadDefaultPersonalKey(Context ctx, String passphrase)
static PersonalKey loadPersonalKey(AccountManager am, Account account)
throws PGPException, IOException, CertificateException {
AccountManager m = AccountManager.get(ctx);
Account acc = getDefaultAccount(m);

String privKeyData = m.getUserData(acc, DATA_PRIVATEKEY);
String pubKeyData = m.getUserData(acc, DATA_PUBLICKEY);
String bridgeCertData = m.getUserData(acc, DATA_BRIDGECERT);
String passphrase = am.getPassword(account);
String privKeyData = am.getUserData(account, DATA_PRIVATEKEY);
String pubKeyData = am.getUserData(account, DATA_PUBLICKEY);
String bridgeCertData = am.getUserData(account, DATA_BRIDGECERT);

if (privKeyData != null && pubKeyData != null && bridgeCertData != null)
if (privKeyData != null && pubKeyData != null && bridgeCertData != null) {
return PersonalKey
.load(Base64.decode(privKeyData, Base64.DEFAULT),
Base64.decode(pubKeyData, Base64.DEFAULT),
passphrase,
Base64.decode(bridgeCertData, Base64.DEFAULT)
Base64.decode(pubKeyData, Base64.DEFAULT),
passphrase,
Base64.decode(bridgeCertData, Base64.DEFAULT)
);

else
}
else {
return null;
}
}

public static void exportDefaultPersonalKey(Context ctx, OutputStream dest, String passphrase, String exportPassphrase, boolean bridgeCertificate)
public static void exportDefaultPersonalKey(Context ctx, OutputStream dest, String exportPassphrase, boolean bridgeCertificate)
throws CertificateException, PGPException,
IOException, KeyStoreException, NoSuchAlgorithmException {

AccountManager m = AccountManager.get(ctx);
Account acc = getDefaultAccount(m);
Account acc = getDefaultSystemAccount(m);
String passphrase = m.getPassword(acc);

String privKeyData = m.getUserData(acc, DATA_PRIVATEKEY);
byte[] privateKey = Base64.decode(privKeyData, Base64.DEFAULT);
Expand All @@ -205,7 +174,7 @@ public static void exportDefaultPersonalKey(Context ctx, OutputStream dest, Stri
public static byte[] getPrivateKeyExportData(Context ctx, String passphrase, String exportPassphrase)
throws PGPException, IOException {
AccountManager m = AccountManager.get(ctx);
Account acc = getDefaultAccount(m);
Account acc = getDefaultSystemAccount(m);

return getPrivateKeyExportData(m, acc, passphrase, exportPassphrase);
}
Expand All @@ -228,7 +197,7 @@ private static byte[] getPrivateKeyExportData(AccountManager m, Account acc, Str
public static void setDefaultPersonalKey(Context ctx, byte[] publicKeyData, byte[] privateKeyData,
byte[] bridgeCertData, String passphrase) {
AccountManager am = AccountManager.get(ctx);
Account acc = getDefaultAccount(am);
Account acc = getDefaultSystemAccount(am);

// password is optional when updating just the public key
if (passphrase != null)
Expand All @@ -250,7 +219,7 @@ public static void changePassphrase(Context ctx, String oldPassphrase, String ne
throws PGPException, IOException {

AccountManager am = AccountManager.get(ctx);
Account acc = getDefaultAccount(am);
Account acc = getDefaultSystemAccount(am);

// get old secret key ring
String privKeyData = am.getUserData(acc, DATA_PRIVATEKEY);
Expand All @@ -268,21 +237,21 @@ public static void changePassphrase(Context ctx, String oldPassphrase, String ne

public static void setPassphrase(Context ctx, String passphrase, boolean fromUser) {
AccountManager am = AccountManager.get(ctx);
Account acc = getDefaultAccount(am);
Account acc = getDefaultSystemAccount(am);
am.setUserData(acc, DATA_USER_PASSPHRASE, String.valueOf(fromUser));
// replace password for account
am.setPassword(acc, passphrase);
}

public static boolean isUserPassphrase(Context ctx) {
AccountManager am = AccountManager.get(ctx);
Account acc = getDefaultAccount(am);
Account acc = getDefaultSystemAccount(am);
return Boolean.parseBoolean(am.getUserData(acc, DATA_USER_PASSPHRASE));
}

public static void removeDefaultAccount(Context ctx, AccountManagerCallback<Boolean> callback) {
AccountManager am = AccountManager.get(ctx);
Account account = getDefaultAccount(am);
Account account = getDefaultSystemAccount(am);

// there is something wrong with this, isn't it? [cit.]
if (account == null)
Expand Down Expand Up @@ -318,7 +287,7 @@ public Boolean getResult(long timeout, TimeUnit unit) throws OperationCanceledEx
});
}
else {
am.removeAccount(getDefaultAccount(am), callback, null);
am.removeAccount(getDefaultSystemAccount(am), callback, null);
}
}

Expand Down
Loading

0 comments on commit 33d0119

Please sign in to comment.