From d93da3ee32e68ab656c2b7805ac512adf6b970fe Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Wed, 11 Sep 2013 14:05:09 +0200 Subject: [PATCH] More authenticator integration - Only start the updateManager if the installation is authenticated (not yet tested) - Provide the installation data to the updateManager requests - Some more documentation - Authenticator defaults to BITAuthenticatorAuthTypeUDIDProvider and BITAuthenticatorValidationTypeNever - Reset usage time if installation identification changes (not yet tested) --- Classes/BITAuthenticator.h | 37 +++++--- Classes/BITAuthenticator.m | 3 + Classes/BITAuthenticator_Private.h | 7 ++ Classes/BITHockeyManager.h | 4 + Classes/BITHockeyManager.m | 144 +++++++++++++++++++---------- Classes/BITUpdateManager.m | 42 ++++++++- Classes/BITUpdateManagerPrivate.h | 6 ++ Classes/HockeySDKPrivate.h | 3 +- 8 files changed, 180 insertions(+), 66 deletions(-) diff --git a/Classes/BITAuthenticator.h b/Classes/BITAuthenticator.h index e9326d02f8..20aa23612d 100644 --- a/Classes/BITAuthenticator.h +++ b/Classes/BITAuthenticator.h @@ -51,35 +51,48 @@ typedef void(^tValidationCompletion)(BOOL validated, NSError *error); @protocol BITAuthenticatorDelegate; /** - * Authenticator module used to identify and optionally authenticate the current - * app installation + * Authenticator module used to identify and optionally authenticate the current app installation + * + * This is the HockeySDK module for handling authentications when using Ad-Hoc or Enterprise provisioning profiles. + * This modul allows you to make sure the current app installation is done on an authorzied device by choosing from + * various authenticatoin and validation mechanisms which provide different levels of authentication. + * + * This does not provide DRM or copy protection in any form and each authentication type and validation type provide + * a different level of authentication. + * + * This module automatically disables itself when running in an App Store build by default! * * Authentication is actually a 2 step process: * 1) authenticate * some kind of token is aquired depending on the authenticationType - * 2) verification - * the aquired token from step 1 is verified depending the validationType + * 2) validation + * the aquired token from step 1 is validated depending the validationType * * There are currently 3 ways of authentication: - * 1) authenticate the user via email only - * 2) authenticate the user via email & passwort (needs to have a HockeyApp Account) - * 3) authenticate the device via its UDID + * 1) authenticate the user via email only (`BITAuthenticatorAuthTypeEmail`) + * 2) authenticate the user via email & passwort (needs to have a HockeyApp Account) (`BITAuthenticatorAuthTypeEmailAndPassword`) + * 3) authenticate the device via its UDID (_Default_) (`BITAuthenticatorAuthTypeUDIDProvider`) * * Additionally, verification can be required: - * 1) never - * 2) optional - * 3) on first launch of every app version, never again until the next version is installed - * 4) every time the app becomes active (needs data connection) + * 1) never (`BITAuthenticatorValidationTypeNever`) + * 2) optional (`BITAuthenticatorValidationTypeOptional`) + * 3) on first launch of every app version, never again until the next version is installed (_Default_) (`BITAuthenticatorValidationTypeOnFirstLaunch`) + * 4) every time the app becomes active (needs data connection) (`BITAuthenticatorValidationTypeOnAppActive`) * */ @interface BITAuthenticator : BITHockeyBaseManager #pragma mark - Configuration +/** + * Defines the authentication mechanism to be used + * + * _Default_: BITAuthenticatorAuthTypeUDIDProvider + */ @property (nonatomic, assign) BITAuthenticatorAuthType authenticationType; /** - * defaults to BITAuthenticatorValidationTypeNever + * _Default_: BITAuthenticatorValidationTypeNever */ @property (nonatomic, assign) BITAuthenticatorValidationType validationType; diff --git a/Classes/BITAuthenticator.m b/Classes/BITAuthenticator.m index f22cc6ea07..1a9fc47e8f 100644 --- a/Classes/BITAuthenticator.m +++ b/Classes/BITAuthenticator.m @@ -54,6 +54,9 @@ static NSString* const kBITAuthenticatorDidSkipOptionalLogin = @"BITAuthenticato self = [super initWithAppIdentifier:appIdentifier isAppStoreEnvironemt:isAppStoreEnvironment]; if( self ) { _webpageURL = [NSURL URLWithString:@"https://rink.hockeyapp.net/"]; + + _authenticationType = BITAuthenticatorAuthTypeUDIDProvider; + _validationType = BITAuthenticatorValidationTypeNever; } return self; } diff --git a/Classes/BITAuthenticator_Private.h b/Classes/BITAuthenticator_Private.h index da9f62220f..7108fe94ff 100644 --- a/Classes/BITAuthenticator_Private.h +++ b/Classes/BITAuthenticator_Private.h @@ -60,6 +60,13 @@ @property (nonatomic, copy) tAuthenticationCompletion authenticationCompletionBlock; +/** + * Specifies the type of installation identification for the current app installation + * + * KVO'able + */ +@property (nonatomic, readonly) BOOL installationIdentificationType; + /** * removes all previously stored authentication tokens, UDIDs, etc */ diff --git a/Classes/BITHockeyManager.h b/Classes/BITHockeyManager.h index f03431afa8..8a7a4a20d3 100644 --- a/Classes/BITHockeyManager.h +++ b/Classes/BITHockeyManager.h @@ -297,6 +297,9 @@ /** Reference to the initialized BITAuthenticator module + The authenticator is disabled by default. To enable it you need to set + `[BITAuthenticator authenticationType]` and `[BITAuthenticator validationType]` + Returns the BITAuthenticator instance initialized by BITHockeyManager @see configureWithIdentifier:delegate: @@ -305,6 +308,7 @@ */ @property (nonatomic, strong, readonly) BITAuthenticator *authenticator; + ///----------------------------------------------------------------------------- /// @name Environment ///----------------------------------------------------------------------------- diff --git a/Classes/BITHockeyManager.m b/Classes/BITHockeyManager.m index 3f63ae49d8..575ebd333a 100644 --- a/Classes/BITHockeyManager.m +++ b/Classes/BITHockeyManager.m @@ -55,6 +55,8 @@ BOOL _validAppIdentifier; BOOL _startManagerIsInvoked; + + BOOL _startUpdateManagerIsInvoked; } #pragma mark - Private Class Methods @@ -103,12 +105,14 @@ _disableCrashManager = NO; _disableUpdateManager = NO; - _enableStoreUpdateManager = NO; _disableFeedbackManager = NO; + + _enableStoreUpdateManager = NO; _appStoreEnvironment = NO; _startManagerIsInvoked = NO; - + _startUpdateManagerIsInvoked = NO; + #if !TARGET_IPHONE_SIMULATOR // check if we are really in an app store environment if (![[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"]) { @@ -158,15 +162,6 @@ BITHockeyLog(@"INFO: Starting HockeyManager"); _startManagerIsInvoked = YES; - // start Authenticator - if ( YES /* ![self isAuthenticatorDisabled] */) { - BITHockeyLog(@"INFO: Start Authenticator"); - if (_serverURL) { - [_authenticator setServerURL:_serverURL]; - } - [_authenticator startManager]; - } - // start CrashManager if (![self isCrashManagerDisabled]) { BITHockeyLog(@"INFO: Start CrashManager"); @@ -176,19 +171,6 @@ [_crashManager startManager]; } - // Setup UpdateManager - if (![self isUpdateManagerDisabled] -#if JIRA_MOBILE_CONNECT_SUPPORT_ENABLED - || [[self class] isJMCPresent] -#endif - ) { - BITHockeyLog(@"INFO: Start UpdateManager with small delay"); - if (_serverURL) { - [_updateManager setServerURL:_serverURL]; - } - [_updateManager performSelector:@selector(startManager) withObject:nil afterDelay:0.5f]; - } - // start StoreUpdateManager if ([self isStoreUpdateManagerEnabled]) { BITHockeyLog(@"INFO: Start StoreUpdateManager"); @@ -197,7 +179,7 @@ } [_storeUpdateManager performSelector:@selector(startManager) withObject:nil afterDelay:0.5f]; } - + // start FeedbackManager if (![self isFeedbackManagerDisabled]) { BITHockeyLog(@"INFO: Start FeedbackManager"); @@ -206,13 +188,29 @@ } [_feedbackManager performSelector:@selector(startManager) withObject:nil afterDelay:1.0f]; } -} - - -- (void)validateStartManagerIsInvoked { - if (_validAppIdentifier && !_appStoreEnvironment) { - if (!_startManagerIsInvoked) { - NSLog(@"[HockeySDK] ERROR: You did not call [[BITHockeyManager sharedHockeyManager] startManager] to startup the HockeySDK! Please do so after setting up all properties. The SDK is NOT running."); + + // start Authenticator + if (![self isAppStoreEnvironment]) { + // hook into manager with kvo! + [_authenticator addObserver:self forKeyPath:@"installationIdentificationValidated" options:0 context:nil]; + [_authenticator addObserver:self forKeyPath:@"installationIdentifier" options:0 context:nil]; + [_authenticator addObserver:self forKeyPath:@"installationIdentificationType" options:0 context:nil]; + + BITHockeyLog(@"INFO: Start Authenticator"); + if (_serverURL) { + [_authenticator setServerURL:_serverURL]; + } + [_authenticator startManager]; + } + + // Setup UpdateManager + if (![self isUpdateManagerDisabled] +#if JIRA_MOBILE_CONNECT_SUPPORT_ENABLED + || [[self class] isJMCPresent] +#endif + ) { + if ([self.authenticator installationIdentificationValidated]) { + [self invokeStartUpdateManager]; } } } @@ -227,8 +225,10 @@ - (void)setEnableStoreUpdateManager:(BOOL)enableStoreUpdateManager { + if (_storeUpdateManager) { + [_storeUpdateManager setEnableStoreUpdateManager:enableStoreUpdateManager]; + } _enableStoreUpdateManager = enableStoreUpdateManager; - [_storeUpdateManager setEnableStoreUpdateManager:enableStoreUpdateManager]; } @@ -253,8 +253,71 @@ } +#pragma mark - KVO + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + BOOL updateManagerIsAvailable = (![self isAppStoreEnvironment] && ![self isUpdateManagerDisabled]); + + // only make changes if we are visible + if ([keyPath isEqualToString:@"installationIdentificationValidated"] && + change[@"installationIdentificationValidated"] && + [change[@"installationIdentificationValidated"] isKindOfClass:[NSNumber class]] ) { + if (updateManagerIsAvailable) { + BOOL isValidated = [(NSNumber *)change[@"installationIdentificationValidated"] boolValue]; + [_updateManager setInstallationIdentificationValidated:isValidated]; + if (isValidated) { + [self invokeStartUpdateManager]; + } + } + } else if ([keyPath isEqualToString:@"installationIdentifier"] && + change[@"installationIdentifier"]) { + if (updateManagerIsAvailable) { + [_updateManager setInstallationIdentifier:change[@"installationIdentifier"]]; + } + } else if ([keyPath isEqualToString:@"installationIdentificationType"] && + change[@"installationIdentificationType"]) { + if (updateManagerIsAvailable) { + [_updateManager setInstallationIdentificationType:change[@"installationIdentificationType"]]; + } +#if JIRA_MOBILE_CONNECT_SUPPORT_ENABLED + } else if (([object trackerConfig]) && ([[object trackerConfig] isKindOfClass:[NSDictionary class]])) { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSMutableDictionary *trackerConfig = [[defaults valueForKey:@"BITTrackerConfigurations"] mutableCopy]; + if (!trackerConfig) { + trackerConfig = [NSMutableDictionary dictionaryWithCapacity:1]; + } + + [trackerConfig setValue:[object trackerConfig] forKey:_appIdentifier]; + [defaults setValue:trackerConfig forKey:@"BITTrackerConfigurations"]; + + [defaults synchronize]; + [self configureJMC]; +#endif + } +} + + #pragma mark - Private Instance Methods +- (void)validateStartManagerIsInvoked { + if (_validAppIdentifier && !_appStoreEnvironment) { + if (!_startManagerIsInvoked) { + NSLog(@"[HockeySDK] ERROR: You did not call [[BITHockeyManager sharedHockeyManager] startManager] to startup the HockeySDK! Please do so after setting up all properties. The SDK is NOT running."); + } + } +} + +- (void)invokeStartUpdateManager { + if (_startUpdateManagerIsInvoked) return; + + _startUpdateManagerIsInvoked = YES; + BITHockeyLog(@"INFO: Start UpdateManager"); + if (_serverURL) { + [_updateManager setServerURL:_serverURL]; + } + [_updateManager performSelector:@selector(startManager) withObject:nil afterDelay:0.5f]; +} + - (BOOL)isSetUpOnMainThread { NSString *errorString = @"ERROR: This SDK has to be setup on the main thread!"; @@ -424,21 +487,6 @@ } } -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if (([object trackerConfig]) && ([[object trackerConfig] isKindOfClass:[NSDictionary class]])) { - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - NSMutableDictionary *trackerConfig = [[defaults valueForKey:@"BITTrackerConfigurations"] mutableCopy]; - if (!trackerConfig) { - trackerConfig = [NSMutableDictionary dictionaryWithCapacity:1]; - } - - [trackerConfig setValue:[object trackerConfig] forKey:_appIdentifier]; - [defaults setValue:trackerConfig forKey:@"BITTrackerConfigurations"]; - - [defaults synchronize]; - [self configureJMC]; - } -} #endif @end diff --git a/Classes/BITUpdateManager.m b/Classes/BITUpdateManager.m index c6a5263168..331339446b 100644 --- a/Classes/BITUpdateManager.m +++ b/Classes/BITUpdateManager.m @@ -101,6 +101,7 @@ typedef NS_ENUM(NSInteger, BITUpdateAlertViewTag) { NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter]; [dnc addObserver:self selector:@selector(didBecomeActiveActions) name:UIApplicationDidBecomeActiveNotification object:nil]; [dnc addObserver:self selector:@selector(didBecomeActiveActions) name:BITHockeyNetworkDidBecomeReachableNotification object:nil]; + _installationIdentifier = [self stringValueFromKeychainForKey:kBITUpdateInstallationIdentifier]; _didSetupDidBecomeActiveNotifications = YES; } } @@ -110,6 +111,7 @@ typedef NS_ENUM(NSInteger, BITUpdateAlertViewTag) { [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil]; } + #pragma mark - Expiry - (BOOL)expiryDateReached { @@ -166,9 +168,8 @@ typedef NS_ENUM(NSInteger, BITUpdateAlertViewTag) { if (newVersion) { [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithDouble:[[NSDate date] timeIntervalSinceReferenceDate]] forKey:kBITUpdateDateOfVersionInstallation]; [[NSUserDefaults standardUserDefaults] setObject:_uuid forKey:kBITUpdateUsageTimeForUUID]; - [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithDouble:0] forKey:kBITUpdateUsageTimeOfCurrentVersion]; - [[NSUserDefaults standardUserDefaults] synchronize]; - } + [self storeUsageTimeForCurrentVersion:[NSNumber numberWithDouble:0]]; + } } - (void)stopUsage { @@ -177,7 +178,11 @@ typedef NS_ENUM(NSInteger, BITUpdateAlertViewTag) { double timeDifference = [[NSDate date] timeIntervalSinceReferenceDate] - [_usageStartTimestamp timeIntervalSinceReferenceDate]; double previousTimeDifference = [(NSNumber *)[[NSUserDefaults standardUserDefaults] valueForKey:kBITUpdateUsageTimeOfCurrentVersion] doubleValue]; - [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithDouble:previousTimeDifference + timeDifference] forKey:kBITUpdateUsageTimeOfCurrentVersion]; + [self storeUsageTimeForCurrentVersion:[NSNumber numberWithDouble:previousTimeDifference + timeDifference]]; +} + +- (void) storeUsageTimeForCurrentVersion:(NSNumber *)usageTime { + [[NSUserDefaults standardUserDefaults] setObject:usageTime forKey:kBITUpdateUsageTimeOfCurrentVersion]; [[NSUserDefaults standardUserDefaults] synchronize]; } @@ -534,6 +539,7 @@ typedef NS_ENUM(NSInteger, BITUpdateAlertViewTag) { - (void)checkForUpdate { if (![self isAppStoreEnvironment] && ![self isUpdateManagerDisabled]) { if ([self expiryDateReached]) return; + if (![self installationIdentificationValidated]) return; if (self.isUpdateAvailable && [self hasNewerMandatoryVersion]) { [self showCheckForUpdateAlert]; @@ -565,6 +571,14 @@ typedef NS_ENUM(NSInteger, BITUpdateAlertViewTag) { BITHOCKEY_VERSION, _uuid]; + // add installationIdentificationType and installationIdentifier if available + if (self.installationIdentifier && self.installationIdentificationType) { + [parameter appendFormat:@"&%@=%@", + bit_URLEncodedString(self.installationIdentificationType), + bit_URLEncodedString(self.installationIdentifier) + ]; + } + // add additional statistics if user didn't disable flag if (_sendUsageData) { [parameter appendFormat:@"&app_version=%@&os=iOS&os_version=%@&device=%@&lang=%@&first_start_at=%@&usage_time=%@", @@ -863,6 +877,26 @@ typedef NS_ENUM(NSInteger, BITUpdateAlertViewTag) { } } +- (void)setInstallationIdentificationValidated:(BOOL)installationIdentificationValidated { + if (installationIdentificationValidated != _installationIdentificationValidated) { + + } +} + +- (void)setInstallationIdentifier:(NSString *)installationIdentifier { + if (![_installationIdentifier isEqualToString:installationIdentifier]) { + if (installationIdentifier) { + [self addStringValueToKeychain:installationIdentifier forKey:kBITUpdateInstallationIdentifier]; + } else { + [self removeKeyFromKeychain:kBITUpdateInstallationIdentifier]; + } + + // we need to reset the usage time, because the user/device may have changed + [self storeUsageTimeForCurrentVersion:[NSNumber numberWithDouble:0]]; + self.usageStartTimestamp = [NSDate date]; + } +} + #pragma mark - UIAlertViewDelegate diff --git a/Classes/BITUpdateManagerPrivate.h b/Classes/BITUpdateManagerPrivate.h index 411a3b07c6..b06a22f571 100644 --- a/Classes/BITUpdateManagerPrivate.h +++ b/Classes/BITUpdateManagerPrivate.h @@ -58,6 +58,12 @@ @property (nonatomic, strong) NSString *companyName; +@property (nonatomic, strong) NSString *installationIdentifier; + +@property (nonatomic, strong) NSString *installationIdentificationType; + +@property (nonatomic) BOOL installationIdentificationValidated; + // if YES, the API will return an existing JMC config // if NO, the API will return only version information @property (nonatomic, assign) BOOL checkForTracker; diff --git a/Classes/HockeySDKPrivate.h b/Classes/HockeySDKPrivate.h index 3c0fab3383..11b7b292dc 100644 --- a/Classes/HockeySDKPrivate.h +++ b/Classes/HockeySDKPrivate.h @@ -49,8 +49,7 @@ #define kBITUpdateDateOfVersionInstallation @"BITUpdateDateOfVersionInstallation" #define kBITUpdateUsageTimeOfCurrentVersion @"BITUpdateUsageTimeOfCurrentVersion" #define kBITUpdateUsageTimeForUUID @"BITUpdateUsageTimeForUUID" -#define kBITUpdateAuthorizedVersion @"BITUpdateAuthorizedVersion" -#define kBITUpdateAuthorizedToken @"BITUpdateAuthorizedToken" +#define kBITUpdateInstallationIdentifier @"BITUpdateInstallationIdentifier" #define kBITStoreUpdateDateOfLastCheck @"BITStoreUpdateDateOfLastCheck" #define kBITStoreUpdateLastStoreVersion @"BITStoreUpdateLastStoreVersion"