add BITAuthenticator

class to control authentication and validation of an installation.

TODO:
* AuthenticatorViewController
* validation
* hooking into BITHockeyManager
This commit is contained in:
Stephan Diederich 2013-08-09 12:23:37 +02:00
parent c81a2b0940
commit 017b8495a8
9 changed files with 812 additions and 3 deletions

View File

@ -0,0 +1,21 @@
//
// BITAuthenticationViewController.h
// HockeySDK
//
// Created by Stephan Diederich on 08.08.13.
//
//
#import <UIKit/UIKit.h>
@protocol BITAuthenticationViewControllerDelegate;
@interface BITAuthenticationViewController : UIViewController
@end
@protocol BITAuthenticationViewControllerDelegate<NSObject>
- (void) authenticationViewControllerDidCancel:(UIViewController*) viewController;
- (void) authenticationViewController:(UIViewController*) viewController authenticatedWithToken:(NSString*) token;
@end

View File

@ -0,0 +1,38 @@
//
// BITAuthenticationViewController.m
// HockeySDK
//
// Created by Stephan Diederich on 08.08.13.
//
//
#import "BITAuthenticationViewController.h"
@interface BITAuthenticationViewController ()
@end
@implementation BITAuthenticationViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end

110
Classes/BITAuthenticator.h Normal file
View File

@ -0,0 +1,110 @@
//
// BITAuthenticator
// HockeySDK
//
// Created by Stephan Diederich on 08.08.13.
//
//
#import <Foundation/Foundation.h>
#import "BITHockeyBaseManager.h"
typedef NS_ENUM(NSUInteger, BITAuthenticatorAuthType) {
BITAuthenticatorAuthTypeEmail,
BITAuthenticatorAuthTypeEmailAndPassword,
//TODO: add Facebook?
};
//TODO: think about name. call it registration?!
typedef NS_ENUM(NSUInteger, BITAuthenticatorValidationType) {
BITAuthenticatorValidationTypeNever = 0, //never try to validate the current installation
BITAuthenticatorValidationTypeOptional, //asks the user if he wants to authenticate himself
BITAuthenticatorValidationTypeOnFirstLaunch, //checks if the user is authenticated first time a new version is run
BITAuthenticatorValidationTypeOnAppActive, //checks if the user is authenticated everytime the app becomes active
};
typedef void(^tAuthenticationCompletion)(NSString* authenticationToken, NSError *error);
typedef void(^tValidationCompletion)(BOOL validated, NSError *error);
@protocol BITAuthenticatorDelegate;
/**
* Authenticator module used to identify and optionally authenticate the current
* app installation
*/
@interface BITAuthenticator : BITHockeyBaseManager
#pragma mark - Configuration
@property (nonatomic, assign) BITAuthenticatorAuthType authenticationType;
/**
* defaults to BITAuthenticatorValidationTypeNever
*/
@property (nonatomic, assign) BITAuthenticatorValidationType validationType;
@property (nonatomic, weak) id<BITAuthenticatorDelegate> delegate;
#pragma mark - Identification
/**
* Provides an identification for the current app installation
*
* During Alpha and Beta-phase HockeyApp tries to uniquely identify each app installation
* to provide better error reporting & analytics. If authenticator is configured to login
* (@see BITAuthenticatorValidationType), this identifier is retrieved from HockeyApp. In case
* it is disabled, it returns this the current vendorIdentifier provided by UIKit.
*
* @return a string identifying this app installation
*/
- (NSString *) installationIdentification;
#pragma mark - Authentication
/**
* Authenticate this app installation
*
* Depending on 'authenticationType', this tries to authenticate the app installation
* against the HockeyApp server.
* You should not need to call this, as it's done automatically once the manager has
* been started, depending on validationType.
*
* @param completion if nil, success/failure is reported via the delegate, if not nil, the
* delegate methods are not called.
*/
- (void) authenticateWithCompletion:(tAuthenticationCompletion) completion;
#pragma mark - Validation
/**
* Validate the app installation
*
* Depending on @see loginOption, this is reset after the app becomes active and tries to revalidate
* the installation.
* You should not need to call this, as it's done automatically once the manager has
* been started, depending on validationType.
*
* @param completion if nil, success/failure is reported via the delegate, if not nil, the
* delegate methods are not called
*/
- (void) validateInstallationWithCompletion:(tValidationCompletion) completion;
@end
@protocol BITAuthenticatorDelegate <NSObject>
/**
* if the authentication (or validation) needs to authenticate the user,
* this delegate method is called with the viewController that we'll present.
*
* @param authenticator authenticator object
* @param viewController viewcontroller used to authenticate the user
*
*/
- (void) authenticator:(BITAuthenticator *)authenticator willShowAuthenticationController:(UIViewController*) viewController;
- (void) authenticatorDidAuthenticate:(BITAuthenticator*) authenticator;
- (void) authenticator:(BITAuthenticator*) authenticator failedToAuthenticateWithError:(NSError*) error;
- (void) authenticatorDidValidateInstallation:(BITAuthenticator*) authenticator;
- (void) authenticator:(BITAuthenticator*) authenticator failedToValidateInstallationWithError:(NSError*) error;
@end

