Differentiate between ikey and manual trusts
This commit is contained in:
parent
4b9e9f9ae9
commit
2ea902a081
|
@ -106,7 +106,7 @@ public class AccountDetailsFragment extends Fragment {
|
||||||
viewModel = new ViewModelProvider(this, factory)
|
viewModel = new ViewModelProvider(this, factory)
|
||||||
.get(AndroidAccountDetailsViewModel.class);
|
.get(AndroidAccountDetailsViewModel.class);
|
||||||
|
|
||||||
this.otherFingerprintsAdapter = new ToggleableFingerprintsAdapter(this::markFingerprintTrusted);
|
this.otherFingerprintsAdapter = new ToggleableFingerprintsAdapter(context, this::markFingerprintTrusted);
|
||||||
this.otherFingerprintsAdapter.setItemLongClickListener(fingerprint -> viewModel.unpublishPublicKey(fingerprint));
|
this.otherFingerprintsAdapter.setItemLongClickListener(fingerprint -> viewModel.unpublishPublicKey(fingerprint));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ public class ContactDetailFragment extends Fragment {
|
||||||
contactName.setOnClickListener(v -> displayChangeContactNameDialog());
|
contactName.setOnClickListener(v -> displayChangeContactNameDialog());
|
||||||
|
|
||||||
|
|
||||||
fingerprintsAdapter = new ToggleableFingerprintsAdapter(
|
fingerprintsAdapter = new ToggleableFingerprintsAdapter(getContext(),
|
||||||
(fingerprint, checked) -> viewModel.markDeviceFingerprintTrusted(fingerprint, checked));
|
(fingerprint, checked) -> viewModel.markDeviceFingerprintTrusted(fingerprint, checked));
|
||||||
fingerprintRecyclerView.setAdapter(fingerprintsAdapter);
|
fingerprintRecyclerView.setAdapter(fingerprintsAdapter);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package org.mercury_im.messenger.android.ui.openpgp;
|
package org.mercury_im.messenger.android.ui.openpgp;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -9,6 +11,7 @@ import android.widget.TextView;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||||
import org.mercury_im.messenger.R;
|
import org.mercury_im.messenger.R;
|
||||||
import org.mercury_im.messenger.core.viewmodel.openpgp.FingerprintViewItem;
|
import org.mercury_im.messenger.core.viewmodel.openpgp.FingerprintViewItem;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
@ -25,8 +28,10 @@ public class ToggleableFingerprintsAdapter extends RecyclerView.Adapter<Toggleab
|
||||||
private OnFingerprintItemLongClickListener longClickListener = null;
|
private OnFingerprintItemLongClickListener longClickListener = null;
|
||||||
|
|
||||||
private static final DateFormat dateFormat = SimpleDateFormat.getDateInstance();
|
private static final DateFormat dateFormat = SimpleDateFormat.getDateInstance();
|
||||||
|
private Context context;
|
||||||
|
|
||||||
public ToggleableFingerprintsAdapter(OnFingerprintItemToggleListener toggleListener) {
|
public ToggleableFingerprintsAdapter(Context context, OnFingerprintItemToggleListener toggleListener) {
|
||||||
|
this.context = context;
|
||||||
this.toggleListener = toggleListener;
|
this.toggleListener = toggleListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +66,9 @@ public class ToggleableFingerprintsAdapter extends RecyclerView.Adapter<Toggleab
|
||||||
boolean checked = ((Switch) v).isChecked();
|
boolean checked = ((Switch) v).isChecked();
|
||||||
toggleListener.onFingerprintToggled(fingerprint, checked);
|
toggleListener.onFingerprintToggled(fingerprint, checked);
|
||||||
});
|
});
|
||||||
|
if (f.getTrusted() == OpenPgpTrustStore.Trust.ikey_trusted && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
holder.trustSwitch.setThumbDrawable(context.getDrawable(R.drawable.ic_lock_black_24dp));
|
||||||
|
}
|
||||||
holder.divider.setVisibility(position == fingerprints.size() - 1 ? View.GONE : View.VISIBLE);
|
holder.divider.setVisibility(position == fingerprints.size() - 1 ? View.GONE : View.VISIBLE);
|
||||||
|
|
||||||
holder.itemView.setOnLongClickListener(v -> {
|
holder.itemView.setOnLongClickListener(v -> {
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
|
||||||
|
</vector>
|
|
@ -150,11 +150,8 @@ public class RxIkeyRepository implements IkeyRepository {
|
||||||
public Completable storeRecord(UUID accountId, EntityBareJid jid, IkeyRecord record) {
|
public Completable storeRecord(UUID accountId, EntityBareJid jid, IkeyRecord record) {
|
||||||
assert jid.equals(record.getJid());
|
assert jid.equals(record.getJid());
|
||||||
return getRecordModel(accountId, jid, false)
|
return getRecordModel(accountId, jid, false)
|
||||||
.doOnNext(m -> LOGGER.log(Level.INFO, "Loaded model: " + m))
|
|
||||||
.single(new IkeyRecordModel())
|
.single(new IkeyRecordModel())
|
||||||
.doOnSuccess(m -> LOGGER.log(Level.INFO, "Singled model: " + m))
|
|
||||||
.map(m -> {
|
.map(m -> {
|
||||||
LOGGER.log(Level.INFO, "Mapping model: " + m);
|
|
||||||
if (m.getId() == null) m.setId(UUID.randomUUID());
|
if (m.getId() == null) m.setId(UUID.randomUUID());
|
||||||
m.setAccountId(accountId);
|
m.setAccountId(accountId);
|
||||||
m.setJid(jid);
|
m.setJid(jid);
|
||||||
|
@ -174,14 +171,11 @@ public class RxIkeyRepository implements IkeyRepository {
|
||||||
sm.setUri(s.getUri());
|
sm.setUri(s.getUri());
|
||||||
m.getSubordinates().add(sm);
|
m.getSubordinates().add(sm);
|
||||||
}
|
}
|
||||||
LOGGER.log(Level.INFO, "Return model: " + m);
|
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
})
|
})
|
||||||
.flatMap(data::upsert)
|
.flatMap(data::upsert)
|
||||||
.doOnSuccess(m -> LOGGER.log(Level.INFO, "Upserting model: " + m))
|
|
||||||
.ignoreElement()
|
.ignoreElement()
|
||||||
.doOnComplete(() -> LOGGER.log(Level.INFO, "Updated model"))
|
|
||||||
.doOnError(e -> LOGGER.log(Level.SEVERE, "Error storing ikey record", e));
|
.doOnError(e -> LOGGER.log(Level.SEVERE, "Error storing ikey record", e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.mercury_im.messenger.core.store.crypto;
|
package org.mercury_im.messenger.core.store.crypto;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ikey.mechanism.IkeyType;
|
import org.jivesoftware.smackx.ikey.record.IkeyRecord;
|
||||||
import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpTrustStore;
|
import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpTrustStore;
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
@ -10,7 +10,6 @@ import org.mercury_im.messenger.core.data.repository.IkeyKeyRepository;
|
||||||
import org.mercury_im.messenger.core.data.repository.IkeyRecordRepository;
|
import org.mercury_im.messenger.core.data.repository.IkeyRecordRepository;
|
||||||
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
|
||||||
import org.mercury_im.messenger.core.data.repository.OpenPgpTrustRepository;
|
import org.mercury_im.messenger.core.data.repository.OpenPgpTrustRepository;
|
||||||
import org.mercury_im.messenger.core.util.Optional;
|
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -19,10 +18,9 @@ import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import io.reactivex.Completable;
|
import io.reactivex.Completable;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Maybe;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
import io.reactivex.subjects.BehaviorSubject;
|
|
||||||
|
|
||||||
public class IkeyAwareOpenPgpStore extends MercuryOpenPgpStore {
|
public class IkeyAwareOpenPgpStore extends MercuryOpenPgpStore {
|
||||||
|
|
||||||
|
@ -56,7 +54,6 @@ public class IkeyAwareOpenPgpStore extends MercuryOpenPgpStore {
|
||||||
private final SchedulersFacade schedulers;
|
private final SchedulersFacade schedulers;
|
||||||
|
|
||||||
private final CompositeDisposable disposable = new CompositeDisposable();
|
private final CompositeDisposable disposable = new CompositeDisposable();
|
||||||
private final BehaviorSubject<Boolean> accountIsIkeyAware = BehaviorSubject.createDefault(false);
|
|
||||||
|
|
||||||
public TrustStore(UUID accountId, OpenPgpTrustRepository openPgpTrustRepository, IkeyKeyRepository ikeyKeyRepository, IkeyRecordRepository ikeyRecordRepository, SchedulersFacade schedulersFacade) {
|
public TrustStore(UUID accountId, OpenPgpTrustRepository openPgpTrustRepository, IkeyKeyRepository ikeyKeyRepository, IkeyRecordRepository ikeyRecordRepository, SchedulersFacade schedulersFacade) {
|
||||||
this.accountId = accountId;
|
this.accountId = accountId;
|
||||||
|
@ -64,27 +61,24 @@ public class IkeyAwareOpenPgpStore extends MercuryOpenPgpStore {
|
||||||
this.ikeyKeyRepository = ikeyKeyRepository;
|
this.ikeyKeyRepository = ikeyKeyRepository;
|
||||||
this.ikeyRecordRepository = ikeyRecordRepository;
|
this.ikeyRecordRepository = ikeyRecordRepository;
|
||||||
this.schedulers = schedulersFacade;
|
this.schedulers = schedulersFacade;
|
||||||
|
|
||||||
accountIsIkeyAware().subscribe(accountIsIkeyAware);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Trust readTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException {
|
protected Trust readTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException {
|
||||||
EntityBareJid jid = owner.asEntityBareJidOrThrow();
|
EntityBareJid jid = owner.asEntityBareJidOrThrow();
|
||||||
Trust trust = accountIsIkeyAware.firstOrError()
|
Trust trust = readIkeyTrust(jid, fingerprint)
|
||||||
.flatMap(isAware -> isAware ?
|
|
||||||
readIkeyTrust(jid, fingerprint) :
|
|
||||||
readManualTrust(jid, fingerprint))
|
|
||||||
.blockingGet();
|
.blockingGet();
|
||||||
return trust == null ? Trust.undecided : trust;
|
return trust == null ? Trust.undecided : trust;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Single<Trust> readIkeyTrust(EntityBareJid owner, OpenPgpV4Fingerprint fingerprint) {
|
private Single<Trust> readIkeyTrust(EntityBareJid owner, OpenPgpV4Fingerprint fingerprint) {
|
||||||
return ikeyRecordRepository.loadRecord(accountId, owner)
|
return ikeyRecordRepository.loadRecord(accountId, owner)
|
||||||
.map(record -> record.hasSubordinate(fingerprint) ?
|
.filter(record -> record.getTrust() == Trust.trusted)
|
||||||
record.getTrust() : Trust.undecided)
|
.filter(record -> record.hasSubordinate(fingerprint))
|
||||||
.doOnNext(trust -> LOGGER.log(Level.INFO, "Read ikey trust " + trust + " for device key " + fingerprint + " of contact " + owner))
|
.map(IkeyRecord::getTrust)
|
||||||
|
.map(trust -> trust == Trust.trusted ? Trust.ikey_trusted : trust)
|
||||||
.firstElement()
|
.firstElement()
|
||||||
|
.flatMap(t -> t == Trust.undecided ? readManualTrust(owner, fingerprint).toMaybe() : Maybe.just(t))
|
||||||
.switchIfEmpty(readManualTrust(owner, fingerprint));
|
.switchIfEmpty(readManualTrust(owner, fingerprint));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,31 +97,10 @@ public class IkeyAwareOpenPgpStore extends MercuryOpenPgpStore {
|
||||||
protected void writeTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint, Trust trust) throws IOException {
|
protected void writeTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint, Trust trust) throws IOException {
|
||||||
EntityBareJid jid = owner.asEntityBareJidOrThrow();
|
EntityBareJid jid = owner.asEntityBareJidOrThrow();
|
||||||
disposable.add(
|
disposable.add(
|
||||||
contactHasIkeyRecord(owner.asEntityBareJidIfPossible())
|
writeManualTrust(jid, fingerprint, trust)
|
||||||
.flatMapCompletable(hasRecord -> hasRecord && accountIsIkeyAware.getValue() ?
|
.compose(schedulers.executeUiSafeCompletable())
|
||||||
skipManualTrustForIkeyContact(jid, fingerprint, trust) :
|
.subscribe(() -> {},
|
||||||
writeManualTrust(jid, fingerprint, trust))
|
e -> LOGGER.log(Level.SEVERE, "An error happened while marking device key " + fingerprint + " of " + jid + " as " + trust, e)));
|
||||||
.compose(schedulers.executeUiSafeCompletable())
|
|
||||||
.subscribe(() -> {},
|
|
||||||
e -> LOGGER.log(Level.SEVERE, "An error happened while marking device key " + fingerprint + " of " + jid + " as " + trust, e)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Completable skipManualTrustForIkeyContact(EntityBareJid owner, OpenPgpV4Fingerprint fingerprint, Trust trust) {
|
|
||||||
return Completable.complete()
|
|
||||||
.doOnComplete(() -> LOGGER.log(Level.INFO,
|
|
||||||
"Contact " + owner + " has an Ikey Record, so do not mark " + fingerprint + " as " + trust));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Single<Boolean> contactHasIkeyRecord(EntityBareJid owner) {
|
|
||||||
return ikeyRecordRepository.loadRecord(accountId, owner.asEntityBareJidOrThrow())
|
|
||||||
.isEmpty()
|
|
||||||
.map(resultEmpty -> !resultEmpty); // negate
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Observable<Boolean> accountIsIkeyAware() {
|
|
||||||
return ikeyKeyRepository.loadSecretKey(accountId)
|
|
||||||
.map(Optional::isPresent)
|
|
||||||
.compose(schedulers.executeUiSafeObservable());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import org.jivesoftware.smack.roster.Roster;
|
||||||
import org.jivesoftware.smack.roster.RosterEntry;
|
import org.jivesoftware.smack.roster.RosterEntry;
|
||||||
import org.jivesoftware.smack.roster.RosterGroup;
|
import org.jivesoftware.smack.roster.RosterGroup;
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpManager;
|
import org.jivesoftware.smackx.ox.OpenPgpManager;
|
||||||
|
import org.jivesoftware.smackx.ox.crypto.OpenPgpProvider;
|
||||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore;
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore;
|
||||||
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
@ -100,8 +101,9 @@ public class ContactDetailViewModel implements MercuryViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Completable init(Peer peer) {
|
public Completable init(Peer peer) {
|
||||||
this.openPgpStore = OpenPgpManager.getInstanceFor(connectionManager.getConnection(peer.getAccount().getId()).getConnection())
|
OpenPgpManager openPgpManager = OpenPgpManager.getInstanceFor(connectionManager.getConnection(peer.getAccount().getId()).getConnection());
|
||||||
.getOpenPgpProvider().getStore();
|
OpenPgpProvider provider = openPgpManager.getOpenPgpProvider();
|
||||||
|
this.openPgpStore = provider.getStore();
|
||||||
|
|
||||||
return Completable.fromAction(() -> {
|
return Completable.fromAction(() -> {
|
||||||
this.peerId = peer.getId();
|
this.peerId = peer.getId();
|
||||||
|
|
|
@ -23,6 +23,6 @@ public class FingerprintViewItem {
|
||||||
OpenPgpTrustStore.Trust trusted;
|
OpenPgpTrustStore.Trust trusted;
|
||||||
|
|
||||||
public boolean isTrusted() {
|
public boolean isTrusted() {
|
||||||
return trusted == OpenPgpTrustStore.Trust.trusted;
|
return trusted == OpenPgpTrustStore.Trust.trusted || trusted == OpenPgpTrustStore.Trust.ikey_trusted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue