Merge pull request #184 from loki-project/friend-request-fix

Friend request fixes
This commit is contained in:
Niels Andriesse 2020-05-05 15:43:37 +10:00 committed by GitHub
commit 82c824a7a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 91 additions and 39 deletions

2
Pods

@ -1 +1 @@
Subproject commit 04c71c59e4c3b060f8ff96b072efca3a74c4980f
Subproject commit a10cec6cd837552a4b0a88462636cf06494644c4

View File

@ -10,12 +10,11 @@ public final class SignalMessage : NSObject {
@objc(ttl)
public let objc_ttl: UInt64
@objc public let isPing: Bool
@objc public let isFriendRequest: Bool
public var ttl: UInt64? { return objc_ttl != 0 ? objc_ttl : nil }
@objc public init(type: SSKProtoEnvelope.SSKProtoEnvelopeType, timestamp: UInt64, senderID: String, senderDeviceID: UInt32,
content: String, recipientID: String, ttl: UInt64, isPing: Bool, isFriendRequest: Bool) {
content: String, recipientID: String, ttl: UInt64, isPing: Bool) {
self.type = type
self.timestamp = timestamp
self.senderID = senderID
@ -24,7 +23,6 @@ public final class SignalMessage : NSObject {
self.recipientID = recipientID
self.objc_ttl = ttl
self.isPing = isPing
self.isFriendRequest = isFriendRequest
super.init()
}
}

View File

@ -108,6 +108,8 @@ public final class FriendRequestProtocol : NSObject {
print("[Loki] Invalid Session ID: \(hexEncodedPublicKey).")
return
}
let userHexEncodedPublicKey = getUserHexEncodedPublicKey()
let userLinkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: userHexEncodedPublicKey, in: transaction)
// Accept all outstanding friend requests associated with this user and try to establish sessions with the
// subset of their devices that haven't sent a friend request.
let linkedDevices = LokiDatabaseUtilities.getLinkedDeviceHexEncodedPublicKeys(for: hexEncodedPublicKey, in: transaction)
@ -117,7 +119,7 @@ public final class FriendRequestProtocol : NSObject {
storage.setFriendRequestStatus(.friends, for: device, transaction: transaction)
sendFriendRequestAcceptanceMessage(to: device, using: transaction)
// Send a contact sync message if needed
guard !LokiDatabaseUtilities.isUserLinkedDevice(hexEncodedPublicKey, transaction: transaction) else { return }
guard !userLinkedDevices.contains(hexEncodedPublicKey) else { return }
let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey
let syncManager = SSKEnvironment.shared.syncManager
syncManager.syncContact(masterHexEncodedPublicKey, transaction: transaction)
@ -165,6 +167,14 @@ public final class FriendRequestProtocol : NSObject {
}
}
@objc(shouldUpdateFriendRequestStatusFromMessage:)
public static func shouldUpdateFriendRequestStatus(from message: TSOutgoingMessage) -> Bool {
// The order of these checks matters
if (message as? DeviceLinkMessage)?.kind == .request { return true }
if message is SessionRequestMessage { return false }
return message is FriendRequestMessage
}
@objc(setFriendRequestStatusToSendingIfNeededForHexEncodedPublicKey:transaction:)
public static func setFriendRequestStatusToSendingIfNeeded(for hexEncodedPublicKey: String, transaction: YapDatabaseReadWriteTransaction) {
let friendRequestStatus = storage.getFriendRequestStatus(for: hexEncodedPublicKey, transaction: transaction)

View File

@ -428,7 +428,7 @@ class FriendRequestProtocolTests : XCTestCase {
self.storage.setFriendRequestStatus(status, for: bob, transaction: transaction)
}
let expectation = self.expectation(description: "sent message")
let expectation = self.expectation(description: "Send message")
let messageSender = self.messageSender
messageSender.sendMessageWasCalledBlock = { sentMessage in
@ -447,6 +447,32 @@ class FriendRequestProtocolTests : XCTestCase {
}
}
func test_acceptFriendRequestShouldNotSendAFriendRequestMessageToOurOwnDevice() {
let statuses: [LKFriendRequestStatus] = [ .none, .requestExpired ]
for status in statuses {
let ourDevice = LokiTestUtilities.getCurrentUserHexEncodedPublicKey()
storage.dbReadWriteConnection.readWrite { transaction in
self.storage.setFriendRequestStatus(status, for: ourDevice, transaction: transaction)
}
let expectation = self.expectation(description: "Send message")
let messageSender = self.messageSender
messageSender.sendMessageWasCalledBlock = { sentMessage in
XCTFail("Expected message not to be sent.")
}
storage.dbReadWriteConnection.readWrite { transaction in
FriendRequestProtocol.acceptFriendRequest(from: ourDevice, using: transaction)
}
expectation.fulfillAfter(2)
wait(for: [ expectation ], timeout: 2)
messageSender.sendMessageWasCalledBlock = nil
}
}
func test_acceptFriendRequestShouldNotDoAnythingIfRequestHasBeenSent() {
// Case: We sent Bob a friend request.
// We can't accept because we don't have keys to communicate with Bob.
@ -594,4 +620,21 @@ class FriendRequestProtocolTests : XCTestCase {
XCTAssertTrue(self.isFriendRequestStatus(.none, for: otherSlave, transaction: transaction))
}
}
// MARK: - shouldUpdateFriendRequestStatus
func test_shouldUpdateFriendRequestStatusReturnsTheCorrectValue() {
let thread = LokiTestUtilities.createContactThread(for: LokiTestUtilities.generateHexEncodedPublicKey())
let message = TSOutgoingMessage(in: thread, messageBody: nil, attachmentId: nil)
let friendRequest = FriendRequestMessage(timestamp: 1, thread: thread, body: "")
let sessionRequest = SessionRequestMessage(thread: thread)
guard let deviceLinkRequest = DeviceLinkMessage(in: thread, masterHexEncodedPublicKey: "", slaveHexEncodedPublicKey: "", masterSignature: nil, slaveSignature: Data(capacity: 0)),
let deviceLinkAuthorisation = DeviceLinkMessage(in: thread, masterHexEncodedPublicKey: "", slaveHexEncodedPublicKey: "", masterSignature: Data(capacity: 0), slaveSignature: Data(capacity: 0)) else { return XCTFail() }
XCTAssertTrue(FriendRequestProtocol.shouldUpdateFriendRequestStatus(from: friendRequest))
XCTAssertTrue(FriendRequestProtocol.shouldUpdateFriendRequestStatus(from: deviceLinkRequest))
XCTAssertFalse(FriendRequestProtocol.shouldUpdateFriendRequestStatus(from: message))
XCTAssertFalse(FriendRequestProtocol.shouldUpdateFriendRequestStatus(from: sessionRequest))
XCTAssertFalse(FriendRequestProtocol.shouldUpdateFriendRequestStatus(from: deviceLinkAuthorisation))
}
}

