diff --git a/Classes/BITAuthenticationViewController.h b/Classes/BITAuthenticationViewController.h index 5464418cd9..d84c6ddb03 100644 --- a/Classes/BITAuthenticationViewController.h +++ b/Classes/BITAuthenticationViewController.h @@ -15,6 +15,15 @@ - (instancetype) initWithDelegate:(id) delegate; +/** + * can be set to YES to show an additional button + description text + * and allowing to login via external website/UDID. + * if this is set to yes, no further email/password options are shown + * + * defaults to NO + */ +@property (nonatomic, assign) BOOL showsLoginViaWebButton; + /** * can be set to YES to also require the users password * @@ -41,6 +50,8 @@ */ - (void) authenticationViewControllerDidCancel:(UIViewController*) viewController; +- (void) authenticationViewControllerDidTapWebButton:(UIViewController*) viewController; + /** * called when the user wants to login * diff --git a/Classes/BITAuthenticationViewController.m b/Classes/BITAuthenticationViewController.m index 24a181d937..7af3114653 100644 --- a/Classes/BITAuthenticationViewController.m +++ b/Classes/BITAuthenticationViewController.m @@ -33,10 +33,14 @@ return self; } +#pragma mark - view lifecycle + - (void)viewDidLoad { [super viewDidLoad]; [self.tableView setScrollEnabled:NO]; + + [self updateWebLoginButton]; } - (void)viewWillAppear:(BOOL)animated { @@ -60,6 +64,7 @@ [[UIApplication sharedApplication] setStatusBarStyle:_statusBarStyle]; } +#pragma mark - Property overrides - (void)setShowsCancelButton:(BOOL)showsCancelButton { if(_showsCancelButton != showsCancelButton) { _showsCancelButton = showsCancelButton; @@ -77,6 +82,44 @@ } } +- (void)setShowsLoginViaWebButton:(BOOL)showsLoginViaWebButton { + if(_showsLoginViaWebButton != showsLoginViaWebButton) { + _showsLoginViaWebButton = showsLoginViaWebButton; + if(self.isViewLoaded) { + [self.tableView reloadData]; + [self updateWebLoginButton]; + } + } +} + +- (void) updateWebLoginButton { + if(self.showsLoginViaWebButton) { + static const CGFloat kFooterHeight = 60.f; + UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, + CGRectGetWidth(self.tableView.bounds), + kFooterHeight)]; + UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; + [button setTitle:BITHockeyLocalizedString(@"Show login page") forState:UIControlStateNormal]; + CGSize buttonSize = [button sizeThatFits:CGSizeMake(CGRectGetWidth(self.tableView.bounds), + kFooterHeight)]; + button.frame = CGRectMake(floorf((CGRectGetWidth(containerView.bounds) - buttonSize.width) / 2.f), + floorf((kFooterHeight - buttonSize.height) / 2.f), + buttonSize.width, + buttonSize.height); + button.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; + [containerView addSubview:button]; + [button addTarget:self + action:@selector(handleWebLoginButton:) + forControlEvents:UIControlEventTouchUpInside]; + self.tableView.tableFooterView = containerView; + } else { + self.tableView.tableFooterView = nil; + } +} + +- (IBAction) handleWebLoginButton:(id)sender { + [self.delegate authenticationViewControllerDidTapWebButton:self]; +} #pragma mark - UIViewController Rotation - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation { @@ -101,16 +144,24 @@ } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - NSInteger rows = 1; - - if ([self requirePassword]) rows ++; - - return rows; + if(self.showsLoginViaWebButton) { + return 0; + } else { + NSInteger rows = 1; + + if ([self requirePassword]) rows ++; + + return rows; + } } - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section { if (section == 0) { - return BITHockeyLocalizedString(@"HockeyAuthenticationViewControllerDataDescription"); + if(self.showsLoginViaWebButton) { + return BITHockeyLocalizedString(@"HockeyAuthenticationViewControllerWebLoginDescription"); + } else { + return BITHockeyLocalizedString(@"HockeyAuthenticationViewControllerDataDescription"); + } } return nil; diff --git a/Classes/BITAuthenticator.h b/Classes/BITAuthenticator.h index 79ba306f90..2ab9bc1c91 100644 --- a/Classes/BITAuthenticator.h +++ b/Classes/BITAuthenticator.h @@ -13,6 +13,7 @@ typedef NS_ENUM(NSUInteger, BITAuthenticatorAuthType) { BITAuthenticatorAuthTypeEmail, BITAuthenticatorAuthTypeEmailAndPassword, + BITAuthenticatorAuthTypeWebbased, //TODO: add Facebook? }; @@ -87,6 +88,35 @@ typedef void(^tValidationCompletion)(BOOL validated, NSError *error); */ @property (nonatomic, readonly) NSString *installationIdentification; +#pragma mark - HandleURL Helper + +/** + * should be used by the app-delegate to forward handle application:openURL:sourceApplication:annotation calls + * + * @param url URL that was passed to the app + * @param sourceApplication sourceApplication that was passed to the app + * @param annotation annotation that was passed to the app + * + * @return YES if the URL request was handled, NO if the URL could not be handled/identified + * + * Sample usage (in AppDelegate) + * - (BOOL)application:(UIApplication *)application + * openURL:(NSURL *)url + * sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { + * if ([[BITHockeyManager sharedHockeyManager].authenticator handleOpenURL:url + * sourceApplication:sourceApplication + * annotation:annotation]) { + * return YES; + * } else { + * //do your own URL handling, return appropriate valu + * } + * return NO; + } + */ +- (BOOL) handleOpenURL:(NSURL *) url + sourceApplication:(NSString *) sourceApplication + annotation:(id) annotation; + @end @protocol BITAuthenticatorDelegate diff --git a/Classes/BITAuthenticator.m b/Classes/BITAuthenticator.m index a96f82e6d7..f8e568185f 100644 --- a/Classes/BITAuthenticator.m +++ b/Classes/BITAuthenticator.m @@ -135,6 +135,9 @@ static NSString* const kBITAuthenticatorLastAuthenticatedVersionKey = @"BITAuthe case BITAuthenticatorAuthTypeEmailAndPassword: params = @{@"auid" : self.authenticationToken}; break; + case BITAuthenticatorAuthTypeWebbased: + params = @{@"udid" : self.authenticationToken}; + break; } return params; } @@ -197,19 +200,21 @@ static NSString* const kBITAuthenticatorLastAuthenticatedVersionKey = @"BITAuthe BITHockeyLog(@"Already authenticating. Ignoring request"); return; } - - BOOL requiresPassword; + + BITAuthenticationViewController *viewController = [[BITAuthenticationViewController alloc] initWithDelegate:self]; switch (self.authenticationType) { case BITAuthenticatorAuthTypeEmailAndPassword: - requiresPassword = YES; + viewController.requirePassword = YES; break; case BITAuthenticatorAuthTypeEmail: - requiresPassword = NO; + viewController.requirePassword = NO; + break; + case BITAuthenticatorAuthTypeWebbased: + viewController.requirePassword = NO; + viewController.showsLoginViaWebButton = YES; break; } - BITAuthenticationViewController *viewController = [[BITAuthenticationViewController alloc] initWithDelegate:self]; - viewController.requirePassword = requiresPassword; switch (self.validationType) { case BITAuthenticatorValidationTypeNever: case BITAuthenticatorValidationTypeOptional: @@ -375,6 +380,65 @@ static NSString* const kBITAuthenticatorLastAuthenticatedVersionKey = @"BITAuthe } } + +- (void)authenticationViewControllerDidTapWebButton:(UIViewController *)viewController { + NSURL *hockeyWebbasedLoginURL = [self.hockeyAppClient.baseURL URLByAppendingPathComponent:[NSString stringWithFormat:@"apps/%@/authorize", self.encodedAppIdentifier]]; + [[UIApplication sharedApplication] openURL:hockeyWebbasedLoginURL]; +} + +- (BOOL) handleOpenURL:(NSURL *) url + sourceApplication:(NSString *) sourceApplication + annotation:(id) annotation { + BOOL isValidURL = NO; + NSString *udid = [self UDIDFromOpenURL:url annotation:annotation isValidURL:&isValidURL]; + if(NO == isValidURL) { + //do nothing, was not for us + return NO; + } + + if(udid){ + [self didAuthenticateWithToken:udid]; + } else { + //reset auth-token + self.authenticationToken = nil; + + if(self.validationType == BITAuthenticatorValidationTypeOptional) { + //dismiss view-controller if login was optional + [_authenticationController dismissModalViewControllerAnimated:YES]; + _authenticationController = nil; + } else { + //keep the viewcontroller and thus block the app + } + } + return YES; +} + +- (NSString *) UDIDFromOpenURL:(NSURL *) url annotation:(id) annotation isValidURL:(BOOL*) isValid{ + NSString *urlScheme = [NSString stringWithFormat:@"ha%@", self.appIdentifier]; + if([[url scheme] isEqualToString:urlScheme]) { + if(isValid) { + *isValid = YES; + } + NSString *query = [url query]; + NSString *udid = nil; + //there should actually only one + static NSString * const UDIDQuerySpecifier = @"udid"; + for(NSString *queryComponents in [query componentsSeparatedByString:@"&"]) { + NSArray *parameterComponents = [queryComponents componentsSeparatedByString:@"="]; + if(2 == parameterComponents.count && [parameterComponents[0] isEqualToString:UDIDQuerySpecifier]) { + udid = parameterComponents[1]; + break; + } + } + return udid; + } else { + if(isValid) { + *isValid = NO; + } + return nil; + } +} + #pragma mark - Validation Pseudo-Delegate - (void)validationFailedWithError:(NSError *)validationError completion:(tValidationCompletion) completion{ if(completion) {