254
Classes/BITAuthenticator.m Normal file
View File

@ -0,0 +1,254 @@
//
// BITAuthenticator
// HockeySDK
//
// Created by Stephan Diederich on 08.08.13.
//
//
#import "BITAuthenticator.h"
#import "HockeySDK.h"
#import "HockeySDKPrivate.h"
#import "BITAuthenticator_Private.h"
static NSString* const kBITAuthenticatorAuthTokenKey = @"BITAuthenticatorAuthTokenKey";
static NSString* const kBITAuthenticatorLastAuthenticatedVersionKey = @"BITAuthenticatorLastAuthenticatedVersionKey";
@implementation BITAuthenticator {
id _appDidBecomeActiveObserver;
UIViewController *_authenticationController;
}
- (void)dealloc {
[self unregisterObservers];
}
- (instancetype) initWithAppIdentifier:(NSString *)appIdentifier isAppStoreEnvironemt:(BOOL)isAppStoreEnvironment {
self = [super initWithAppIdentifier:appIdentifier isAppStoreEnvironemt:isAppStoreEnvironment];
if( self ) {
}
return self;
}
#pragma mark - BITHockeyBaseManager overrides
- (void)startManager {
[self registerObservers];
switch (self.validationType) {
case BITAuthenticatorValidationTypeOnAppActive:
[self validateInstallationWithCompletion:nil];
break;
case BITAuthenticatorValidationTypeOnFirstLaunch:
if(![self.lastAuthenticatedVersion isEqualToString:self.executableUUID]) {
[self validateInstallationWithCompletion:nil];
}
break;
case BITAuthenticatorValidationTypeNever:
case BITAuthenticatorValidationTypeOptional:
break;
}
}
#pragma mark -
- (NSString *)installationIdentification {
NSString *authToken = self.authenticationToken;
if(authToken) {
return authToken;
}
UIDevice *device = self.currentDevice;
if([device respondsToSelector:@selector(identifierForVendor)]) {
return self.currentDevice.identifierForVendor.UUIDString;
} else {
SEL uniqueIdentifier = NSSelectorFromString(@"uniqueIdentifier");
if([device respondsToSelector:uniqueIdentifier]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
NSString *uuid = [device performSelector:uniqueIdentifier];
#pragma clang diagnostic pop
return uuid;
} else {
//TODO: what?
return nil;
}
}
}
- (void) validateInstallationWithCompletion:(tValidationCompletion) completion {
if(nil == self.authenticationToken) {
[self authenticateWithCompletion:^(NSString *authenticationToken, NSError *error) {
if(nil == authenticationToken) {
//if authentication fails, there's nothing to validate
NSError *error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain
code:BITAuthenticatorNotAuthorized
userInfo:nil];
if(completion) {
completion(NO, error);
} else {
[self.delegate authenticator:self failedToValidateInstallationWithError:error];
}
} else {
if(completion) {
completion(YES, nil);
} else {
[self.delegate authenticatorDidValidateInstallation:self];
}
}
}];
} else {
NSError *error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain
code:BITAuthenticatorNetworkError
userInfo:nil];
if(completion) {
completion(NO, error);
} else {
[self.delegate authenticator:self failedToValidateInstallationWithError:error];
}
}
}
- (void)authenticateWithCompletion:(tAuthenticationCompletion)completion {
if(_authenticationController) {
BITHockeyLog(@"Already authenticating. Ignoring request");
return;
}
UIViewController *viewController = nil;
switch (self.authenticationType) {
case BITAuthenticatorAuthTypeEmail:
case BITAuthenticatorAuthTypeEmailAndPassword:
viewController = [UIViewController new];
//TODO
break;
}
if(viewController) {
[self.delegate authenticator:self willShowAuthenticationController:viewController];
_authenticationController = viewController;
_authenticationCompletionBlock = completion;
UIViewController *rootViewController = [self.findVisibleWindow rootViewController];
[rootViewController presentModalViewController:_authenticationController
animated:YES];
}
}
#pragma mark - AuthenticationViewControllerDelegate
- (void) authenticationViewControllerDidCancel:(UIViewController*) viewController {
_authenticationController = nil;
self.authenticationToken = nil;
NSError *error = [NSError errorWithDomain:kBITAuthenticatorErrorDomain
code:BITAuthenticatorAuthenticationCancelled
userInfo:nil];
if(self.authenticationCompletionBlock) {
self.authenticationCompletionBlock(self.authenticationToken, error);
self.authenticationCompletionBlock = nil;
} else {
[self.delegate authenticator:self failedToAuthenticateWithError:error];
}
}
- (void) authenticationViewController:(UIViewController*) viewController
authenticatedWithToken:(NSString*) token {
_authenticationController = nil;
self.authenticationToken = token;
if(self.authenticationCompletionBlock) {
self.authenticationCompletionBlock(self.authenticationToken, nil);
self.authenticationCompletionBlock = nil;
} else {
[self.delegate authenticatorDidAuthenticate:self];
}
}
#pragma mark - Validation Pseudo-Delegate
- (void)validationFailedWithError:(NSError *)validationError {
if(self.validationCompletion) {
self.validationCompletion(NO, validationError);
self.validationCompletion = nil;
} else {
[self.delegate authenticator:self failedToValidateInstallationWithError:validationError];
}
switch (self.validationType) {
case BITAuthenticatorValidationTypeNever:
case BITAuthenticatorValidationTypeOptional:
break;
case BITAuthenticatorValidationTypeOnAppActive:
case BITAuthenticatorValidationTypeOnFirstLaunch:
//TODO tell delegate and block the application
break;
}
}
- (void)validationSucceeded {
if(self.validationCompletion) {
self.validationCompletion(YES, nil);
self.validationCompletion = nil;
} else {
[self.delegate authenticatorDidValidateInstallation:self];
}
}
#pragma mark - Private helpers
- (UIDevice *)currentDevice {
return _currentDevice ? : [UIDevice currentDevice];;
}
- (void) cleanupInternalStorage {
[self removeKeyFromKeychain:kBITAuthenticatorAuthTokenKey];
[self setLastAuthenticatedVersion:nil];
}
- (void) registerObservers {
__weak typeof(self) weakSelf = self;
_appDidBecomeActiveObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification
object:nil
queue:NSOperationQueue.mainQueue
usingBlock:^(NSNotification *note) {
typeof(self) strongSelf = weakSelf;
[strongSelf applicationDidBecomeActive:note];
}];
}
- (void) unregisterObservers {
if(_appDidBecomeActiveObserver) {
[[NSNotificationCenter defaultCenter] removeObserver:_appDidBecomeActiveObserver];
_appDidBecomeActiveObserver = nil;
}
}
#pragma mark - Property overrides
- (void)setAuthenticationToken:(NSString *)authenticationToken {
if(![self.authenticationToken isEqualToString:authenticationToken]) {
if(nil == authenticationToken) {
[self removeKeyFromKeychain:kBITAuthenticatorAuthTokenKey];
} else {
[self addStringValueToKeychain:authenticationToken forKey:kBITAuthenticatorAuthTokenKey];
}
}
}
- (NSString *)authenticationToken {
return [self stringValueFromKeychainForKey:kBITAuthenticatorAuthTokenKey];
}
- (void)setLastAuthenticatedVersion:(NSString *)lastAuthenticatedVersion {
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
if(nil == lastAuthenticatedVersion){
[defaults removeObjectForKey:kBITAuthenticatorLastAuthenticatedVersionKey];
} else {
[defaults setObject:lastAuthenticatedVersion
forKey:kBITAuthenticatorLastAuthenticatedVersionKey];
[defaults synchronize];
}
}
- (NSString *)lastAuthenticatedVersion {
return [[NSUserDefaults standardUserDefaults] objectForKey:kBITAuthenticatorLastAuthenticatedVersionKey];
}
#pragma mark - Application Lifecycle
- (void)applicationDidBecomeActive:(NSNotification *)note {
if(BITAuthenticatorValidationTypeOnAppActive == self.validationType) {
[self validateInstallationWithCompletion:nil];
}
}
@end