View File

@ -4,4 +4,6 @@
NS_SWIFT_NAME(FriendRequestMessage)
@interface LKFriendRequestMessage : TSOutgoingMessage
- (_Nonnull instancetype)initWithTimestamp:(uint64_t)timestamp thread:(nullable TSThread *)thread body:(nullable NSString *)body;
@end

View File

@ -9,6 +9,12 @@
@implementation LKFriendRequestMessage
#pragma mark Initialization
- (instancetype)initWithTimestamp:(uint64_t)timestamp thread:(nullable TSThread *)thread body:(nullable NSString *)body {
return [self initOutgoingMessageWithTimestamp:timestamp inThread:thread messageBody:body attachmentIds:@[] expiresInSeconds:0 expireStartedAt:0
isVoiceMessage:false groupMetaMessage:TSGroupMetaMessageUnspecified quotedMessage:nil contactShare:nil linkPreview:nil];
}
#pragma mark Building
- (SSKProtoContentBuilder *)prepareCustomContentBuilder:(SignalRecipient *)recipient {
SSKProtoContentBuilder *contentBuilder = SSKProtoContent.builder;

View File

@ -14,6 +14,6 @@ NS_SWIFT_NAME(DeviceLinkMessage)
@property (nonatomic, readonly) NSData *slaveSignature;
@property (nonatomic, readonly) LKDeviceLinkMessageKind kind;
- (instancetype)initInThread:(TSThread *)thread masterHexEncodedPublicKey:(NSString *)masterHexEncodedPublicKey slaveHexEncodedPublicKey:(NSString *)slaveHexEncodedPublicKey masterSignature:(NSData *)masterSignature slaveSignature:(NSData *)slaveSignature;
- (instancetype)initInThread:(TSThread *)thread masterHexEncodedPublicKey:(NSString *)masterHexEncodedPublicKey slaveHexEncodedPublicKey:(NSString *)slaveHexEncodedPublicKey masterSignature:(NSData * _Nullable)masterSignature slaveSignature:(NSData *)slaveSignature;
@end

View File

@ -21,7 +21,7 @@
}
#pragma mark Initialization
- (instancetype)initInThread:(TSThread *)thread masterHexEncodedPublicKey:(NSString *)masterHexEncodedPublicKey slaveHexEncodedPublicKey:(NSString *)slaveHexEncodedPublicKey masterSignature:(NSData *)masterSignature slaveSignature:(NSData *)slaveSignature {
- (instancetype)initInThread:(TSThread *)thread masterHexEncodedPublicKey:(NSString *)masterHexEncodedPublicKey slaveHexEncodedPublicKey:(NSString *)slaveHexEncodedPublicKey masterSignature:(NSData * _Nullable)masterSignature slaveSignature:(NSData *)slaveSignature {
self = [self initOutgoingMessageWithTimestamp:NSDate.ows_millisecondTimeStamp inThread:thread messageBody:@"" attachmentIds:[NSMutableArray<NSString *> new]
expiresInSeconds:0 expireStartedAt:0 isVoiceMessage:NO groupMetaMessage:TSGroupMetaMessageUnspecified quotedMessage:nil contactShare:nil linkPreview:nil];
if (self) {

View File

@ -151,10 +151,7 @@ public final class MultiDeviceProtocol : NSObject {
@objc(getAutoGeneratedMultiDeviceFRMessageForHexEncodedPublicKey:in:)
public static func getAutoGeneratedMultiDeviceFRMessage(for hexEncodedPublicKey: String, in transaction: YapDatabaseReadWriteTransaction) -> FriendRequestMessage {
let thread = TSContactThread.getOrCreateThread(withContactId: hexEncodedPublicKey, transaction: transaction)
let result = FriendRequestMessage(outgoingMessageWithTimestamp: NSDate.ows_millisecondTimeStamp(), in: thread,
messageBody: "Please accept to enable messages to be synced across devices",
attachmentIds: [], expiresInSeconds: 0, expireStartedAt: 0, isVoiceMessage: false,
groupMetaMessage: .unspecified, quotedMessage: nil, contactShare: nil, linkPreview: nil)
let result = FriendRequestMessage(timestamp: NSDate.ows_millisecondTimeStamp(), thread: thread, body: "Please accept to enable messages to be synced across devices")
result.skipSave = true // TODO: Why is this necessary again?
return result
}

View File

@ -23,6 +23,10 @@ enum LokiTestUtilities {
return Curve25519.generateKeyPair()
}
public static func getCurrentUserHexEncodedPublicKey() -> String {
return OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey
}
public static func generateHexEncodedPublicKey() -> String {
return generateKeyPair().hexEncodedPublicKey
}

View File

@ -1177,14 +1177,14 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
NSString *recipientID = signalMessageInfo[@"destination"];
uint64_t ttl = ((NSNumber *)signalMessageInfo[@"ttl"]).unsignedIntegerValue;
BOOL isPing = ((NSNumber *)signalMessageInfo[@"isPing"]).boolValue;
BOOL isFriendRequest = ((NSNumber *)signalMessageInfo[@"isFriendRequest"]).boolValue;
LKSignalMessage *signalMessage = [[LKSignalMessage alloc] initWithType:type timestamp:timestamp senderID:senderID senderDeviceID:senderDeviceID content:content recipientID:recipientID ttl:ttl isPing:isPing isFriendRequest:isFriendRequest];
LKSignalMessage *signalMessage = [[LKSignalMessage alloc] initWithType:type timestamp:timestamp senderID:senderID senderDeviceID:senderDeviceID content:content recipientID:recipientID ttl:ttl isPing:isPing];
BOOL shouldUpdateFriendRequestStatus = [LKFriendRequestProtocol shouldUpdateFriendRequestStatusFromMessage:message];
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
if (!message.skipSave) {
// Update the PoW calculation status
[message saveIsCalculatingProofOfWork:YES withTransaction:transaction];
}
if (signalMessage.isFriendRequest) {
if (shouldUpdateFriendRequestStatus) {
[LKFriendRequestProtocol setFriendRequestStatusToSendingIfNeededForHexEncodedPublicKey:recipientID transaction:transaction];
}
}];
@ -1196,7 +1196,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
// Update the PoW calculation status
[message saveIsCalculatingProofOfWork:NO withTransaction:transaction];
}
if (signalMessage.isFriendRequest) {
if (shouldUpdateFriendRequestStatus) {
[LKFriendRequestProtocol setFriendRequestStatusToFailedIfNeededForHexEncodedPublicKey:recipientID transaction:transaction];
}
}];
@ -1216,7 +1216,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
if (isSuccess) { return; } // Succeed as soon as the first promise succeeds
[NSNotificationCenter.defaultCenter postNotificationName:NSNotification.messageSent object:[[NSNumber alloc] initWithUnsignedLongLong:signalMessage.timestamp]];
isSuccess = YES;
if (signalMessage.isFriendRequest) {
if (shouldUpdateFriendRequestStatus) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
if (!message.skipSave) {
// Update the message
@ -1803,18 +1803,17 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
OWSFailDebug(@"Failed to encrypt friend request for: %@.", recipientId);
return nil;
}
OWSMessageServiceParams *messageParams =
[[OWSMessageServiceParams alloc] initWithType:TSFriendRequestMessageType
recipientId:recipientId
device:[deviceId intValue]
content:serializedMessage
isSilent:false
isOnline:false
registrationId:0
ttl:message.ttl
isPing:false
isFriendRequest:true];
[[OWSMessageServiceParams alloc] initWithType:TSFriendRequestMessageType
recipientId:recipientId
device:[deviceId intValue]
content:serializedMessage
isSilent:false
isOnline:false
registrationId:0
ttl:message.ttl
isPing:false];
NSError *error;
NSDictionary *jsonDict = [MTLJSONAdapter JSONDictionaryFromModel:messageParams error:&error];
@ -1849,7 +1848,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
@(OWSDevicePrimaryDeviceId));
}
BOOL isFriendRequest = [messageSend.message isKindOfClass:LKFriendRequestMessage.class];
BOOL isFriendRequestMessage = [messageSend.message isKindOfClass:LKFriendRequestMessage.class];
BOOL isDeviceLinkMessage = [messageSend.message isKindOfClass:LKDeviceLinkMessage.class]
&& ((LKDeviceLinkMessage *)messageSend.message).kind == LKDeviceLinkMessageKindRequest;
@ -1879,7 +1878,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
paddedPlaintext:[plainText paddedMessageBody]
senderCertificate:messageSend.senderCertificate
protocolContext:transaction
isFriendRequest:isFriendRequest || isDeviceLinkMessage
useFallbackSessionCipher:isFriendRequestMessage || isDeviceLinkMessage
error:&error];
SCKRaiseIfExceptionWrapperError(error);
@ -1910,8 +1909,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
isOnline:isOnline
registrationId:[cipher throws_remoteRegistrationId:transaction]
ttl:message.ttl
isPing:isPing
isFriendRequest:isFriendRequest || isDeviceLinkMessage];
isPing:isPing];
NSError *error;
NSDictionary *jsonDict = [MTLJSONAdapter JSONDictionaryFromModel:messageParams error:&error];

View File

@ -30,9 +30,6 @@ NS_ASSUME_NONNULL_BEGIN
// Loki: Wether this message is a p2p ping
@property (nonatomic, readonly) BOOL isPing;
// Loki: Wether this message is a friend request
@property (nonatomic, readonly) BOOL isFriendRequest;
- (instancetype)initWithType:(TSWhisperMessageType)type
recipientId:(NSString *)destination
device:(int)deviceId
@ -41,8 +38,7 @@ NS_ASSUME_NONNULL_BEGIN
isOnline:(BOOL)isOnline
registrationId:(int)registrationId
ttl:(uint)ttl
isPing:(BOOL)isPing
isFriendRequest:(BOOL)isFriendRequest;
isPing:(BOOL)isPing;
@end

View File

@ -24,7 +24,6 @@ NS_ASSUME_NONNULL_BEGIN
registrationId:(int)registrationId
ttl:(uint)ttl
isPing:(BOOL)isPing
isFriendRequest:(BOOL)isFriendRequest
{
self = [super init];
@ -41,7 +40,6 @@ NS_ASSUME_NONNULL_BEGIN
_online = isOnline;
_ttl = ttl;
_isPing = isPing;
_isFriendRequest = isFriendRequest;
return self;
}