FIRInstanceIDStore.m 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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 "Firebase/InstanceID/FIRInstanceIDStore.h"
  17. #import "Firebase/InstanceID/FIRInstanceIDCheckinStore.h"
  18. #import "Firebase/InstanceID/FIRInstanceIDConstants.h"
  19. #import "Firebase/InstanceID/FIRInstanceIDLogger.h"
  20. #import "Firebase/InstanceID/FIRInstanceIDTokenStore.h"
  21. #import "Firebase/InstanceID/Private/FIRInstanceIDCheckinPreferences.h"
  22. // NOTE: These values should be in sync with what InstanceID saves in as.
  23. static NSString *const kCheckinFileName = @"g-checkin";
  24. // APNS token (use the old key value i.e. with prefix GMS)
  25. static NSString *const kFIRInstanceIDLibraryVersion = @"GMSInstanceID-version";
  26. @interface FIRInstanceIDStore ()
  27. @property(nonatomic, readwrite, strong) FIRInstanceIDCheckinStore *checkinStore;
  28. @property(nonatomic, readwrite, strong) FIRInstanceIDTokenStore *tokenStore;
  29. @end
  30. @implementation FIRInstanceIDStore
  31. - (instancetype)initWithDelegate:(NSObject<FIRInstanceIDStoreDelegate> *)delegate {
  32. FIRInstanceIDCheckinStore *checkinStore = [[FIRInstanceIDCheckinStore alloc]
  33. initWithCheckinPlistFileName:kCheckinFileName
  34. subDirectoryName:kFIRInstanceIDSubDirectoryName];
  35. FIRInstanceIDTokenStore *tokenStore = [FIRInstanceIDTokenStore defaultStore];
  36. return [self initWithCheckinStore:checkinStore tokenStore:tokenStore delegate:delegate];
  37. }
  38. - (instancetype)initWithCheckinStore:(FIRInstanceIDCheckinStore *)checkinStore
  39. tokenStore:(FIRInstanceIDTokenStore *)tokenStore
  40. delegate:(NSObject<FIRInstanceIDStoreDelegate> *)delegate {
  41. self = [super init];
  42. if (self) {
  43. _checkinStore = checkinStore;
  44. _tokenStore = tokenStore;
  45. _delegate = delegate;
  46. [self resetCredentialsIfNeeded];
  47. }
  48. return self;
  49. }
  50. #pragma mark - Upgrades
  51. + (BOOL)hasSubDirectory:(NSString *)subDirectoryName {
  52. NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName];
  53. BOOL isDirectory;
  54. if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
  55. isDirectory:&isDirectory]) {
  56. return NO;
  57. } else if (!isDirectory) {
  58. return NO;
  59. }
  60. return YES;
  61. }
  62. + (NSSearchPathDirectory)supportedDirectory {
  63. #if TARGET_OS_TV
  64. return NSCachesDirectory;
  65. #else
  66. return NSApplicationSupportDirectory;
  67. #endif
  68. }
  69. + (NSString *)pathForSupportSubDirectory:(NSString *)subDirectoryName {
  70. NSArray *directoryPaths =
  71. NSSearchPathForDirectoriesInDomains([self supportedDirectory], NSUserDomainMask, YES);
  72. NSString *dirPath = directoryPaths.lastObject;
  73. NSArray *components = @[ dirPath, subDirectoryName ];
  74. return [NSString pathWithComponents:components];
  75. }
  76. + (BOOL)createSubDirectory:(NSString *)subDirectoryName {
  77. NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName];
  78. BOOL hasSubDirectory;
  79. if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
  80. isDirectory:&hasSubDirectory]) {
  81. NSError *error;
  82. [[NSFileManager defaultManager] createDirectoryAtPath:subDirectoryPath
  83. withIntermediateDirectories:YES
  84. attributes:nil
  85. error:&error];
  86. if (error) {
  87. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore000,
  88. @"Cannot create directory %@, error: %@", subDirectoryPath, error);
  89. return NO;
  90. }
  91. } else {
  92. if (!hasSubDirectory) {
  93. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore001,
  94. @"Found file instead of directory at %@", subDirectoryPath);
  95. return NO;
  96. }
  97. }
  98. return YES;
  99. }
  100. + (BOOL)removeSubDirectory:(NSString *)subDirectoryName error:(NSError **)error {
  101. if ([self hasSubDirectory:subDirectoryName]) {
  102. NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName];
  103. BOOL isDirectory;
  104. if ([[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
  105. isDirectory:&isDirectory]) {
  106. return [[NSFileManager defaultManager] removeItemAtPath:subDirectoryPath error:error];
  107. }
  108. }
  109. return YES;
  110. }
  111. /**
  112. * Reset the keychain preferences if the app had been deleted earlier and then reinstalled.
  113. * Keychain preferences are not cleared in the above scenario so explicitly clear them.
  114. *
  115. * In case of an iCloud backup and restore the Keychain preferences should already be empty
  116. * since the Keychain items are marked with `*BackupThisDeviceOnly`.
  117. */
  118. - (void)resetCredentialsIfNeeded {
  119. BOOL checkinPlistExists = [self.checkinStore hasCheckinPlist];
  120. // Checkin info existed in backup excluded plist. Should not be a fresh install.
  121. if (checkinPlistExists) {
  122. return;
  123. }
  124. // Resets checkin in keychain if a fresh install.
  125. // Keychain can still exist even if app is uninstalled.
  126. FIRInstanceIDCheckinPreferences *oldCheckinPreferences =
  127. [self.checkinStore cachedCheckinPreferences];
  128. if (oldCheckinPreferences) {
  129. [self.checkinStore removeCheckinPreferencesWithHandler:^(NSError *error) {
  130. if (!error) {
  131. FIRInstanceIDLoggerDebug(
  132. kFIRInstanceIDMessageCodeStore002,
  133. @"Removed cached checkin preferences from Keychain because this is a fresh install.");
  134. } else {
  135. FIRInstanceIDLoggerError(
  136. kFIRInstanceIDMessageCodeStore003,
  137. @"Couldn't remove cached checkin preferences for a fresh install. Error: %@", error);
  138. }
  139. if (oldCheckinPreferences.deviceID.length && oldCheckinPreferences.secretToken.length) {
  140. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore006,
  141. @"App reset detected. Will delete server registrations.");
  142. // We don't really need to delete old FCM tokens created via IID auth tokens since
  143. // those tokens are already hashed by APNS token as the has so creating a new
  144. // token should automatically delete the old-token.
  145. [self.delegate store:self didDeleteFCMScopedTokensForCheckin:oldCheckinPreferences];
  146. } else {
  147. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore009,
  148. @"App reset detected but no valid checkin auth preferences found."
  149. @" Will not delete server registrations.");
  150. }
  151. }];
  152. }
  153. }
  154. #pragma mark - Get
  155. - (FIRInstanceIDTokenInfo *)tokenInfoWithAuthorizedEntity:(NSString *)authorizedEntity
  156. scope:(NSString *)scope {
  157. // TODO(chliangGoogle): If we don't have the token plist we should delete all the tokens from
  158. // the keychain. This is because not having the plist signifies a backup and restore operation.
  159. // In case the keychain has any tokens these would now be stale and therefore should be
  160. // deleted.
  161. if (![authorizedEntity length] || ![scope length]) {
  162. return nil;
  163. }
  164. FIRInstanceIDTokenInfo *info = [self.tokenStore tokenInfoWithAuthorizedEntity:authorizedEntity
  165. scope:scope];
  166. return info;
  167. }
  168. - (NSArray<FIRInstanceIDTokenInfo *> *)cachedTokenInfos {
  169. return [self.tokenStore cachedTokenInfos];
  170. }
  171. #pragma mark - Save
  172. - (void)saveTokenInfo:(FIRInstanceIDTokenInfo *)tokenInfo
  173. handler:(void (^)(NSError *error))handler {
  174. [self.tokenStore saveTokenInfo:tokenInfo handler:handler];
  175. }
  176. - (void)saveTokenInfoInCacheOnly:(FIRInstanceIDTokenInfo *)tokenInfo {
  177. [self.tokenStore saveTokenInfoInCacheOnly:tokenInfo];
  178. }
  179. #pragma mark - Delete
  180. - (void)removeCachedTokenWithAuthorizedEntity:(NSString *)authorizedEntity scope:(NSString *)scope {
  181. if (![authorizedEntity length] || ![scope length]) {
  182. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore012,
  183. @"Will not delete token with invalid entity: %@, scope: %@",
  184. authorizedEntity, scope);
  185. return;
  186. }
  187. [self.tokenStore removeTokenWithAuthorizedEntity:authorizedEntity scope:scope];
  188. }
  189. - (void)removeAllCachedTokensWithHandler:(void (^)(NSError *error))handler {
  190. [self.tokenStore removeAllTokensWithHandler:handler];
  191. }
  192. #pragma mark - FIRInstanceIDCheckinCache protocol
  193. - (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences
  194. handler:(void (^)(NSError *error))handler {
  195. [self.checkinStore saveCheckinPreferences:preferences handler:handler];
  196. }
  197. - (FIRInstanceIDCheckinPreferences *)cachedCheckinPreferences {
  198. return [self.checkinStore cachedCheckinPreferences];
  199. }
  200. - (void)removeCheckinPreferencesWithHandler:(void (^)(NSError *))handler {
  201. [self.checkinStore removeCheckinPreferencesWithHandler:^(NSError *error) {
  202. if (handler) {
  203. handler(error);
  204. }
  205. }];
  206. }
  207. @end