View File

@ -0,0 +1,49 @@
//
// BITAuthenticator_Private.h
// HockeySDK
//
// Created by Stephan Diederich on 08.08.13.
//
//
#import "BITAuthenticator.h"
#import "BITHockeyBaseManagerPrivate.h"
#import "BITAuthenticationViewController.h"
@interface BITAuthenticator ()<BITAuthenticationViewControllerDelegate>
//can be set for testing
@property (nonatomic) UIDevice *currentDevice;
/**
* if set, this serves as the installationIdentifier.
* This is retrieved from the hockeyApp backend
* @see installationIdentifier
*/
@property (nonatomic, copy) NSString *authenticationToken;
/**
* holds the identifier of the last version that was authenticated
* only used if validation is set BITAuthenticatorValidationTypeOnFirstLaunch
*/
@property (nonatomic, copy) NSString *lastAuthenticatedVersion;
@property (nonatomic, copy) tAuthenticationCompletion authenticationCompletionBlock;
@property (nonatomic, copy) tValidationCompletion validationCompletion;
/**
* removes all previously stored authentication tokens, UDIDs, etc
*/
- (void) cleanupInternalStorage;
/**
* method registered as observer for applicationsDidBecomeActive events
*/
- (void) applicationDidBecomeActive:(NSNotification*) note;
#pragma mark - Validation callbacks
- (void) validationSucceeded;
- (void) validationFailedWithError:(NSError *) validationError;
@end

