Merge pull request #105 from loki-project/security

Fix Profile Picture Issues
This commit is contained in:
gmbnt 2020-02-17 10:47:47 +11:00 committed by GitHub
commit 251a93e529
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 32 additions and 32 deletions

View file

@ -5,7 +5,7 @@
<key>BuildDetails</key> <key>BuildDetails</key>
<dict> <dict>
<key>CarthageVersion</key> <key>CarthageVersion</key>
<string>0.33.0</string> <string>0.34.0</string>
<key>OSXVersion</key> <key>OSXVersion</key>
<string>10.15.3</string> <string>10.15.3</string>
<key>WebRTCCommit</key> <key>WebRTCCommit</key>

View file

@ -155,7 +155,7 @@ final class DisplayNameVC : UIViewController {
return showError(title: NSLocalizedString("Please pick a shorter display name", comment: "")) return showError(title: NSLocalizedString("Please pick a shorter display name", comment: ""))
} }
TSAccountManager.sharedInstance().didRegister() TSAccountManager.sharedInstance().didRegister()
OWSProfileManager.shared().updateLocalProfileName(displayName, avatarImage: nil, success: { }, failure: { }) // Try to save the user name but ignore the result OWSProfileManager.shared().updateLocalProfileName(displayName, avatarImage: nil, success: { }, failure: { _ in }) // Try to save the user name but ignore the result
let homeVC = HomeVC() let homeVC = HomeVC()
navigationController!.setViewControllers([ homeVC ], animated: true) navigationController!.setViewControllers([ homeVC ], animated: true)
} }

View file

