diff --git a/Classes/BITAuthenticator.m b/Classes/BITAuthenticator.m index cdd034e273..7505a486a8 100644 --- a/Classes/BITAuthenticator.m +++ b/Classes/BITAuthenticator.m @@ -41,22 +41,22 @@ #include -static NSString* const kBITAuthenticatorUUIDKey = @"BITAuthenticatorUUIDKey"; -static NSString* const kBITAuthenticatorIdentifierKey = @"BITAuthenticatorIdentifierKey"; -static NSString* const kBITAuthenticatorIdentifierTypeKey = @"BITAuthenticatorIdentifierTypeKey"; -static NSString* const kBITAuthenticatorLastAuthenticatedVersionKey = @"BITAuthenticatorLastAuthenticatedVersionKey"; -static NSString* const kBITAuthenticatorUserEmailKey = @"BITAuthenticatorUserEmailKey"; +static NSString *const kBITAuthenticatorUUIDKey = @"BITAuthenticatorUUIDKey"; +static NSString *const kBITAuthenticatorIdentifierKey = @"BITAuthenticatorIdentifierKey"; +static NSString *const kBITAuthenticatorIdentifierTypeKey = @"BITAuthenticatorIdentifierTypeKey"; +static NSString *const kBITAuthenticatorLastAuthenticatedVersionKey = @"BITAuthenticatorLastAuthenticatedVersionKey"; +static NSString *const kBITAuthenticatorUserEmailKey = @"BITAuthenticatorUserEmailKey"; //deprecated -static NSString* const kBITAuthenticatorAuthTokenKey = @"BITAuthenticatorAuthTokenKey"; -static NSString* const kBITAuthenticatorAuthTokenTypeKey = @"BITAuthenticatorAuthTokenTypeKey"; +static NSString *const kBITAuthenticatorAuthTokenKey = @"BITAuthenticatorAuthTokenKey"; +static NSString *const kBITAuthenticatorAuthTokenTypeKey = @"BITAuthenticatorAuthTokenTypeKey"; typedef unsigned int bit_uint32; static unsigned char kBITPNGHeader[8] = {137, 80, 78, 71, 13, 10, 26, 10}; static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; -@interface BITAuthenticator () +@interface BITAuthenticator() @property (nonatomic, assign) BOOL isSetup; @@ -71,7 +71,7 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; - (instancetype)initWithAppIdentifier:(NSString *)appIdentifier appEnvironment:(BITEnvironment)environment { self = [super initWithAppIdentifier:appIdentifier appEnvironment:environment]; - if( self ) { + if (self) { _webpageURL = [NSURL URLWithString:@"https://rink.hockeyapp.net/"]; _identificationType = BITAuthenticatorIdentificationTypeAnonymous; @@ -87,36 +87,19 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; } #pragma mark - BITHockeyBaseManager overrides + - (void)startManager { //disabled in TestFlight and the AppStore - if(self.appEnvironment != BITEnvironmentOther) return; + if (self.appEnvironment != BITEnvironmentOther) { return; } self.isSetup = YES; } #pragma mark - -/** - * This method has to be called on the main queue - */ -- (void)dismissAuthenticationControllerAnimated:(BOOL)animated completion:(void (^)(void))completion { - if (!_authenticationController) return; - UIViewController *presentingViewController = [_authenticationController presentingViewController]; - - // If there is no presenting view controller just remove view - if (presentingViewController) { - [_authenticationController dismissViewControllerAnimated:animated completion:completion]; - } else { - [_authenticationController.navigationController.view removeFromSuperview]; - if (completion) { - completion(); - } - } - self.authenticationController = nil; -} - (void)authenticateInstallation { //disabled in TestFlight and the AppStore - if(self.appEnvironment != BITEnvironmentOther) return; + if (self.appEnvironment != BITEnvironmentOther) { return; } // make sure this is called after startManager so all modules are fully setup if (!_isSetup) { @@ -136,10 +119,10 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; [self registerObservers]; } -- (void) authenticate { +- (void)authenticate { [self identifyWithCompletion:^(BOOL identified, NSError *error) { - if(identified) { - if([self needsValidation]) { + if (identified) { + if ([self needsValidation]) { [self validate]; } else { [self dismissAuthenticationControllerAnimated:YES completion:nil]; @@ -150,56 +133,33 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; }]; } -- (BOOL) needsValidation { - if(BITAuthenticatorIdentificationTypeAnonymous == self.identificationType) { - return NO; - } - if(NO == self.restrictApplicationUsage) { - return NO; - } - if(self.restrictionEnforcementFrequency == BITAuthenticatorAppRestrictionEnforcementOnFirstLaunch && - ![self.executableUUID isEqualToString:self.lastAuthenticatedVersion]) { - return YES; - } - if(NO == self.isValidated && self.restrictionEnforcementFrequency == BITAuthenticatorAppRestrictionEnforcementOnAppActive) { - return YES; - } - return NO; -} - -- (void)alertOnFailureStoringTokenInKeychain { - if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive) { - return; - } - - NSLog(@"[HockeySDK] ERROR: The authentication token could not be stored due to a keychain error. This is most likely a signing or keychain entitlement issue!"); -} +#pragma mark - Identification - (void)identifyWithCompletion:(void (^)(BOOL identified, NSError *))completion { - if(_authenticationController) { + if (_authenticationController) { BITHockeyLog(@"Authentication controller already visible. Ignoring identify request"); - if(completion) completion(NO, nil); + if (completion) { completion(NO, nil); } return; } //first check if the stored identification type matches the one currently configured NSString *storedTypeString = [self stringValueFromKeychainForKey:kBITAuthenticatorIdentifierTypeKey]; NSString *configuredTypeString = [self.class stringForIdentificationType:self.identificationType]; - if(storedTypeString && ![storedTypeString isEqualToString:configuredTypeString]) { + if (storedTypeString && ![storedTypeString isEqualToString:configuredTypeString]) { BITHockeyLog(@"Identification type mismatch for stored auth-token. Resetting."); [self storeInstallationIdentifier:nil withType:BITAuthenticatorIdentificationTypeAnonymous]; } NSString *identification = [self installationIdentifier]; - if(identification) { + if (identification) { self.identified = YES; - if(completion) completion(YES, nil); + if (completion) { completion(YES, nil); } return; } [self processFullSizeImage]; if (self.identified) { - if(completion) completion(YES, nil); + if (completion) { completion(YES, nil); } return; } @@ -209,7 +169,7 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; case BITAuthenticatorIdentificationTypeAnonymous: [self storeInstallationIdentifier:bit_UUID() withType:BITAuthenticatorIdentificationTypeAnonymous]; self.identified = YES; - if(completion) completion(YES, nil); + if (completion) { completion(YES, nil); } return; break; case BITAuthenticatorIdentificationTypeHockeyAppUser: @@ -230,11 +190,11 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; viewController.tableViewTitle = BITHockeyLocalizedString(@"HockeyAuthenticationViewControllerWebAuthLoginDescription"); break; case BITAuthenticatorIdentificationTypeHockeyAppEmail: - if(nil == self.authenticationSecret) { + if (nil == self.authenticationSecret) { NSError *error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain code:BITAuthenticatorAuthorizationSecretMissing - userInfo:@{NSLocalizedDescriptionKey : @"For email identification, the authentication secret must be set"}]; - if(completion) completion(NO, error); + userInfo:@{NSLocalizedDescriptionKey:@"For email identification, the authentication secret must be set"}]; + if (completion) { completion(NO, error); } return; } viewController = [[BITAuthenticationViewController alloc] initWithDelegate:self]; @@ -243,7 +203,7 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; break; } - if([self.delegate respondsToSelector:@selector(authenticator:willShowAuthenticationController:)]) { + if ([self.delegate respondsToSelector:@selector(authenticator:willShowAuthenticationController:)]) { [self.delegate authenticator:self willShowAuthenticationController:viewController]; } @@ -259,10 +219,27 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; #pragma mark - Validation -- (void) validate { +- (BOOL)needsValidation { + if (BITAuthenticatorIdentificationTypeAnonymous == self.identificationType) { + return NO; + } + if (NO == self.restrictApplicationUsage) { + return NO; + } + if (self.restrictionEnforcementFrequency == BITAuthenticatorAppRestrictionEnforcementOnFirstLaunch && + ![self.executableUUID isEqualToString:self.lastAuthenticatedVersion]) { + return YES; + } + if (NO == self.isValidated && self.restrictionEnforcementFrequency == BITAuthenticatorAppRestrictionEnforcementOnAppActive) { + return YES; + } + return NO; +} + +- (void)validate { [self validateWithCompletion:^(BOOL validated, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ - if(validated) { + if (validated) { [self dismissAuthenticationControllerAnimated:YES completion:nil]; } else { BITHockeyLog(@"Validation failed with error: %@", error); @@ -305,22 +282,22 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; }]; } -- (void) validateWithCompletion:(void (^)(BOOL validated, NSError *))completion { +- (void)validateWithCompletion:(void (^)(BOOL validated, NSError *))completion { BOOL requirementsFulfilled = YES; NSError *error = nil; - switch(self.identificationType) { + switch (self.identificationType) { case BITAuthenticatorIdentificationTypeAnonymous: { error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain code:BITAuthenticatorErrorUnknown - userInfo:@{NSLocalizedDescriptionKey : @"Anonymous users can't be validated"}]; + userInfo:@{NSLocalizedDescriptionKey:@"Anonymous users can't be validated"}]; requirementsFulfilled = NO; break; } case BITAuthenticatorIdentificationTypeHockeyAppEmail: - if(nil == self.authenticationSecret) { + if (nil == self.authenticationSecret) { error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain code:BITAuthenticatorAuthorizationSecretMissing - userInfo:@{NSLocalizedDescriptionKey : @"For email validation, the authentication secret must be set"}]; + userInfo:@{NSLocalizedDescriptionKey:@"For email validation, the authentication secret must be set"}]; requirementsFulfilled = NO; break; } @@ -328,16 +305,16 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; case BITAuthenticatorIdentificationTypeDevice: case BITAuthenticatorIdentificationTypeHockeyAppUser: case BITAuthenticatorIdentificationTypeWebAuth: - if(nil == self.installationIdentifier) { + if (nil == self.installationIdentifier) { error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain code:BITAuthenticatorNotIdentified - userInfo:@{NSLocalizedDescriptionKey : @"Make sure to identify the installation first"}]; + userInfo:@{NSLocalizedDescriptionKey:@"Make sure to identify the installation first"}]; requirementsFulfilled = NO; } break; } - if(NO == requirementsFulfilled) { - if(completion) { + if (NO == requirementsFulfilled) { + if (completion) { completion(NO, error); } return; @@ -345,7 +322,7 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; NSString *validationPath = [NSString stringWithFormat:@"api/3/apps/%@/identity/validate", self.encodedAppIdentifier]; - __weak typeof (self) weakSelf = self; + __weak typeof(self) weakSelf = self; if ([BITHockeyHelper isURLSessionSupported]) { NSURLRequest *request = [self.hockeyAppClient requestWithMethod:@"GET" path:validationPath parameters:[self validationParameters]]; @@ -353,28 +330,28 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; __block NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; NSURLSessionDataTask *task = [session dataTaskWithRequest:request - completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error) { - typeof (self) strongSelf = weakSelf; + completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + typeof(self) strongSelf = weakSelf; [session finishTasksAndInvalidate]; [strongSelf handleValidationResponseWithData:data error:error completion:completion]; }]; [task resume]; - }else{ + } else { [self.hockeyAppClient getPath:validationPath parameters:[self validationParameters] - completion:^(BITHTTPOperation *operation, NSData* responseData, NSError *error) { - typeof (self) strongSelf = weakSelf; + completion:^(BITHTTPOperation *operation, NSData *responseData, NSError *error) { + typeof(self) strongSelf = weakSelf; [strongSelf handleValidationResponseWithData:responseData error:error completion:completion]; }]; } } - (void)handleValidationResponseWithData:(NSData *)responseData error:(NSError *)error completion:(void (^)(BOOL validated, NSError *))completion { - if(nil == responseData) { - NSDictionary *userInfo = @{NSLocalizedDescriptionKey : BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}; - if(error) { + if (nil == responseData) { + NSDictionary *userInfo = @{NSLocalizedDescriptionKey:BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}; + if (error) { NSMutableDictionary *dict = [userInfo mutableCopy]; dict[NSUnderlyingErrorKey] = error; userInfo = dict; @@ -383,28 +360,28 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; code:BITAuthenticatorNetworkError userInfo:userInfo]; self.validated = NO; - if(completion) { completion(NO, error); } + if (completion) { completion(NO, error); } } else { NSError *validationParseError = nil; BOOL valid = [self.class isValidationResponseValid:responseData error:&validationParseError]; self.validated = valid; - if(valid) { + if (valid) { [self setLastAuthenticatedVersion:self.executableUUID]; } - if(completion) { completion(valid, validationParseError); } + if (completion) { completion(valid, validationParseError); } } } -- (NSDictionary*)validationParameters { +- (NSDictionary *)validationParameters { NSParameterAssert(self.installationIdentifier); NSParameterAssert(self.installationIdentifierParameterString); NSString *installString = bit_appAnonID(NO); if (installString) { - return @{self.installationIdentifierParameterString : self.installationIdentifier, @"install_string": installString}; + return @{self.installationIdentifierParameterString:self.installationIdentifier, @"install_string":installString}; } - return @{self.installationIdentifierParameterString : self.installationIdentifier}; + return @{self.installationIdentifierParameterString:self.installationIdentifier}; } + (BOOL)isValidationResponseValid:(id)response error:(NSError **)error { @@ -414,58 +391,82 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; id jsonObject = [NSJSONSerialization JSONObjectWithData:response options:0 error:&jsonParseError]; - if(nil == jsonObject) { - if(error) { + if (nil == jsonObject) { + if (error) { *error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain code:BITAuthenticatorAPIServerReturnedInvalidResponse - userInfo:@{NSLocalizedDescriptionKey : BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}]; + userInfo:@{NSLocalizedDescriptionKey:BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}]; } return NO; } - if(![jsonObject isKindOfClass:[NSDictionary class]]) { - if(error) { + if (![jsonObject isKindOfClass:[NSDictionary class]]) { + if (error) { *error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain code:BITAuthenticatorAPIServerReturnedInvalidResponse - userInfo:@{NSLocalizedDescriptionKey : BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}]; + userInfo:@{NSLocalizedDescriptionKey:BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}]; } return NO; } NSString *status = jsonObject[@"status"]; - if([status isEqualToString:@"not authorized"]) { - if(error) { + if ([status isEqualToString:@"not authorized"]) { + if (error) { *error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain code:BITAuthenticatorNotAuthorized - userInfo:@{NSLocalizedDescriptionKey : BITHockeyLocalizedString(@"HockeyAuthenticationNotMember")}]; + userInfo:@{NSLocalizedDescriptionKey:BITHockeyLocalizedString(@"HockeyAuthenticationNotMember")}]; } return NO; - } else if([status isEqualToString:@"not found"]) { - if(error) { + } else if ([status isEqualToString:@"not found"]) { + if (error) { *error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain code:BITAuthenticatorUnknownApplicationID - userInfo:@{NSLocalizedDescriptionKey : BITHockeyLocalizedString(@"HockeyAuthenticationContactDeveloper")}]; + userInfo:@{NSLocalizedDescriptionKey:BITHockeyLocalizedString(@"HockeyAuthenticationContactDeveloper")}]; } return NO; - } else if([status isEqualToString:@"validated"]) { + } else if ([status isEqualToString:@"validated"]) { return YES; } else { - if(error) { + if (error) { *error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain code:BITAuthenticatorAPIServerReturnedInvalidResponse - userInfo:@{NSLocalizedDescriptionKey : BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}]; + userInfo:@{NSLocalizedDescriptionKey:BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}]; } return NO; } } +#pragma mark - AuthenticationViewController Helper + +/** + * This method has to be called on the main queue + */ +- (void)dismissAuthenticationControllerAnimated:(BOOL)animated completion:(void (^)(void))completion { + if (!_authenticationController) { return; } + UIViewController *presentingViewController = [_authenticationController presentingViewController]; + + // If there is no presenting view controller just remove view + if (presentingViewController) { + [_authenticationController dismissViewControllerAnimated:animated completion:completion]; + } else { + [_authenticationController.navigationController.view removeFromSuperview]; + if (completion) { + completion(); + } + } + self.authenticationController = nil; +} + #pragma mark - AuthenticationViewControllerDelegate + - (void)authenticationViewController:(UIViewController *)viewController handleAuthenticationWithEmail:(NSString *)email password:(NSString *)password completion:(void (^)(BOOL, NSError *))completion { + NSParameterAssert(email && email.length); NSParameterAssert(self.identificationType == BITAuthenticatorIdentificationTypeHockeyAppEmail || (password && password.length)); - NSURLRequest* request = [self requestForAuthenticationEmail:email password:password]; + + NSURLRequest *request = [self requestForAuthenticationEmail:email password:password]; [self authenticationViewController:viewController handleAuthenticationWithEmail:email request:request completion:completion]; } @@ -474,38 +475,52 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; handleAuthenticationWithEmail:(NSString *)email request:(NSURLRequest *)request completion:(void (^)(BOOL, NSError *))completion { - __weak typeof (self) weakSelf = self; - if([BITHockeyHelper isURLSessionSupported]) { + + __weak typeof(self) weakSelf = self; + + if ([BITHockeyHelper isURLSessionSupported]) { NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; __block NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; NSURLSessionDataTask *task = [session dataTaskWithRequest:request - completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error) { - typeof (self) strongSelf = weakSelf; - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*) response; + completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + typeof(self) strongSelf = weakSelf; + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; [session finishTasksAndInvalidate]; [strongSelf handleAuthenticationWithResponse:httpResponse email:email data:data completion:completion]; }]; [task resume]; - }else{ + } else { BITHTTPOperation *operation = [self.hockeyAppClient operationWithURLRequest:request - completion:^(BITHTTPOperation *operation, NSData* responseData, NSError *error) { - typeof (self) strongSelf = weakSelf; - [strongSelf handleAuthenticationWithResponse:operation.response email:email data:responseData completion:completion]; + completion:^(BITHTTPOperation *operation, NSData *responseData, NSError *error) { + typeof(self) strongSelf = weakSelf; + [strongSelf handleAuthenticationWithResponse:operation.response + email:email + data:responseData + completion:completion]; }]; [self.hockeyAppClient enqeueHTTPOperation:operation]; } } -- (void)handleAuthenticationWithResponse:(NSHTTPURLResponse *)response email:(NSString *)email data:(NSData *)data completion:(void (^)(BOOL, NSError *))completion{ +- (void)authenticationViewControllerDidTapWebButton:(UIViewController *)viewController { + NSURL *url = [self deviceAuthenticationURL]; + if (url) { + [[UIApplication sharedApplication] openURL:url]; + } +} + +#pragma mark - Networking + +- (void)handleAuthenticationWithResponse:(NSHTTPURLResponse *)response email:(NSString *)email data:(NSData *)data completion:(void (^)(BOOL, NSError *))completion { NSError *authParseError = nil; NSString *authToken = [self.class authenticationTokenFromURLResponse:response data:data error:&authParseError]; BOOL identified; - if(authToken) { + if (authToken) { identified = YES; [self storeInstallationIdentifier:authToken withType:self.identificationType]; [self dismissAuthenticationControllerAnimated:YES completion:nil]; @@ -519,13 +534,13 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; } self.identified = identified; if (completion) { completion(identified, authParseError); } - if(self.identificationCompletion) { + if (self.identificationCompletion) { self.identificationCompletion(identified, authParseError); self.identificationCompletion = nil; } } -- (NSURLRequest *) requestForAuthenticationEmail:(NSString*) email password:(NSString*) password { +- (NSURLRequest *)requestForAuthenticationEmail:(NSString *)email password:(NSString *)password { NSString *authenticationPath = [self authenticationPath]; NSMutableDictionary *params = [NSMutableDictionary dictionary]; @@ -534,19 +549,19 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; params[@"install_string"] = installString; } - if(BITAuthenticatorIdentificationTypeHockeyAppEmail == self.identificationType) { + if (BITAuthenticatorIdentificationTypeHockeyAppEmail == self.identificationType) { NSString *authCode = BITHockeyMD5([NSString stringWithFormat:@"%@%@", - self.authenticationSecret ? : @"", - email ? : @""]); + self.authenticationSecret ?: @"", + email ?: @""]); - params[@"email"] = email ? : @""; + params[@"email"] = email ?: @""; params[@"authcode"] = authCode.lowercaseString; } NSMutableURLRequest *request = [self.hockeyAppClient requestWithMethod:@"POST" path:authenticationPath parameters:params]; - if(BITAuthenticatorIdentificationTypeHockeyAppUser == self.identificationType) { + if (BITAuthenticatorIdentificationTypeHockeyAppUser == self.identificationType) { NSString *authStr = [NSString stringWithFormat:@"%@:%@", email, password]; NSData *authData = [authStr dataUsingEncoding:NSUTF8StringEncoding]; NSString *authValue = [NSString stringWithFormat:@"Basic %@", bit_base64String(authData, authData.length)]; @@ -556,31 +571,31 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; return request; } -- (NSString *) authenticationPath { - if(BITAuthenticatorIdentificationTypeHockeyAppUser == self.identificationType) { +- (NSString *)authenticationPath { + if (BITAuthenticatorIdentificationTypeHockeyAppUser == self.identificationType) { return [NSString stringWithFormat:@"api/3/apps/%@/identity/authorize", self.encodedAppIdentifier]; } else { return [NSString stringWithFormat:@"api/3/apps/%@/identity/check", self.encodedAppIdentifier]; } } -+ (NSString *) authenticationTokenFromURLResponse:(NSHTTPURLResponse*) urlResponse data:(NSData*) data error:(NSError **) error { - if(nil == urlResponse) { - if(error) { ++ (NSString *)authenticationTokenFromURLResponse:(NSHTTPURLResponse *)urlResponse data:(NSData *)data error:(NSError **)error { + if (nil == urlResponse) { + if (error) { *error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain code:BITAuthenticatorAPIServerReturnedInvalidResponse - userInfo:@{ NSLocalizedDescriptionKey : BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}]; + userInfo:@{NSLocalizedDescriptionKey:BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}]; } return nil; } switch (urlResponse.statusCode) { case 401: - if(error) { + if (error) { *error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain code:BITAuthenticatorNotAuthorized userInfo:@{ - NSLocalizedDescriptionKey : BITHockeyLocalizedString(@"HockeyAuthenticationWrongEmailPassword") + NSLocalizedDescriptionKey:BITHockeyLocalizedString(@"HockeyAuthenticationWrongEmailPassword") }]; } break; @@ -589,15 +604,15 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; //Do nothing, handled below break; default: - if(error) { + if (error) { *error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain code:BITAuthenticatorAPIServerReturnedInvalidResponse - userInfo:@{ NSLocalizedDescriptionKey : BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}]; + userInfo:@{NSLocalizedDescriptionKey:BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}]; } break; } - if(200 != urlResponse.statusCode && 404 != urlResponse.statusCode) { + if (200 != urlResponse.statusCode && 404 != urlResponse.statusCode) { //make sure we have an error created if user wanted to have one NSParameterAssert(nil == error || *error); return nil; @@ -608,10 +623,10 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; options:0 error:&jsonParseError]; //no json or unexpected json - if(nil == jsonObject || ![jsonObject isKindOfClass:[NSDictionary class]]) { - if(error) { - NSDictionary *userInfo = @{NSLocalizedDescriptionKey: BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}; - if(jsonParseError) { + if (nil == jsonObject || ![jsonObject isKindOfClass:[NSDictionary class]]) { + if (error) { + NSDictionary *userInfo = @{NSLocalizedDescriptionKey:BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}; + if (jsonParseError) { NSMutableDictionary *userInfoMutable = [userInfo mutableCopy]; userInfoMutable[NSUnderlyingErrorKey] = jsonParseError; userInfo = userInfoMutable; @@ -625,23 +640,23 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; NSString *status = jsonObject[@"status"]; NSString *authToken = nil; - if([status isEqualToString:@"identified"]) { + if ([status isEqualToString:@"identified"]) { authToken = jsonObject[@"iuid"]; - } else if([status isEqualToString:@"authorized"]) { + } else if ([status isEqualToString:@"authorized"]) { authToken = jsonObject[@"auid"]; - } else if([status isEqualToString:@"not authorized"]) { - if(error) { + } else if ([status isEqualToString:@"not authorized"]) { + if (error) { *error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain code:BITAuthenticatorNotAuthorized - userInfo:@{NSLocalizedDescriptionKey: BITHockeyLocalizedString(@"HockeyAuthenticationNotMember")}]; + userInfo:@{NSLocalizedDescriptionKey:BITHockeyLocalizedString(@"HockeyAuthenticationNotMember")}]; } } //if no error is set yet, but error parameter is given, return a generic error - if(nil == authToken && error && nil == *error) { + if (nil == authToken && error && nil == *error) { *error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain code:BITAuthenticatorAPIServerReturnedInvalidResponse - userInfo:@{NSLocalizedDescriptionKey: BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}]; + userInfo:@{NSLocalizedDescriptionKey:BITHockeyLocalizedString(@"HockeyAuthenticationFailedAuthenticate")}]; } return authToken; } @@ -667,21 +682,14 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; return url; } -- (void)authenticationViewControllerDidTapWebButton:(UIViewController *)viewController { - NSURL *url = [self deviceAuthenticationURL]; - if(url) { - [[UIApplication sharedApplication] openURL:url]; - } -} - -- (BOOL) handleOpenURL:(NSURL *) url - sourceApplication:(NSString *) sourceApplication - annotation:(id) annotation { +- (BOOL)handleOpenURL:(NSURL *)url + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation { //check if this URL was meant for us, if not return NO so the user can //handle it NSString *const kAuthorizationHost = @"authorize"; - NSString *urlScheme = self.urlScheme ? : [NSString stringWithFormat:@"ha%@", self.appIdentifier]; - if(!([[url scheme] isEqualToString:urlScheme] && [[url host] isEqualToString:kAuthorizationHost])) { + NSString *urlScheme = self.urlScheme ?: [NSString stringWithFormat:@"ha%@", self.appIdentifier]; + if (!([[url scheme] isEqualToString:urlScheme] && [[url host] isEqualToString:kAuthorizationHost])) { BITHockeyLog(@"URL scheme for authentication doesn't match!"); return NO; } @@ -692,7 +700,7 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; case BITAuthenticatorIdentificationTypeWebAuth: { NSString *email = nil; [self.class email:&email andIUID:&installationIdentifier fromOpenURL:url]; - if(email) { + if (email) { BOOL success = [self addStringValueToKeychain:email forKey:kBITAuthenticatorUserEmailKey]; if (!success) { [self alertOnFailureStoringTokenInKeychain]; @@ -714,14 +722,14 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; return NO; } - if(installationIdentifier){ + if (installationIdentifier) { BITHockeyLog(@"Authentication succeeded."); - if(NO == self.restrictApplicationUsage) { + if (NO == self.restrictApplicationUsage) { [self dismissAuthenticationControllerAnimated:YES completion:nil]; } [self storeInstallationIdentifier:installationIdentifier withType:self.identificationType]; self.identified = YES; - if(self.identificationCompletion) { + if (self.identificationCompletion) { self.identificationCompletion(YES, nil); self.identificationCompletion = nil; } @@ -730,10 +738,10 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; BITHockeyLog(@"Resetting authentication token"); [self storeInstallationIdentifier:nil withType:self.identificationType]; self.identified = NO; - if(self.identificationCompletion) { + if (self.identificationCompletion) { NSError *error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain code:BITAuthenticatorErrorUnknown - userInfo:@{NSLocalizedDescriptionKey : localizedErrorDescription}]; + userInfo:@{NSLocalizedDescriptionKey:localizedErrorDescription}]; self.identificationCompletion(NO, error); self.identificationCompletion = nil; } @@ -741,14 +749,14 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; return YES; } -+ (NSString *) UDIDFromOpenURL:(NSURL *) url annotation:(id) annotation { ++ (NSString *)UDIDFromOpenURL:(NSURL *)url annotation:(id)annotation { NSString *query = [url query]; NSString *udid = nil; //there should actually only one - static NSString * const UDIDQuerySpecifier = @"udid"; - for(NSString *queryComponents in [query componentsSeparatedByString:@"&"]) { + static NSString *const UDIDQuerySpecifier = @"udid"; + for (NSString *queryComponents in [query componentsSeparatedByString:@"&"]) { NSArray *parameterComponents = [queryComponents componentsSeparatedByString:@"="]; - if(2 == parameterComponents.count && [parameterComponents[0] isEqualToString:UDIDQuerySpecifier]) { + if (2 == parameterComponents.count && [parameterComponents[0] isEqualToString:UDIDQuerySpecifier]) { udid = parameterComponents[1]; break; } @@ -756,16 +764,16 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; return udid; } -+ (void) email:(NSString**) email andIUID:(NSString**) iuid fromOpenURL:(NSURL *) url { ++ (void)email:(NSString **)email andIUID:(NSString **)iuid fromOpenURL:(NSURL *)url { NSString *query = [url query]; //there should actually only one - static NSString * const EmailQuerySpecifier = @"email"; - static NSString * const IUIDQuerySpecifier = @"iuid"; - for(NSString *queryComponents in [query componentsSeparatedByString:@"&"]) { + static NSString *const EmailQuerySpecifier = @"email"; + static NSString *const IUIDQuerySpecifier = @"iuid"; + for (NSString *queryComponents in [query componentsSeparatedByString:@"&"]) { NSArray *parameterComponents = [queryComponents componentsSeparatedByString:@"="]; - if(email && 2 == parameterComponents.count && [parameterComponents[0] isEqualToString:EmailQuerySpecifier]) { + if (email && 2 == parameterComponents.count && [parameterComponents[0] isEqualToString:EmailQuerySpecifier]) { *email = parameterComponents[1]; - } else if(iuid && 2 == parameterComponents.count && [parameterComponents[0] isEqualToString:IUIDQuerySpecifier]) { + } else if (iuid && 2 == parameterComponents.count && [parameterComponents[0] isEqualToString:IUIDQuerySpecifier]) { *iuid = parameterComponents[1]; } } @@ -773,7 +781,15 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; #pragma mark - Private helpers -- (void) cleanupInternalStorage { +- (void)alertOnFailureStoringTokenInKeychain { + if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive) { + return; + } + + NSLog(@"[HockeySDK] ERROR: The authentication token could not be stored due to a keychain error. This is most likely a signing or keychain entitlement issue!"); +} + +- (void)cleanupInternalStorage { [self removeKeyFromKeychain:kBITAuthenticatorIdentifierTypeKey]; [self removeKeyFromKeychain:kBITAuthenticatorIdentifierKey]; [self removeKeyFromKeychain:kBITAuthenticatorUUIDKey]; @@ -789,7 +805,7 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; #ifdef BIT_INTERNAL_DEBUG NSString* path = [[NSBundle mainBundle] pathForResource:@"iTunesArtwork" ofType:@"png"]; #else - NSString* path = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/../iTunesArtwork"]; + NSString *path = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/../iTunesArtwork"]; #endif struct stat fs; @@ -851,7 +867,7 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; } } - if (!memcmp(name, kBITPNGEndChunk, 4)){ + if (!memcmp(name, kBITPNGEndChunk, 4)) { chunk_index = 128; } } @@ -872,43 +888,45 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; } } -#pragma mark - KVO -- (void) registerObservers { +#pragma mark - NSNotification + +- (void)registerObservers { __weak typeof(self) weakSelf = self; - if(nil == self.appDidBecomeActiveObserver) { + if (nil == self.appDidBecomeActiveObserver) { self.appDidBecomeActiveObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification - object:nil - queue:NSOperationQueue.mainQueue - usingBlock:^(NSNotification *note) { - typeof(self) strongSelf = weakSelf; - [strongSelf applicationDidBecomeActive:note]; - }]; + object:nil + queue:NSOperationQueue.mainQueue + usingBlock:^(NSNotification *note) { + typeof(self) strongSelf = weakSelf; + [strongSelf applicationDidBecomeActive:note]; + }]; } - if(nil == self.appDidEnterBackgroundObserver) { + if (nil == self.appDidEnterBackgroundObserver) { self.appDidEnterBackgroundObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification - object:nil - queue:NSOperationQueue.mainQueue - usingBlock:^(NSNotification *note) { - typeof(self) strongSelf = weakSelf; - [strongSelf applicationDidEnterBackground:note]; - }]; + object:nil + queue:NSOperationQueue.mainQueue + usingBlock:^(NSNotification *note) { + typeof(self) strongSelf = weakSelf; + [strongSelf applicationDidEnterBackground:note]; + }]; } } -- (void) unregisterObservers { - if(self.appDidBecomeActiveObserver) { +- (void)unregisterObservers { + if (self.appDidBecomeActiveObserver) { [[NSNotificationCenter defaultCenter] removeObserver:self.appDidBecomeActiveObserver]; self.appDidBecomeActiveObserver = nil; } - if(self.appDidEnterBackgroundObserver) { + if (self.appDidEnterBackgroundObserver) { [[NSNotificationCenter defaultCenter] removeObserver:self.appDidEnterBackgroundObserver]; self.appDidEnterBackgroundObserver = nil; } } #pragma mark - Property overrides -- (void)storeInstallationIdentifier:(NSString *)installationIdentifier withType:(BITAuthenticatorIdentificationType) type { - if(nil == installationIdentifier) { + +- (void)storeInstallationIdentifier:(NSString *)installationIdentifier withType:(BITAuthenticatorIdentificationType)type { + if (nil == installationIdentifier) { [self removeKeyFromKeychain:kBITAuthenticatorIdentifierKey]; [self removeKeyFromKeychain:kBITAuthenticatorIdentifierTypeKey]; } else { @@ -924,14 +942,14 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; } } -- (NSString*) installationIdentifier { +- (NSString *)installationIdentifier { NSString *identifier = [self stringValueFromKeychainForKey:kBITAuthenticatorIdentifierKey]; return identifier; } - (void)setLastAuthenticatedVersion:(NSString *)lastAuthenticatedVersion { - NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - if(nil == lastAuthenticatedVersion){ + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + if (nil == lastAuthenticatedVersion) { [defaults removeObjectForKey:kBITAuthenticatorLastAuthenticatedVersionKey]; } else { [defaults setObject:lastAuthenticatedVersion @@ -945,28 +963,36 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; } - (NSString *)installationIdentifierParameterString { - switch(self.identificationType) { + switch (self.identificationType) { case BITAuthenticatorIdentificationTypeHockeyAppEmail: case BITAuthenticatorIdentificationTypeWebAuth: return @"iuid"; - case BITAuthenticatorIdentificationTypeHockeyAppUser: return @"auid"; - case BITAuthenticatorIdentificationTypeDevice: return @"udid"; - case BITAuthenticatorIdentificationTypeAnonymous: return @"uuid"; + case BITAuthenticatorIdentificationTypeHockeyAppUser: + return @"auid"; + case BITAuthenticatorIdentificationTypeDevice: + return @"udid"; + case BITAuthenticatorIdentificationTypeAnonymous: + return @"uuid"; } } -+ (NSString *)stringForIdentificationType:(BITAuthenticatorIdentificationType) identificationType { - switch(identificationType) { - case BITAuthenticatorIdentificationTypeHockeyAppEmail: return @"iuid"; - case BITAuthenticatorIdentificationTypeWebAuth: return @"webAuth"; - case BITAuthenticatorIdentificationTypeHockeyAppUser: return @"auid"; - case BITAuthenticatorIdentificationTypeDevice: return @"udid"; - case BITAuthenticatorIdentificationTypeAnonymous: return @"uuid"; ++ (NSString *)stringForIdentificationType:(BITAuthenticatorIdentificationType)identificationType { + switch (identificationType) { + case BITAuthenticatorIdentificationTypeHockeyAppEmail: + return @"iuid"; + case BITAuthenticatorIdentificationTypeWebAuth: + return @"webAuth"; + case BITAuthenticatorIdentificationTypeHockeyAppUser: + return @"auid"; + case BITAuthenticatorIdentificationTypeDevice: + return @"udid"; + case BITAuthenticatorIdentificationTypeAnonymous: + return @"uuid"; } } - (void)setIdentificationType:(BITAuthenticatorIdentificationType)identificationType { - if(_identificationType != identificationType) { + if (_identificationType != identificationType) { _identificationType = identificationType; self.identified = NO; self.validated = NO; @@ -986,12 +1012,13 @@ static unsigned char kBITPNGEndChunk[4] = {0x49, 0x45, 0x4e, 0x44}; } #pragma mark - Application Lifecycle + - (void)applicationDidBecomeActive:(NSNotification *)note { [self authenticate]; } - (void)applicationDidEnterBackground:(NSNotification *)note { - if(BITAuthenticatorAppRestrictionEnforcementOnAppActive == self.restrictionEnforcementFrequency) { + if (BITAuthenticatorAppRestrictionEnforcementOnAppActive == self.restrictionEnforcementFrequency) { self.validated = NO; } }