Rework profile fetch to reflect UD changes.

pull/1/head
Matthew Chen 7 years ago
parent 2eeba2d79f
commit 0b41e5e240

@ -1123,7 +1123,7 @@ typedef enum : NSUInteger {
{ {
[super viewDidAppear:animated]; [super viewDidAppear:animated];
[ProfileFetcherJob runWithThread:self.thread networkManager:self.networkManager]; [ProfileFetcherJob runWithThread:self.thread];
[self markVisibleMessagesAsRead]; [self markVisibleMessagesAsRead];
[self startReadTimer]; [self startReadTimer];
[self updateNavigationBarSubtitleLabel]; [self updateNavigationBarSubtitleLabel];

@ -88,9 +88,7 @@ public class OWS106EnsureProfileComplete: OWSDatabaseMigration {
let (promise, fulfill, reject) = Promise<Void>.pending() let (promise, fulfill, reject) = Promise<Void>.pending()
let networkManager = SSKEnvironment.shared.networkManager ProfileFetcherJob().getProfile(recipientId: localRecipientId).then { _ -> Void in
ProfileFetcherJob(networkManager: networkManager).getProfile(recipientId: localRecipientId).then { _ -> Void in
Logger.info("verified recipient profile is in good shape: \(localRecipientId)") Logger.info("verified recipient profile is in good shape: \(localRecipientId)")
fulfill(()) fulfill(())

@ -495,7 +495,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
if (!localNumber) { if (!localNumber) {
return; return;
} }
[ProfileFetcherJob runWithRecipientId:localNumber networkManager:self.networkManager ignoreThrottling:YES]; [ProfileFetcherJob runWithRecipientId:localNumber ignoreThrottling:YES];
} }
#pragma mark - Profile Whitelist #pragma mark - Profile Whitelist
@ -716,7 +716,6 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
completion:^{ completion:^{
dispatch_async(dispatch_get_main_queue(), ^(void) { dispatch_async(dispatch_get_main_queue(), ^(void) {
[ProfileFetcherJob runWithRecipientId:recipientId [ProfileFetcherJob runWithRecipientId:recipientId
networkManager:self.networkManager
ignoreThrottling:YES]; ignoreThrottling:YES];
}); });
}]; }];

@ -9,10 +9,6 @@ import SignalServiceKit
@objc @objc
public class ProfileFetcherJob: NSObject { public class ProfileFetcherJob: NSObject {
let networkManager: TSNetworkManager
let socketManager: TSSocketManager
let primaryStorage: OWSPrimaryStorage
// This property is only accessed on the main queue. // This property is only accessed on the main queue.
static var fetchDateMap = [String: Date]() static var fetchDateMap = [String: Date]()
@ -21,22 +17,39 @@ public class ProfileFetcherJob: NSObject {
var backgroundTask: OWSBackgroundTask? var backgroundTask: OWSBackgroundTask?
@objc @objc
public class func run(thread: TSThread, networkManager: TSNetworkManager) { public class func run(thread: TSThread) {
ProfileFetcherJob(networkManager: networkManager).run(recipientIds: thread.recipientIdentifiers) ProfileFetcherJob().run(recipientIds: thread.recipientIdentifiers)
} }
@objc @objc
public class func run(recipientId: String, networkManager: TSNetworkManager, ignoreThrottling: Bool) { public class func run(recipientId: String, ignoreThrottling: Bool) {
ProfileFetcherJob(networkManager: networkManager, ignoreThrottling: ignoreThrottling).run(recipientIds: [recipientId]) ProfileFetcherJob(ignoreThrottling: ignoreThrottling).run(recipientIds: [recipientId])
} }
public init(networkManager: TSNetworkManager, ignoreThrottling: Bool = false) { public init(ignoreThrottling: Bool = false) {
self.networkManager = networkManager
self.socketManager = TSSocketManager.shared()
self.primaryStorage = OWSPrimaryStorage.shared()
self.ignoreThrottling = ignoreThrottling self.ignoreThrottling = ignoreThrottling
} }
// MARK: - Dependencies
private var networkManager: TSNetworkManager {
return SSKEnvironment.shared.networkManager
}
private var socketManager: TSSocketManager {
return TSSocketManager.shared()
}
private var primaryStorage: OWSPrimaryStorage {
return SSKEnvironment.shared.primaryStorage
}
private var udManager: OWSUDManager {
return SSKEnvironment.shared.udManager
}
// MARK: -
public func run(recipientIds: [String]) { public func run(recipientIds: [String]) {
AssertIsOnMainThread() AssertIsOnMainThread()
@ -117,7 +130,7 @@ public class ProfileFetcherJob: NSObject {
self.socketManager.make(request, self.socketManager.make(request,
success: { (responseObject: Any?) -> Void in success: { (responseObject: Any?) -> Void in
do { do {
let profile = try SignalServiceProfile(recipientId: recipientId, rawResponse: responseObject) let profile = try SignalServiceProfile(recipientId: recipientId, responseObject: responseObject)
fulfill(profile) fulfill(profile)
} catch { } catch {
reject(error) reject(error)
@ -130,7 +143,7 @@ public class ProfileFetcherJob: NSObject {
self.networkManager.makeRequest(request, self.networkManager.makeRequest(request,
success: { (_: URLSessionDataTask?, responseObject: Any?) -> Void in success: { (_: URLSessionDataTask?, responseObject: Any?) -> Void in
do { do {
let profile = try SignalServiceProfile(recipientId: recipientId, rawResponse: responseObject) let profile = try SignalServiceProfile(recipientId: recipientId, responseObject: responseObject)
fulfill(profile) fulfill(profile)
} catch { } catch {
reject(error) reject(error)
@ -155,6 +168,13 @@ public class ProfileFetcherJob: NSObject {
OWSProfileManager.shared().updateProfile(forRecipientId: signalServiceProfile.recipientId, OWSProfileManager.shared().updateProfile(forRecipientId: signalServiceProfile.recipientId,
profileNameEncrypted: signalServiceProfile.profileNameEncrypted, profileNameEncrypted: signalServiceProfile.profileNameEncrypted,
avatarUrlPath: signalServiceProfile.avatarUrlPath) avatarUrlPath: signalServiceProfile.avatarUrlPath)
udManager.setShouldAllowUnrestrictedAccess(recipientId: signalServiceProfile.recipientId, shouldAllowUnrestrictedAccess: signalServiceProfile.hasUnrestrictedUnidentifiedAccess)
if signalServiceProfile.unidentifiedAccessKey != nil {
udManager.addUDRecipientId(signalServiceProfile.recipientId)
} else {
udManager.removeUDRecipientId(signalServiceProfile.recipientId)
}
} }
private func verifyIdentityUpToDateAsync(recipientId: String, latestIdentityKey: Data) { private func verifyIdentityUpToDateAsync(recipientId: String, latestIdentityKey: Data) {
@ -182,37 +202,33 @@ public class SignalServiceProfile: NSObject {
public let identityKey: Data public let identityKey: Data
public let profileNameEncrypted: Data? public let profileNameEncrypted: Data?
public let avatarUrlPath: String? public let avatarUrlPath: String?
public let unidentifiedAccessKey: Data?
public let hasUnrestrictedUnidentifiedAccess: Bool
init(recipientId: String, rawResponse: Any?) throws { init(recipientId: String, responseObject: Any?) throws {
self.recipientId = recipientId self.recipientId = recipientId
guard let responseDict = rawResponse as? [String: Any?] else { guard let params = ParamParser(responseObject: responseObject) else {
throw ValidationError.invalid(description: "unexpected type: \(String(describing: rawResponse))") throw ValidationError.invalid(description: "invalid response: \(String(describing: responseObject))")
} }
guard let identityKeyString = responseDict["identityKey"] as? String else { let identityKeyWithType = try params.requiredBase64EncodedData(key: "identityKey")
throw ValidationError.invalidIdentityKey(description: "missing identity key: \(String(describing: rawResponse))")
}
guard let identityKeyWithType = Data(base64Encoded: identityKeyString) else {
throw ValidationError.invalidIdentityKey(description: "unable to parse identity key: \(identityKeyString)")
}
let kIdentityKeyLength = 33 let kIdentityKeyLength = 33
guard identityKeyWithType.count == kIdentityKeyLength else { guard identityKeyWithType.count == kIdentityKeyLength else {
throw ValidationError.invalidIdentityKey(description: "malformed key \(identityKeyString) with decoded length: \(identityKeyWithType.count)") throw ValidationError.invalidIdentityKey(description: "malformed identity key \(identityKeyWithType.hexadecimalString) with decoded length: \(identityKeyWithType.count)")
} }
// `removeKeyType` is an objc category method only on NSData, so temporarily cast.
self.identityKey = (identityKeyWithType as NSData).removeKeyType() as Data
if let profileNameString = responseDict["name"] as? String { self.profileNameEncrypted = try params.optionalBase64EncodedData(key: "name")
guard let data = Data(base64Encoded: profileNameString) else {
throw ValidationError.invalidProfileName(description: "unable to parse profile name: \(profileNameString)")
}
self.profileNameEncrypted = data
} else {
self.profileNameEncrypted = nil
}
self.avatarUrlPath = responseDict["avatar"] as? String let avatarUrlPath: String? = try params.optional(key: "avatar")
self.avatarUrlPath = avatarUrlPath
// `removeKeyType` is an objc category method only on NSData, so temporarily cast. // TODO: Should this key be "unidentifiedAccessKey" or "unidentifiedAccess"?
self.identityKey = (identityKeyWithType as NSData).removeKeyType() as Data // The docs don't agree with the response from staging.
self.unidentifiedAccessKey = try params.optionalBase64EncodedData(key: "unidentifiedAccess")
self.hasUnrestrictedUnidentifiedAccess = try params.optionalBool(key: "unrestrictedUnidentifiedAccess", defaultValue: false)
} }
} }

@ -524,6 +524,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[recipientIds addObjectsFromArray:message.sendingRecipientIds]; [recipientIds addObjectsFromArray:message.sendingRecipientIds];
// Only send to members in the latest known group member list. // Only send to members in the latest known group member list.
[recipientIds intersectSet:[NSSet setWithArray:gThread.groupModel.groupMemberIds]]; [recipientIds intersectSet:[NSSet setWithArray:gThread.groupModel.groupMemberIds]];
if ([recipientIds containsObject:TSAccountManager.localNumber]) {
OWSFailDebug(@"Message send recipients should not include self.");
}
} else if ([message isKindOfClass:[OWSOutgoingSyncMessage class]]) { } else if ([message isKindOfClass:[OWSOutgoingSyncMessage class]]) {
[recipientIds addObject:[TSAccountManager localNumber]]; [recipientIds addObject:[TSAccountManager localNumber]];
} else if ([thread isKindOfClass:[TSContactThread class]]) { } else if ([thread isKindOfClass:[TSContactThread class]]) {
@ -545,6 +549,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
} }
[recipientIds addObject:recipientContactId]; [recipientIds addObject:recipientContactId];
if ([recipientIds containsObject:TSAccountManager.localNumber]) {
OWSFailDebug(@"Message send recipients should not include self.");
}
} else { } else {
// Neither a group nor contact thread? This should never happen. // Neither a group nor contact thread? This should never happen.
OWSFailDebug(@"Unknown message type: %@", [message class]); OWSFailDebug(@"Unknown message type: %@", [message class]);
@ -552,15 +560,11 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError(); NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
[error setIsRetryable:NO]; [error setIsRetryable:NO];
failureHandler(error); failureHandler(error);
return;
} }
[recipientIds minusSet:[NSSet setWithArray:self.blockingManager.blockedPhoneNumbers]]; [recipientIds minusSet:[NSSet setWithArray:self.blockingManager.blockedPhoneNumbers]];
if ([recipientIds containsObject:TSAccountManager.localNumber]) {
OWSFailDebug(@"Message send recipients should not include self.");
}
[recipientIds removeObject:TSAccountManager.localNumber];
// Mark skipped recipients as such. We skip because: // Mark skipped recipients as such. We skip because:
// //
// * Recipient is no longer in the group. // * Recipient is no longer in the group.

@ -39,9 +39,13 @@ public enum OWSUDError: Error {
// MARK: - Unrestricted Access // MARK: - Unrestricted Access
@objc func shouldAllowUnrestrictedAccess() -> Bool @objc func shouldAllowUnrestrictedAccessLocal() -> Bool
@objc func setShouldAllowUnrestrictedAccess(_ value: Bool) @objc func setShouldAllowUnrestrictedAccessLocal(_ value: Bool)
@objc func shouldAllowUnrestrictedAccess(recipientId: String) -> Bool
@objc func setShouldAllowUnrestrictedAccess(recipientId: String, shouldAllowUnrestrictedAccess: Bool)
} }
// MARK: - // MARK: -
@ -51,10 +55,11 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
private let dbConnection: YapDatabaseConnection private let dbConnection: YapDatabaseConnection
private let kUDRecipientModeCollection = "kUDRecipientModeCollection"
private let kUDCollection = "kUDCollection" private let kUDCollection = "kUDCollection"
private let kUDCurrentSenderCertificateKey = "kUDCurrentSenderCertificateKey" private let kUDCurrentSenderCertificateKey = "kUDCurrentSenderCertificateKey"
private let kUDUnrestrictedAccessKey = "kUDUnrestrictedAccessKey" private let kUDUnrestrictedAccessKey = "kUDUnrestrictedAccessKey"
private let kUDRecipientModeCollection = "kUDRecipientModeCollection"
private let kUDUnrestrictedAccessCollection = "kUDUnrestrictedAccessCollection"
@objc @objc
public required init(primaryStorage: OWSPrimaryStorage) { public required init(primaryStorage: OWSPrimaryStorage) {
@ -215,12 +220,22 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
// MARK: - Unrestricted Access // MARK: - Unrestricted Access
@objc @objc
public func shouldAllowUnrestrictedAccess() -> Bool { public func shouldAllowUnrestrictedAccessLocal() -> Bool {
return dbConnection.bool(forKey: kUDUnrestrictedAccessKey, inCollection: kUDRecipientModeCollection, defaultValue: false) return dbConnection.bool(forKey: kUDUnrestrictedAccessKey, inCollection: kUDCollection, defaultValue: false)
}
@objc
public func setShouldAllowUnrestrictedAccessLocal(_ value: Bool) {
dbConnection.setBool(value, forKey: kUDUnrestrictedAccessKey, inCollection: kUDCollection)
}
@objc
public func shouldAllowUnrestrictedAccess(recipientId: String) -> Bool {
return dbConnection.bool(forKey: recipientId, inCollection: kUDUnrestrictedAccessCollection, defaultValue: false)
} }
@objc @objc
public func setShouldAllowUnrestrictedAccess(_ value: Bool) { public func setShouldAllowUnrestrictedAccess(recipientId: String, shouldAllowUnrestrictedAccess: Bool) {
dbConnection.setBool(value, forKey: kUDUnrestrictedAccessKey, inCollection: kUDRecipientModeCollection) dbConnection.setBool(shouldAllowUnrestrictedAccess, forKey: recipientId, inCollection: kUDUnrestrictedAccessCollection)
} }
} }

@ -281,7 +281,7 @@ NS_ASSUME_NONNULL_BEGIN
// Crash app if UD cannot be enabled. // Crash app if UD cannot be enabled.
OWSFail(@"Could not determine UD access key: %@.", error); OWSFail(@"Could not determine UD access key: %@.", error);
} }
BOOL allowUnrestrictedUD = [self.udManager shouldAllowUnrestrictedAccess] && udAccessKey != nil; BOOL allowUnrestrictedUD = [self.udManager shouldAllowUnrestrictedAccessLocal] && udAccessKey != nil;
NSMutableDictionary *accountAttributes = [@{ NSMutableDictionary *accountAttributes = [@{
@"signalingKey" : signalingKey, @"signalingKey" : signalingKey,

@ -69,16 +69,31 @@ public class OWSFakeUDManager: NSObject, OWSUDManager {
// MARK: - Unrestricted Access // MARK: - Unrestricted Access
private var _shouldAllowUnrestrictedAccess = false private var _shouldAllowUnrestrictedAccessLocal = false
private var _shouldAllowUnrestrictedAccessSet = Set<String>()
@objc @objc
public func shouldAllowUnrestrictedAccess() -> Bool { public func shouldAllowUnrestrictedAccessLocal() -> Bool {
return _shouldAllowUnrestrictedAccess return _shouldAllowUnrestrictedAccessLocal
} }
@objc @objc
public func setShouldAllowUnrestrictedAccess(_ value: Bool) { public func setShouldAllowUnrestrictedAccessLocal(_ value: Bool) {
_shouldAllowUnrestrictedAccess = value _shouldAllowUnrestrictedAccessLocal = value
}
@objc
public func shouldAllowUnrestrictedAccess(recipientId: String) -> Bool {
return _shouldAllowUnrestrictedAccessSet.contains(recipientId)
}
@objc
public func setShouldAllowUnrestrictedAccess(recipientId: String, shouldAllowUnrestrictedAccess: Bool) {
if shouldAllowUnrestrictedAccess {
_shouldAllowUnrestrictedAccessSet.insert(recipientId)
} else {
_shouldAllowUnrestrictedAccessSet.remove(recipientId)
}
} }
} }

@ -139,4 +139,13 @@ public class ParamParser {
return data return data
} }
// MARK: Bool
public func optionalBool(key: Key, defaultValue: Bool) throws -> Bool {
guard let optionalValue: Bool = try optional(key: key) else {
return defaultValue
}
return optionalValue
}
} }

Loading…
Cancel
Save