Introduce uidStatus view for external provider

This commit is contained in:
Vincent Breitmoser
2018-07-07 03:43:37 +02:00
parent 86ecd13c1d
commit 4c8fda0798
5 changed files with 80 additions and 41 deletions

View File

@@ -51,7 +51,7 @@ import timber.log.Timber;
*/ */
public class KeychainDatabase { public class KeychainDatabase {
private static final String DATABASE_NAME = "openkeychain.db"; private static final String DATABASE_NAME = "openkeychain.db";
private static final int DATABASE_VERSION = 29; private static final int DATABASE_VERSION = 30;
private final SupportSQLiteOpenHelper supportSQLiteOpenHelper; private final SupportSQLiteOpenHelper supportSQLiteOpenHelper;
private static KeychainDatabase sInstance; private static KeychainDatabase sInstance;
@@ -133,6 +133,8 @@ public class KeychainDatabase {
db.execSQL(AutocryptPeersModel.CREATE_TABLE); db.execSQL(AutocryptPeersModel.CREATE_TABLE);
db.execSQL(ApiAllowedKeysModel.CREATE_TABLE); db.execSQL(ApiAllowedKeysModel.CREATE_TABLE);
db.execSQL(KeysModel.UNIFIEDKEYVIEW); db.execSQL(KeysModel.UNIFIEDKEYVIEW);
db.execSQL(KeysModel.VALIDKEYSVIEW);
db.execSQL(UserPacketsModel.UIDSTATUS);
db.execSQL("CREATE INDEX keys_by_rank ON keys (" + KeysColumns.RANK + ", " + KeysColumns.MASTER_KEY_ID + ");"); db.execSQL("CREATE INDEX keys_by_rank ON keys (" + KeysColumns.RANK + ", " + KeysColumns.MASTER_KEY_ID + ");");
db.execSQL("CREATE INDEX uids_by_rank ON user_packets (" + UserPacketsColumns.RANK + ", " db.execSQL("CREATE INDEX uids_by_rank ON user_packets (" + UserPacketsColumns.RANK + ", "
@@ -356,18 +358,28 @@ public class KeychainDatabase {
renameApiAutocryptPeersTable(db); renameApiAutocryptPeersTable(db);
case 28: case 28:
recreateUnifiedKeyView(db);
// drop old table from version 20 // drop old table from version 20
db.execSQL("DROP TABLE IF EXISTS api_accounts"); db.execSQL("DROP TABLE IF EXISTS api_accounts");
case 29:
recreateUnifiedKeyView(db);
} }
} }
private void recreateUnifiedKeyView(SupportSQLiteDatabase db) { private void recreateUnifiedKeyView(SupportSQLiteDatabase db) {
try { try {
db.beginTransaction(); db.beginTransaction();
// noinspection deprecation // noinspection deprecation
db.execSQL("DROP VIEW IF EXISTS " + KeysModel.UNIFIEDKEYVIEW_VIEW_NAME); db.execSQL("DROP VIEW IF EXISTS " + KeysModel.UNIFIEDKEYVIEW_VIEW_NAME);
db.execSQL(KeysModel.UNIFIEDKEYVIEW); db.execSQL(KeysModel.UNIFIEDKEYVIEW);
// noinspection deprecation
db.execSQL("DROP VIEW IF EXISTS " + KeysModel.VALIDMASTERKEYS_VIEW_NAME);
db.execSQL(KeysModel.VALIDKEYSVIEW);
// noinspection deprecation
db.execSQL("DROP VIEW IF EXISTS " + UserPacketsModel.UIDSTATUS_VIEW_NAME);
db.execSQL(UserPacketsModel.UIDSTATUS);
db.setTransactionSuccessful(); db.setTransactionSuccessful();
} finally { } finally {
db.endTransaction(); db.endTransaction();

View File

@@ -16,6 +16,8 @@ public abstract class UserPacket implements UserPacketsModel {
FACTORY.selectUserIdsByMasterKeyIdMapper(AutoValue_UserPacket_UserId::new); FACTORY.selectUserIdsByMasterKeyIdMapper(AutoValue_UserPacket_UserId::new);
public static final SelectUserAttributesByTypeAndMasterKeyIdMapper<UserAttribute> USER_ATTRIBUTE_MAPPER = public static final SelectUserAttributesByTypeAndMasterKeyIdMapper<UserAttribute> USER_ATTRIBUTE_MAPPER =
FACTORY.selectUserAttributesByTypeAndMasterKeyIdMapper(AutoValue_UserPacket_UserAttribute::new); FACTORY.selectUserAttributesByTypeAndMasterKeyIdMapper(AutoValue_UserPacket_UserAttribute::new);
public static final UidStatusMapper<UidStatus> UID_STATUS_MAPPER =
FACTORY.selectUserIdStatusByEmailMapper(AutoValue_UserPacket_UidStatus::new);
public static UserPacket create(long masterKeyId, int rank, Long type, String userId, String name, String email, public static UserPacket create(long masterKeyId, int rank, Long type, String userId, String name, String email,
String comment, byte[] attribute_data, boolean isPrimary, boolean isRevoked) { String comment, byte[] attribute_data, boolean isPrimary, boolean isRevoked) {
@@ -55,4 +57,11 @@ public abstract class UserPacket implements UserPacketsModel {
return CustomColumnAdapters.VERIFICATON_STATUS_ADAPTER.decode(verified_int()); return CustomColumnAdapters.VERIFICATON_STATUS_ADAPTER.decode(verified_int());
} }
} }
@AutoValue
public static abstract class UidStatus implements UidStatusModel {
public VerificationStatus keyStatus() {
return CustomColumnAdapters.VERIFICATON_STATUS_ADAPTER.decode(key_status_int());
}
}
} }

View File

@@ -36,12 +36,16 @@ import android.net.Uri;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import com.squareup.sqldelight.SqlDelightQuery;
import org.sufficientlysecure.keychain.BuildConfig; import org.sufficientlysecure.keychain.BuildConfig;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.KeychainDatabase; import org.sufficientlysecure.keychain.KeychainDatabase;
import org.sufficientlysecure.keychain.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.daos.ApiAppDao; import org.sufficientlysecure.keychain.daos.ApiAppDao;
import org.sufficientlysecure.keychain.daos.DatabaseNotifyManager; import org.sufficientlysecure.keychain.daos.DatabaseNotifyManager;
import org.sufficientlysecure.keychain.model.UserPacket;
import org.sufficientlysecure.keychain.model.UserPacket.UidStatus;
import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing.VerificationStatus;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
@@ -248,7 +252,7 @@ public class KeychainExternalProvider extends ContentProvider {
plist.contains(AutocryptStatus.UID_MASTER_KEY_ID) || plist.contains(AutocryptStatus.UID_MASTER_KEY_ID) ||
plist.contains(AutocryptStatus.UID_CANDIDATES); plist.contains(AutocryptStatus.UID_CANDIDATES);
if (queriesUidResult) { if (queriesUidResult) {
fillTempTableWithUidResult(db, isWildcardSelector); fillTempTableWithUidResult(db, isWildcardSelector, selectionArgs);
} }
boolean queriesAutocryptResult = plist.contains(AutocryptStatus.AUTOCRYPT_PEER_STATE) || boolean queriesAutocryptResult = plist.contains(AutocryptStatus.AUTOCRYPT_PEER_STATE) ||
@@ -343,44 +347,32 @@ public class KeychainExternalProvider extends ContentProvider {
} }
} }
private void fillTempTableWithUidResult(SupportSQLiteDatabase db, boolean isWildcardSelector) { private void fillTempTableWithUidResult(SupportSQLiteDatabase db,
String cmpOperator = isWildcardSelector ? " LIKE " : " = "; boolean isWildcardSelector, String[] selectionArgs) {
long unixSeconds = System.currentTimeMillis() / 1000; SqlDelightQuery query;
db.execSQL("REPLACE INTO " + TEMP_TABLE_QUERIED_ADDRESSES + if (isWildcardSelector) {
"(" + TEMP_TABLE_COLUMN_ADDRES + ", " + AutocryptStatus.UID_KEY_STATUS + ", " + query = UserPacket.FACTORY.selectUserIdStatusByEmailLike(selectionArgs[0]);
AutocryptStatus.UID_ADDRESS + ", " + AutocryptStatus.UID_MASTER_KEY_ID } else {
+ ", " + AutocryptStatus.UID_CANDIDATES + ")" + query = UserPacket.FACTORY.selectUserIdStatusByEmail(selectionArgs);
" SELECT " + TEMP_TABLE_COLUMN_ADDRES + ", " + }
"CASE ( MIN (" + Tables.CERTS + "." + Certs.VERIFIED + " ) ) "
// remap to keep this provider contract independent from our internal representation try (Cursor cursor = db.query(query)) {
+ " WHEN " + Certs.VERIFIED_SELF + " THEN " + KeychainExternalContract.KEY_STATUS_UNVERIFIED ContentValues cv = new ContentValues();
+ " WHEN " + Certs.VERIFIED_SECRET + " THEN " + KeychainExternalContract.KEY_STATUS_VERIFIED while (cursor.moveToNext()) {
+ " END AS " + AutocryptStatus.UID_KEY_STATUS UidStatus uidStatus = UserPacket.UID_STATUS_MAPPER.map(cursor);
+ ", " + Tables.USER_PACKETS + "." + UserPackets.USER_ID int keyStatus = uidStatus.keyStatus() == VerificationStatus.VERIFIED_SECRET ?
+ ", " + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID KeychainExternalContract.KEY_STATUS_VERIFIED : KeychainExternalContract.KEY_STATUS_UNVERIFIED;
+ ", COUNT(DISTINCT " + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + ")"
+ " FROM " + TEMP_TABLE_QUERIED_ADDRESSES cv.put(AutocryptStatus.UID_ADDRESS, uidStatus.user_id());
+ " LEFT JOIN " + Tables.USER_PACKETS + " ON (" cv.put(AutocryptStatus.UID_MASTER_KEY_ID, uidStatus.master_key_id());
+ Tables.USER_PACKETS + "." + UserPackets.EMAIL + cmpOperator + cv.put(AutocryptStatus.UID_KEY_STATUS, keyStatus);
TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES cv.put(AutocryptStatus.UID_CANDIDATES, uidStatus.candidates());
+ ")"
+ " LEFT JOIN " + Tables.CERTS + " ON (" db.update(TEMP_TABLE_QUERIED_ADDRESSES, SQLiteDatabase.CONFLICT_IGNORE, cv,
+ Tables.CERTS + "." + Certs.MASTER_KEY_ID + " = " + Tables.USER_PACKETS + "." + TEMP_TABLE_COLUMN_ADDRES + "= ?",
UserPackets.MASTER_KEY_ID new String[] { isWildcardSelector ? selectionArgs[0] : uidStatus.email() });
+ " AND " + Tables.CERTS + "." + Certs.RANK + " = " + Tables.USER_PACKETS + "." + }
UserPackets.RANK }
+ " AND " + Tables.CERTS + "." + Certs.VERIFIED + " > 0"
+ ")"
+ " WHERE (EXISTS (SELECT 1 FROM " + Tables.KEYS + " WHERE "
+ Tables.KEYS + "." + Keys.KEY_ID + " = " + Tables.USER_PACKETS + "." +
UserPackets.MASTER_KEY_ID
+ " AND " + Tables.KEYS + "." + Keys.RANK + " = 0"
+ " AND " + Tables.KEYS + "." + Keys.IS_REVOKED + " = 0"
+ " AND NOT " + "(" + Tables.KEYS + "." + Keys.EXPIRY + " IS NOT NULL AND " + Tables.KEYS +
"." + Keys.EXPIRY
+ " < " + unixSeconds + ")"
+ ")) OR " + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " IS NULL"
+ " GROUP BY " + TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES);
} }
private int getPeerStateValue(AutocryptState autocryptState) { private int getPeerStateValue(AutocryptState autocryptState) {

View File

@@ -36,6 +36,12 @@ UPDATE keys
SET has_secret = ?2 SET has_secret = ?2
WHERE key_id = ?1; WHERE key_id = ?1;
validKeysView:
CREATE VIEW validMasterKeys AS
SELECT *
FROM keys
WHERE rank = 0 AND is_revoked = 0 AND is_secure = 1 AND (expiry IS NULL OR expiry >= strftime('%s', 'now'));
unifiedKeyView: unifiedKeyView:
CREATE VIEW unifiedKeyView AS CREATE VIEW unifiedKeyView AS
SELECT keys.master_key_id, keys.fingerprint, MIN(user_packets.rank), user_packets.user_id, user_packets.name, user_packets.email, user_packets.comment, keys.creation, keys.expiry, keys.is_revoked, keys.is_secure, keys.can_certify, certs.verified, SELECT keys.master_key_id, keys.fingerprint, MIN(user_packets.rank), user_packets.user_id, user_packets.name, user_packets.email, user_packets.comment, keys.creation, keys.expiry, keys.is_revoked, keys.is_secure, keys.can_certify, certs.verified,

View File

@@ -49,3 +49,23 @@ SELECT user_packets.master_key_id, user_packets.rank, attribute_data, is_primary
LEFT JOIN certs ON ( user_packets.master_key_id = certs.master_key_id AND user_packets.rank = certs.rank AND certs.verified > 0 ) LEFT JOIN certs ON ( user_packets.master_key_id = certs.master_key_id AND user_packets.rank = certs.rank AND certs.verified > 0 )
WHERE user_packets.type = ? AND user_packets.master_key_id = ? AND user_packets.rank = ? WHERE user_packets.type = ? AND user_packets.master_key_id = ? AND user_packets.rank = ?
GROUP BY user_packets.master_key_id, user_packets.rank; GROUP BY user_packets.master_key_id, user_packets.rank;
uidStatus:
CREATE VIEW uidStatus AS
SELECT user_packets.email, MIN(certs.verified) AS key_status_int, user_packets.user_id, user_packets.master_key_id, COUNT(DISTINCT user_packets.master_key_id) AS candidates
FROM user_packets
JOIN validMasterKeys USING (master_key_id)
LEFT JOIN certs ON (certs.master_key_id = user_packets.master_key_id AND certs.rank = user_packets.rank AND certs.verified > 0)
WHERE user_packets.email IS NOT NULL
GROUP BY user_packets.email;
selectUserIdStatusByEmail:
SELECT *
FROM uidStatus
WHERE email IN ?;
selectUserIdStatusByEmailLike:
SELECT *
FROM uidStatus
WHERE email LIKE ?;