@ -274,10 +274,16 @@ final class SettingsVC : UIViewController, AvatarViewHelperDelegate {
self.displayNameToBeUploaded = nil self.displayNameToBeUploaded = nil
} }
} }
}, failure: { }, failure: { error in
DispatchQueue.main.async { DispatchQueue.main.async {
modalActivityIndicator.dismiss { modalActivityIndicator.dismiss {
let alert = UIAlertController(title: NSLocalizedString("Couldn't Update Profile", comment: ""), message: NSLocalizedString("Please check your internet connection and try again", comment: ""), preferredStyle: .alert) var isMaxFileSizeExceeded = false
if let error = error as? LokiDotNetAPI.LokiDotNetAPIError {
isMaxFileSizeExceeded = (error == .maxFileSizeExceeded)
}
let title = isMaxFileSizeExceeded ? "Maximum File Size Exceeded" : NSLocalizedString("Couldn't Update Profile", comment: "")
let message = isMaxFileSizeExceeded ? "Please select a smaller photo and try again" : NSLocalizedString("Please check your internet connection and try again", comment: "")
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil) self?.present(alert, animated: true, completion: nil)
} }

View file

@ -419,7 +419,7 @@ NSString *const kProfileView_LastPresentedDate = @"kProfileView_LastPresentedDat
}]; }];
}); });
} }
failure:^{ failure:^(NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[modalActivityIndicator dismissWithCompletion:^{ [modalActivityIndicator dismissWithCompletion:^{
[OWSAlerts showErrorAlertWithMessage:NSLocalizedString( [OWSAlerts showErrorAlertWithMessage:NSLocalizedString(

View file

@ -170,7 +170,7 @@ public class OnboardingProfileViewController: OnboardingBaseViewController {
self.onboardingController.profileDidComplete(fromView: self) self.onboardingController.profileDidComplete(fromView: self)
}) })
} }
}, failure: { }, failure: { _ in
DispatchQueue.main.async { DispatchQueue.main.async {
modal.dismiss(completion: { modal.dismiss(completion: {
OWSAlerts.showErrorAlert(message: NSLocalizedString("PROFILE_VIEW_ERROR_UPDATE_FAILED", OWSAlerts.showErrorAlert(message: NSLocalizedString("PROFILE_VIEW_ERROR_UPDATE_FAILED",

View file

@ -336,7 +336,7 @@ NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKe
success:^{ success:^{
resolve(@(1)); resolve(@(1));
} }
failure:^{ failure:^(NSError *error) {
// Ignore errors related to local profile. // Ignore errors related to local profile.
resolve(@(1)); resolve(@(1));
}]; }];

View file

@ -50,7 +50,7 @@ extern const NSUInteger kOWSProfileManager_MaxAvatarDiameter;
- (void)updateLocalProfileName:(nullable NSString *)profileName - (void)updateLocalProfileName:(nullable NSString *)profileName
avatarImage:(nullable UIImage *)avatarImage avatarImage:(nullable UIImage *)avatarImage
success:(void (^)(void))successBlock success:(void (^)(void))successBlock
failure:(void (^)(void))failureBlock; failure:(void (^)(NSError *))failureBlock;
- (BOOL)isProfileNameTooLong:(nullable NSString *)profileName; - (BOOL)isProfileNameTooLong:(nullable NSString *)profileName;

View file

@ -229,13 +229,13 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
- (void)updateLocalProfileName:(nullable NSString *)profileName - (void)updateLocalProfileName:(nullable NSString *)profileName
avatarImage:(nullable UIImage *)avatarImage avatarImage:(nullable UIImage *)avatarImage
success:(void (^)(void))successBlockParameter success:(void (^)(void))successBlockParameter
failure:(void (^)(void))failureBlockParameter failure:(void (^)(NSError *))failureBlockParameter
{ {
OWSAssertDebug(successBlockParameter); OWSAssertDebug(successBlockParameter);
OWSAssertDebug(failureBlockParameter); OWSAssertDebug(failureBlockParameter);
// Ensure that the success and failure blocks are called on the main thread. // Ensure that the success and failure blocks are called on the main thread.
void (^failureBlock)(void) = ^{ void (^failureBlock)(NSError *) = ^(NSError *error) {
OWSLogError(@"Updating service with profile failed."); OWSLogError(@"Updating service with profile failed.");
// We use a "self-only" contact sync to indicate to desktop // We use a "self-only" contact sync to indicate to desktop
@ -247,7 +247,7 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
[[self.syncManager syncLocalContact] retainUntilComplete]; [[self.syncManager syncLocalContact] retainUntilComplete];
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
failureBlockParameter(); failureBlockParameter(error);
}); });
}; };
void (^successBlock)(void) = ^{ void (^successBlock)(void) = ^{
@ -288,7 +288,7 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
}]; }];
} }
failure:^(NSError *error) { failure:^(NSError *error) {
failureBlock(); failureBlock(error);
}]; }];
}; };
@ -319,11 +319,11 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
tryToUpdateService(avatarUrlPath, fileName); tryToUpdateService(avatarUrlPath, fileName);
} }
failure:^(NSError *error) { failure:^(NSError *error) {
failureBlock(); failureBlock(error);
}]; }];
} }
failure:^(NSError *error) { failure:^(NSError *error) {
failureBlock(); failureBlock(error);
}]; }];
} }
} else if (userProfile.avatarUrlPath) { } else if (userProfile.avatarUrlPath) {
@ -333,7 +333,7 @@ typedef void (^ProfileManagerFailureBlock)(NSError *error);
tryToUpdateService(nil, nil); tryToUpdateService(nil, nil);
} }
failure:^(NSError *error) { failure:^(NSError *error) {
failureBlock(); failureBlock(error);
}]; }];
} else { } else {
OWSLogVerbose(@"Updating local profile on service with no avatar."); OWSLogVerbose(@"Updating local profile on service with no avatar.");

View file

@ -100,10 +100,10 @@ public class LokiDotNetAPI : NSObject {
return seal.reject(error) return seal.reject(error)
} }
// Send the request // Send the request
func parseResponse(_ response: Any) { func parseResponse(_ responseObject: Any) {
// Parse the server ID & download URL // Parse the server ID & download URL
guard let json = response as? JSON, let data = json["data"] as? JSON, let serverID = data["id"] as? UInt64, let downloadURL = data["url"] as? String else { guard let json = responseObject as? JSON, let data = json["data"] as? JSON, let serverID = data["id"] as? UInt64, let downloadURL = data["url"] as? String else {
print("[Loki] Couldn't parse attachment from: \(response).") print("[Loki] Couldn't parse attachment from: \(responseObject).")
return seal.reject(LokiDotNetAPIError.parsingFailed) return seal.reject(LokiDotNetAPIError.parsingFailed)
} }
// Update the attachment // Update the attachment

View file

@ -138,6 +138,9 @@ public final class LokiFileServerAPI : LokiDotNetAPI {
// MARK: Profile Pictures (Public API) // MARK: Profile Pictures (Public API)
public static func setProfilePicture(_ profilePicture: Data) -> Promise<String> { public static func setProfilePicture(_ profilePicture: Data) -> Promise<String> {
return Promise<String>() { seal in return Promise<String>() { seal in
guard profilePicture.count < maxFileSize else {
return seal.reject(LokiDotNetAPIError.maxFileSizeExceeded)
}
getAuthToken(for: server).done { token in getAuthToken(for: server).done { token in
let url = "\(server)/users/me/avatar" let url = "\(server)/users/me/avatar"
let parameters: JSON = [ "type" : attachmentType, "Content-Type" : "application/binary" ] let parameters: JSON = [ "type" : attachmentType, "Content-Type" : "application/binary" ]
@ -150,24 +153,15 @@ public final class LokiFileServerAPI : LokiDotNetAPI {
print("[Loki] Couldn't upload profile picture due to error: \(error).") print("[Loki] Couldn't upload profile picture due to error: \(error).")
throw error throw error
} }
let task = AFURLSessionManager(sessionConfiguration: .default).uploadTask(withStreamedRequest: request as URLRequest, progress: nil, completionHandler: { response, responseObject, error in let _ = LokiFileServerProxy(for: server).performLokiFileServerNSURLRequest(request as NSURLRequest).done { responseObject in
if let error = error {
print("[Loki] Couldn't upload profile picture due to error: \(error).")
return seal.reject(error)
}
let statusCode = (response as! HTTPURLResponse).statusCode
let isSuccessful = (200...299) ~= statusCode
guard isSuccessful else {
print("[Loki] Couldn't upload profile picture.")
return seal.reject(LokiDotNetAPIError.generic)
}
guard let json = responseObject as? JSON, let data = json["data"] as? JSON, let profilePicture = data["avatar_image"] as? JSON, let downloadURL = profilePicture["url"] as? String else { guard let json = responseObject as? JSON, let data = json["data"] as? JSON, let profilePicture = data["avatar_image"] as? JSON, let downloadURL = profilePicture["url"] as? String else {
print("[Loki] Couldn't parse profile picture from: \(responseObject).") print("[Loki] Couldn't parse profile picture from: \(responseObject).")
return seal.reject(LokiDotNetAPIError.parsingFailed) return seal.reject(LokiDotNetAPIError.parsingFailed)
} }
return seal.fulfill(downloadURL) return seal.fulfill(downloadURL)
}) }.catch { error in
task.resume() seal.reject(error)
}
}.catch { error in }.catch { error in
print("[Loki] Couldn't upload profile picture due to error: \(error).") print("[Loki] Couldn't upload profile picture due to error: \(error).")
seal.reject(error) seal.reject(error)

View file

@ -106,7 +106,7 @@ internal class LokiFileServerProxy : LokiHTTPClient {
print("[Loki] Received an invalid response.") print("[Loki] Received an invalid response.")
throw Error.proxyResponseParsingFailed throw Error.proxyResponseParsingFailed
} }
let isSuccess = (200..<300).contains(statusCode) let isSuccess = (200...299) ~= statusCode
guard isSuccess else { throw HTTPError.networkError(code: statusCode, response: nil, underlyingError: Error.fileServerHTTPError(code: statusCode, message: nil)) } guard isSuccess else { throw HTTPError.networkError(code: statusCode, response: nil, underlyingError: Error.fileServerHTTPError(code: statusCode, message: nil)) }
let uncheckedJSONAsData = try DiffieHellman.decrypt(cipherText, using: symmetricKey) let uncheckedJSONAsData = try DiffieHellman.decrypt(cipherText, using: symmetricKey)
if uncheckedJSONAsData.isEmpty { return () } if uncheckedJSONAsData.isEmpty { return () }