View File

@ -86,6 +86,15 @@ typedef enum {
} BITFeedbackErrorReason;
extern NSString *const __attribute__((unused)) kBITFeedbackErrorDomain;
// hockey Authenticator error domain
typedef NS_ENUM(NSInteger, BITAuthenticatorReason) {
BITAuthenticatorErrorUnknown,
BITAuthenticatorNetworkError,
BITAuthenticatorAPIServerReturnedInvalidRespone,
BITAuthenticatorNotAuthorized,
BITAuthenticatorAuthenticationCancelled,
};
extern NSString *const __attribute__((unused)) kBITAuthenticatorErrorDomain;
// HockeySDK

View File

@ -35,6 +35,7 @@ NSString *const kBITCrashErrorDomain = @"BITCrashReporterErrorDomain";
NSString *const kBITUpdateErrorDomain = @"BITUpdaterErrorDomain";
NSString *const kBITFeedbackErrorDomain = @"BITFeedbackErrorDomain";
NSString *const kBITHockeyErrorDomain = @"BITHockeyErrorDomain";
NSString *const kBITAuthenticatorErrorDomain = @"BITAuthenticatorErrorDomain";
// Load the framework bundle.
NSBundle *BITHockeyBundle(void) {

View File

@ -33,9 +33,9 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
1E0829001708F69A0073050E /* BITStoreUpdateManagerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E0828FF1708F69A0073050E /* BITStoreUpdateManagerDelegate.h */; };
1E0FEE28173BDB260061331F /* BITKeychainUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E0FEE26173BDB260061331F /* BITKeychainUtils.h */; };
1E0FEE29173BDB260061331F /* BITKeychainUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E0FEE27173BDB260061331F /* BITKeychainUtils.m */; };
1E0829001708F69A0073050E /* BITStoreUpdateManagerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E0828FF1708F69A0073050E /* BITStoreUpdateManagerDelegate.h */; };
1E1127C416580C87007067A2 /* buttonRoundedDelete.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E1127BC16580C87007067A2 /* buttonRoundedDelete.png */; };
1E1127C516580C87007067A2 /* buttonRoundedDelete@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E1127BD16580C87007067A2 /* buttonRoundedDelete@2x.png */; };
1E1127C616580C87007067A2 /* buttonRoundedDeleteHighlighted.png in Resources */ = {isa = PBXBuildFile; fileRef = 1E1127BE16580C87007067A2 /* buttonRoundedDeleteHighlighted.png */; };
@ -130,6 +130,11 @@
1EF95CA7162CB037000AE3AD /* BITFeedbackActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EF95CA5162CB036000AE3AD /* BITFeedbackActivity.m */; };
1EF95CAA162CB314000AE3AD /* BITFeedbackComposeViewControllerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EF95CA9162CB313000AE3AD /* BITFeedbackComposeViewControllerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
E405266217A2AD300096359C /* BITFeedbackManagerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = E405266117A2AD300096359C /* BITFeedbackManagerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
E48A3DEC17B3ED1C00924C3D /* BITAuthenticator.h in Headers */ = {isa = PBXBuildFile; fileRef = E48A3DEA17B3ED1C00924C3D /* BITAuthenticator.h */; };
E48A3DED17B3ED1C00924C3D /* BITAuthenticator.m in Sources */ = {isa = PBXBuildFile; fileRef = E48A3DEB17B3ED1C00924C3D /* BITAuthenticator.m */; };
E48A3DEF17B3EFF100924C3D /* BITAuthenticatorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E48A3DEE17B3EFF100924C3D /* BITAuthenticatorTests.m */; };
E4B4DB7D17B435550099C67F /* BITAuthenticationViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = E4B4DB7B17B435550099C67F /* BITAuthenticationViewController.h */; };
E4B4DB7E17B435550099C67F /* BITAuthenticationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E4B4DB7C17B435550099C67F /* BITAuthenticationViewController.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -164,10 +169,9 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
1E0FEE22173BD9BB0061331F /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
1E0828FF1708F69A0073050E /* BITStoreUpdateManagerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITStoreUpdateManagerDelegate.h; sourceTree = "<group>"; };
1E0FEE26173BDB260061331F /* BITKeychainUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITKeychainUtils.h; sourceTree = "<group>"; };
1E0FEE27173BDB260061331F /* BITKeychainUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITKeychainUtils.m; sourceTree = "<group>"; };
1E0828FF1708F69A0073050E /* BITStoreUpdateManagerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITStoreUpdateManagerDelegate.h; sourceTree = "<group>"; };
1E1127BC16580C87007067A2 /* buttonRoundedDelete.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = buttonRoundedDelete.png; sourceTree = "<group>"; };
1E1127BD16580C87007067A2 /* buttonRoundedDelete@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "buttonRoundedDelete@2x.png"; sourceTree = "<group>"; };
1E1127BE16580C87007067A2 /* buttonRoundedDeleteHighlighted.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = buttonRoundedDeleteHighlighted.png; sourceTree = "<group>"; };
@ -279,6 +283,12 @@
E41EB465148D7BF50015DEDC /* BITHockeyManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITHockeyManager.h; sourceTree = "<group>"; };
E41EB466148D7BF50015DEDC /* BITHockeyManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITHockeyManager.m; sourceTree = "<group>"; };
E41EB48B148D7C4E0015DEDC /* CrashReporter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CrashReporter.framework; path = ../Vendor/CrashReporter.framework; sourceTree = "<group>"; };
E48A3DEA17B3ED1C00924C3D /* BITAuthenticator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITAuthenticator.h; sourceTree = "<group>"; };
E48A3DEB17B3ED1C00924C3D /* BITAuthenticator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITAuthenticator.m; sourceTree = "<group>"; };
E48A3DEE17B3EFF100924C3D /* BITAuthenticatorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITAuthenticatorTests.m; sourceTree = "<group>"; };
E48A3DF117B408F400924C3D /* BITAuthenticator_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BITAuthenticator_Private.h; sourceTree = "<group>"; };
E4B4DB7B17B435550099C67F /* BITAuthenticationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITAuthenticationViewController.h; sourceTree = "<group>"; };
E4B4DB7C17B435550099C67F /* BITAuthenticationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITAuthenticationViewController.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -349,6 +359,7 @@
1EA1170216F53B49001C015C /* Fixtures */,
1E5A459716F0DFC200B55C04 /* Supporting Files */,
1E5A459D16F0DFC200B55C04 /* BITStoreUpdateManagerTests.m */,
E48A3DEE17B3EFF100924C3D /* BITAuthenticatorTests.m */,
);
path = HockeySDKTests;
sourceTree = "<group>";
@ -522,6 +533,7 @@
E41EB458148D7BF50015DEDC /* Classes */ = {
isa = PBXGroup;
children = (
E48A3DE917B3ECF900924C3D /* Network */,
1E754E441621F95E0070AB92 /* Helper */,
1E754E551621FBAF0070AB92 /* CrashReports */,
1E754E461621FA9A0070AB92 /* Feedback */,
@ -545,6 +557,26 @@
name = HockeySDK;
sourceTree = "<group>";
};
E48A3DE917B3ECF900924C3D /* Network */ = {
isa = PBXGroup;
children = (
E48A3DF017B408D800924C3D /* private */,
E48A3DEA17B3ED1C00924C3D /* BITAuthenticator.h */,
E48A3DEB17B3ED1C00924C3D /* BITAuthenticator.m */,
);
name = Network;
sourceTree = "<group>";
};
E48A3DF017B408D800924C3D /* private */ = {
isa = PBXGroup;
children = (
E48A3DF117B408F400924C3D /* BITAuthenticator_Private.h */,
E4B4DB7B17B435550099C67F /* BITAuthenticationViewController.h */,
E4B4DB7C17B435550099C67F /* BITAuthenticationViewController.m */,
);
name = private;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -560,6 +592,7 @@
1E49A4731612226D00463151 /* BITUpdateManager.h in Headers */,
1E49A4791612226D00463151 /* BITUpdateManagerDelegate.h in Headers */,
1E49A44E1612223B00463151 /* BITFeedbackManager.h in Headers */,
E4B4DB7D17B435550099C67F /* BITAuthenticationViewController.h in Headers */,
1E49A4481612223B00463151 /* BITFeedbackListViewController.h in Headers */,
1E49A47F1612226D00463151 /* BITUpdateViewController.h in Headers */,
1E49A43C1612223B00463151 /* BITFeedbackComposeViewController.h in Headers */,
@ -583,6 +616,7 @@
1E49A4D8161222D400463151 /* HockeySDKPrivate.h in Headers */,
1EC69F601615001500808FD9 /* BITHockeyManagerPrivate.h in Headers */,
1E754E5F1621FBB70070AB92 /* BITCrashManagerPrivate.h in Headers */,
E48A3DEC17B3ED1C00924C3D /* BITAuthenticator.h in Headers */,
1E754E601621FBB70070AB92 /* BITCrashReportTextFormatter.h in Headers */,
1EACC97B162F041E007578C5 /* BITAttributedLabel.h in Headers */,
1E0FEE28173BDB260061331F /* BITKeychainUtils.h in Headers */,
@ -796,12 +830,14 @@
1E49A4701612226D00463151 /* BITAppVersionMetaInfo.m in Sources */,
1E49A4761612226D00463151 /* BITUpdateManager.m in Sources */,
1E49A4821612226D00463151 /* BITUpdateViewController.m in Sources */,
E4B4DB7E17B435550099C67F /* BITAuthenticationViewController.m in Sources */,
1E49A4B2161222B900463151 /* BITHockeyBaseManager.m in Sources */,
1E49A4BB161222B900463151 /* BITHockeyBaseViewController.m in Sources */,
1E49A4C1161222B900463151 /* BITHockeyHelper.m in Sources */,
1E49A4C7161222B900463151 /* BITAppStoreHeader.m in Sources */,
1E49A4CD161222B900463151 /* BITStoreButton.m in Sources */,
1E49A4D3161222B900463151 /* BITWebTableViewCell.m in Sources */,
E48A3DED17B3ED1C00924C3D /* BITAuthenticator.m in Sources */,
1E49A4DB161222D400463151 /* HockeySDKPrivate.m in Sources */,
1E754E5D1621FBB70070AB92 /* BITCrashManager.m in Sources */,
1E754E611621FBB70070AB92 /* BITCrashReportTextFormatter.m in Sources */,
@ -824,6 +860,7 @@
buildActionMask = 2147483647;
files = (
1E5A459E16F0DFC200B55C04 /* BITStoreUpdateManagerTests.m in Sources */,
E48A3DEF17B3EFF100924C3D /* BITAuthenticatorTests.m in Sources */,
1EA1170716F53B91001C015C /* BITTestHelper.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -0,0 +1,290 @@
//
// HockeySDKTests.m
// HockeySDKTests
//
// Created by Andreas Linde on 13.03.13.
//
//
#import <SenTestingKit/SenTestingKit.h>
#define HC_SHORTHAND
#import <OCHamcrestIOS/OCHamcrestIOS.h>
#define MOCKITO_SHORTHAND
#import <OCMockitoIOS/OCMockitoIOS.h>
#import "HockeySDK.h"
#import "BITAuthenticator.h"
#import "BITAuthenticator_Private.h"
#import "BITTestHelper.h"
@interface MyDevice : NSObject
- (NSString*) uniqueIdentifier;
@end
@implementation MyDevice
- (NSString*) uniqueIdentifier {return @"reallyUnique";}
@end
@interface BITAuthenticatorTests : SenTestCase
@end
@implementation BITAuthenticatorTests {
BITAuthenticator *_sut;
}
- (void)setUp {
[super setUp];
_sut = [[BITAuthenticator alloc] initWithAppIdentifier:nil isAppStoreEnvironemt:YES];
}
- (void)tearDown {
[_sut cleanupInternalStorage];
_sut = nil;
[super tearDown];
}
#pragma mark - Setup helpers
- (NSDictionary *)jsonFromFixture:(NSString *)fixture {
NSString *dataString = [BITTestHelper jsonFixture:fixture];
NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSDictionary *json = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
return json;
}
#pragma mark - Setup Tests
- (void) testThatItInstantiates {
STAssertNotNil(_sut, @"Should be there");
}
#pragma mark - Persistence Tests
- (void) testThatAuthenticationTokenIsPersisted {
_sut.authenticationToken = @"SuperToken";
_sut = [[BITAuthenticator alloc] initWithAppIdentifier:nil isAppStoreEnvironemt:YES];
assertThat(_sut.authenticationToken, equalTo(@"SuperToken"));
}
- (void) testThatLastAuthenticatedVersionIsPersisted {
_sut.lastAuthenticatedVersion = @"1.2.1";
_sut = [[BITAuthenticator alloc] initWithAppIdentifier:nil isAppStoreEnvironemt:YES];
assertThat(_sut.lastAuthenticatedVersion, equalTo(@"1.2.1"));
}
- (void) testThatCleanupWorks {
_sut.authenticationToken = @"MyToken";
_sut.lastAuthenticatedVersion = @"1.2";
[_sut cleanupInternalStorage];
assertThat(_sut.authenticationToken, equalTo(nil));
assertThat(_sut.lastAuthenticatedVersion, equalTo(nil));
}
#pragma mark - Identification Tests
- (void) testIdentificationReturnsTheVendorIdentifierIfAvailable {
STAssertEqualObjects([_sut installationIdentification], [[UIDevice currentDevice] identifierForVendor].UUIDString,
@"Freshly initialized, it should return the vendor identifier");
}
- (void) testIdentificationReturnsTheUniqueIdentifier {
//use a device that responds to the old -(NSString*)uniqueIdentifier, but not to -(NSUUID*)identifierForVendor
MyDevice *device = [MyDevice new];
_sut.currentDevice = (UIDevice*)device;
NSString *hockeyUUID = [_sut installationIdentification];
STAssertEqualObjects(hockeyUUID, @"reallyUnique",
@"If there is no vendorIdentifier, it should use the old uniqueIdentifier");
}
- (void) testIdentificationReturnsTheAuthTokenIfSet {
_sut.authenticationToken = @"PeterPan";
assertThat(_sut.installationIdentification, equalTo(@"PeterPan"));
}
#pragma mark - authentication tests
- (void) testThatAuthenticateWithTypeEmailShowsAViewController {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
_sut.authenticationType = BITAuthenticatorAuthTypeEmail;
[_sut authenticateWithCompletion:nil];
[verifyCount(delegateMock, times(1)) authenticator:_sut willShowAuthenticationController:(id)anything()];
}
- (void) testThatAuthenticateWithTypeEmailAndPasswordShowsAViewController {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
_sut.authenticationType = BITAuthenticatorAuthTypeEmailAndPassword;
[_sut authenticateWithCompletion:nil];
[verifyCount(delegateMock, times(1)) authenticator:_sut willShowAuthenticationController:(id)anything()];
}
- (void) testThatIsDoesntShowMoreThanOneAuthenticationController {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
_sut.authenticationType = BITAuthenticatorAuthTypeEmailAndPassword;
[_sut authenticateWithCompletion:nil];
[_sut authenticateWithCompletion:nil];
[_sut authenticateWithCompletion:nil];
[verifyCount(delegateMock, times(1)) authenticator:_sut willShowAuthenticationController:(id)anything()];
}
- (void) testThatSuccessfulAuthenticationStoresTheToken {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
//this will prepare everything and show the viewcontroller
[_sut authenticateWithCompletion:nil];
//fake delegate call from the viewcontroller
[_sut authenticationViewController:nil authenticatedWithToken:@"SuperToken"];
assertThat(_sut.authenticationToken, equalTo(@"SuperToken"));
}
- (void) testThatSuccessfulAuthenticationCallsTheDelegate {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
_sut.authenticationToken = @"Test";
[_sut authenticateWithCompletion:nil];
[_sut authenticationViewController:nil authenticatedWithToken:@"SuperToken"];
[verify(delegateMock) authenticatorDidAuthenticate:_sut];
}
- (void) testThatCancelledAuthenticationCallsTheErrorDelegate {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
//this will prepare everything and show the viewcontroller
[_sut authenticateWithCompletion:nil];
//fake delegate call from the viewcontroller
[_sut authenticationViewControllerDidCancel:nil];
[verify(delegateMock) authenticator:_sut failedToAuthenticateWithError:[NSError errorWithDomain:kBITAuthenticatorErrorDomain
code:BITAuthenticatorAuthenticationCancelled
userInfo:nil]];
}
- (void) testThatCancelledAuthenticationResetsTheToken {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
_sut.authenticationToken = @"Meh";
//this will prepare everything and show the viewcontroller
[_sut authenticateWithCompletion:nil];
//fake delegate call from the viewcontroller
[_sut authenticationViewControllerDidCancel:nil];
assertThat(_sut.authenticationToken, equalTo(nil));
}
#pragma mark - validation tests
- (void) testThatValidationWithoutTokenWantsToShowTheAuthenticationViewController {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
_sut.validationType = BITAuthenticatorValidationTypeOptional;
[_sut validateInstallationWithCompletion:nil];
[verify(delegateMock) authenticator:_sut willShowAuthenticationController:(id)anything()];
}
- (void) testThatValidationFailsWithoutAnURL {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
_sut.authenticationToken = @"Test";
[_sut validateInstallationWithCompletion:nil];
[verify(delegateMock) authenticator:_sut failedToValidateInstallationWithError:(id)anything()];
}
#pragma mark - Lifetime checks
- (void) testThatValidationDoesntTriggerIfDisabled {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
_sut.validationType = BITAuthenticatorValidationTypeNever;
[_sut startManager];
[verifyCount(delegateMock, never()) authenticator:_sut willShowAuthenticationController:(id)anything()];
}
- (void) testThatValidationTriggersOnStart {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
_sut.validationType = BITAuthenticatorValidationTypeOnFirstLaunch;
[_sut startManager];
[verify(delegateMock) authenticator:_sut willShowAuthenticationController:(id)anything()];
}
- (void) testThatValidationTriggersOnDidBecomeActive {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
_sut.validationType = BITAuthenticatorValidationTypeOnAppActive;
[_sut applicationDidBecomeActive:nil];
[verify(delegateMock) authenticator:_sut willShowAuthenticationController:(id)anything()];
}
- (void) testThatValidationTriggersOnNewVersion {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
_sut.validationType = BITAuthenticatorValidationTypeOnFirstLaunch;
_sut.lastAuthenticatedVersion = @"111xxx";
[_sut startManager];
[verify(delegateMock) authenticator:_sut willShowAuthenticationController:(id)anything()];
}
- (void) testThatValidationDoesNotTriggerOnSameVersion {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
_sut.validationType = BITAuthenticatorValidationTypeOnFirstLaunch;
_sut.lastAuthenticatedVersion = _sut.executableUUID;
[_sut startManager];
[verifyCount(delegateMock, never()) authenticator:_sut willShowAuthenticationController:(id)anything()];
}
- (void) testThatFailedValidationCallsTheDelegate {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
_sut.validationType = BITAuthenticatorValidationTypeOnFirstLaunch;
[_sut validateInstallationWithCompletion:nil];
[_sut validationFailedWithError:nil];
[verifyCount(delegateMock, times(1)) authenticator:_sut failedToValidateInstallationWithError:(id)anything()];
[verifyCount(delegateMock, never()) authenticatorDidValidateInstallation:_sut];
}
- (void) testThatSuccessValidationCallsTheDelegate {
id delegateMock = mockProtocol(@protocol(BITAuthenticatorDelegate));
_sut.delegate = delegateMock;
_sut.validationType = BITAuthenticatorValidationTypeOnFirstLaunch;
[_sut validateInstallationWithCompletion:nil];
[_sut validationSucceeded];
[verifyCount(delegateMock, never()) authenticator:_sut failedToValidateInstallationWithError:(id)anything()];
[verifyCount(delegateMock, times(1)) authenticatorDidValidateInstallation:_sut];
}
@end