FIRInstanceIDStore.m 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*
  2. * Copyright 2019 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "FIRInstanceIDStore.h"
  17. #import "FIRInstanceIDCheckinPreferences.h"
  18. #import "FIRInstanceIDCheckinStore.h"
  19. #import "FIRInstanceIDConstants.h"
  20. #import "FIRInstanceIDLogger.h"
  21. #import "FIRInstanceIDTokenStore.h"
  22. #import "FIRInstanceIDVersionUtilities.h"
  23. // NOTE: These values should be in sync with what InstanceID saves in as.
  24. static NSString *const kCheckinFileName = @"g-checkin";
  25. // APNS token (use the old key value i.e. with prefix GMS)
  26. static NSString *const kFIRInstanceIDLibraryVersion = @"GMSInstanceID-version";
  27. @interface FIRInstanceIDStore ()
  28. @property(nonatomic, readwrite, strong) FIRInstanceIDCheckinStore *checkinStore;
  29. @property(nonatomic, readwrite, strong) FIRInstanceIDTokenStore *tokenStore;
  30. @end
  31. @implementation FIRInstanceIDStore
  32. - (instancetype)initWithDelegate:(NSObject<FIRInstanceIDStoreDelegate> *)delegate {
  33. FIRInstanceIDCheckinStore *checkinStore = [[FIRInstanceIDCheckinStore alloc]
  34. initWithCheckinPlistFileName:kCheckinFileName
  35. applicationSupportSubDirectory:kFIRInstanceIDApplicationSupportSubDirectory];
  36. FIRInstanceIDTokenStore *tokenStore = [FIRInstanceIDTokenStore defaultStore];
  37. return [self initWithCheckinStore:checkinStore tokenStore:tokenStore delegate:delegate];
  38. }
  39. - (instancetype)initWithCheckinStore:(FIRInstanceIDCheckinStore *)checkinStore
  40. tokenStore:(FIRInstanceIDTokenStore *)tokenStore
  41. delegate:(NSObject<FIRInstanceIDStoreDelegate> *)delegate {
  42. self = [super init];
  43. if (self) {
  44. _checkinStore = checkinStore;
  45. _tokenStore = tokenStore;
  46. _delegate = delegate;
  47. [self resetCredentialsIfNeeded];
  48. }
  49. return self;
  50. }
  51. #pragma mark - Upgrades
  52. + (BOOL)hasApplicationSupportSubDirectory:(NSString *)subDirectoryName {
  53. NSString *subDirectoryPath = [self pathForApplicationSupportSubDirectory:subDirectoryName];
  54. BOOL isDirectory;
  55. if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
  56. isDirectory:&isDirectory]) {
  57. return NO;
  58. } else if (!isDirectory) {
  59. return NO;
  60. }
  61. return YES;
  62. }
  63. + (NSString *)pathForApplicationSupportSubDirectory:(NSString *)subDirectoryName {
  64. NSArray *directoryPaths =
  65. NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
  66. NSString *applicationSupportDirPath = directoryPaths.lastObject;
  67. NSArray *components = @[ applicationSupportDirPath, subDirectoryName ];
  68. return [NSString pathWithComponents:components];
  69. }
  70. + (BOOL)createApplicationSupportSubDirectory:(NSString *)subDirectoryName {
  71. NSString *subDirectoryPath = [self pathForApplicationSupportSubDirectory:subDirectoryName];
  72. BOOL hasSubDirectory;
  73. if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
  74. isDirectory:&hasSubDirectory]) {
  75. NSError *error;
  76. [[NSFileManager defaultManager] createDirectoryAtPath:subDirectoryPath
  77. withIntermediateDirectories:YES
  78. attributes:nil
  79. error:&error];
  80. if (error) {
  81. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore000,
  82. @"Cannot create directory %@, error: %@", subDirectoryPath, error);
  83. return NO;
  84. }
  85. } else {
  86. if (!hasSubDirectory) {
  87. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore001,
  88. @"Found file instead of directory at %@", subDirectoryPath);
  89. return NO;
  90. }
  91. }
  92. return YES;
  93. }
  94. + (BOOL)removeApplicationSupportSubDirectory:(NSString *)subDirectoryName error:(NSError **)error {
  95. if ([self hasApplicationSupportSubDirectory:subDirectoryName]) {
  96. NSString *subDirectoryPath = [self pathForApplicationSupportSubDirectory:subDirectoryName];
  97. BOOL isDirectory;
  98. if ([[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
  99. isDirectory:&isDirectory]) {
  100. return [[NSFileManager defaultManager] removeItemAtPath:subDirectoryPath error:error];
  101. }
  102. }
  103. return YES;
  104. }
  105. /**
  106. * Reset the keychain preferences if the app had been deleted earlier and then reinstalled.
  107. * Keychain preferences are not cleared in the above scenario so explicitly clear them.
  108. *
  109. * In case of an iCloud backup and restore the Keychain preferences should already be empty
  110. * since the Keychain items are marked with `*BackupThisDeviceOnly`.
  111. */
  112. - (void)resetCredentialsIfNeeded {
  113. BOOL checkinPlistExists = [self.checkinStore hasCheckinPlist];
  114. // Checkin info existed in backup excluded plist. Should not be a fresh install.
  115. if (checkinPlistExists) {
  116. // FCM user can still have the old version of checkin, migration should only happen once.
  117. [self.checkinStore migrateCheckinItemIfNeeded];
  118. return;
  119. }
  120. // reset checkin in keychain if a fresh install.
  121. // set the old checkin preferences to unregister pre-registered tokens
  122. FIRInstanceIDCheckinPreferences *oldCheckinPreferences =
  123. [self.checkinStore cachedCheckinPreferences];
  124. if (oldCheckinPreferences) {
  125. [self.checkinStore removeCheckinPreferencesWithHandler:^(NSError *error) {
  126. if (!error) {
  127. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore002,
  128. @"Removed cached checkin preferences from Keychain.");
  129. } else {
  130. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore003,
  131. @"Couldn't remove cached checkin preferences. Error: %@", error);
  132. }
  133. if (oldCheckinPreferences.deviceID.length && oldCheckinPreferences.secretToken.length) {
  134. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore006,
  135. @"App reset detected. Will delete server registrations.");
  136. // We don't really need to delete old FCM tokens created via IID auth tokens since
  137. // those tokens are already hashed by APNS token as the has so creating a new
  138. // token should automatically delete the old-token.
  139. [self.delegate store:self didDeleteFCMScopedTokensForCheckin:oldCheckinPreferences];
  140. } else {
  141. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore009,
  142. @"App reset detected but no valid checkin auth preferences found."
  143. @" Will not delete server registrations.");
  144. }
  145. }];
  146. }
  147. }
  148. #pragma mark - Get
  149. - (FIRInstanceIDTokenInfo *)tokenInfoWithAuthorizedEntity:(NSString *)authorizedEntity
  150. scope:(NSString *)scope {
  151. // TODO(chliangGoogle): If we don't have the token plist we should delete all the tokens from
  152. // the keychain. This is because not having the plist signifies a backup and restore operation.
  153. // In case the keychain has any tokens these would now be stale and therefore should be
  154. // deleted.
  155. if (![authorizedEntity length] || ![scope length]) {
  156. return nil;
  157. }
  158. FIRInstanceIDTokenInfo *info = [self.tokenStore tokenInfoWithAuthorizedEntity:authorizedEntity
  159. scope:scope];
  160. return info;
  161. }
  162. - (NSArray<FIRInstanceIDTokenInfo *> *)cachedTokenInfos {
  163. return [self.tokenStore cachedTokenInfos];
  164. }
  165. #pragma mark - Save
  166. - (void)saveTokenInfo:(FIRInstanceIDTokenInfo *)tokenInfo
  167. handler:(void (^)(NSError *error))handler {
  168. [self.tokenStore saveTokenInfo:tokenInfo handler:handler];
  169. }
  170. #pragma mark - Delete
  171. - (void)removeCachedTokenWithAuthorizedEntity:(NSString *)authorizedEntity scope:(NSString *)scope {
  172. if (![authorizedEntity length] || ![scope length]) {
  173. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore012,
  174. @"Will not delete token with invalid entity: %@, scope: %@",
  175. authorizedEntity, scope);
  176. return;
  177. }
  178. [self.tokenStore removeTokenWithAuthorizedEntity:authorizedEntity scope:scope];
  179. }
  180. - (void)removeAllCachedTokensWithHandler:(void (^)(NSError *error))handler {
  181. [self.tokenStore removeAllTokensWithHandler:handler];
  182. }
  183. #pragma mark - FIRInstanceIDCheckinCache protocol
  184. - (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences
  185. handler:(void (^)(NSError *error))handler {
  186. [self.checkinStore saveCheckinPreferences:preferences handler:handler];
  187. }
  188. - (FIRInstanceIDCheckinPreferences *)cachedCheckinPreferences {
  189. return [self.checkinStore cachedCheckinPreferences];
  190. }
  191. - (void)removeCheckinPreferencesWithHandler:(void (^)(NSError *))handler {
  192. [self.checkinStore removeCheckinPreferencesWithHandler:^(NSError *error) {
  193. if (handler) {
  194. handler(error);
  195. }
  196. }];
  197. }
  198. @end