You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

240 lines
9.0 KiB

  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. subDirectoryName:kFIRInstanceIDSubDirectoryName];
  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)hasSubDirectory:(NSString *)subDirectoryName {
  53. NSString *subDirectoryPath = [self pathForSupportSubDirectory: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. + (NSSearchPathDirectory)supportedDirectory {
  64. #if TARGET_OS_TV
  65. return NSCachesDirectory;
  66. #else
  67. return NSApplicationSupportDirectory;
  68. #endif
  69. }
  70. + (NSString *)pathForSupportSubDirectory:(NSString *)subDirectoryName {
  71. NSArray *directoryPaths =
  72. NSSearchPathForDirectoriesInDomains([self supportedDirectory], NSUserDomainMask, YES);
  73. NSString *dirPath = directoryPaths.lastObject;
  74. NSArray *components = @[ dirPath, subDirectoryName ];
  75. return [NSString pathWithComponents:components];
  76. }
  77. + (BOOL)createSubDirectory:(NSString *)subDirectoryName {
  78. NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName];
  79. BOOL hasSubDirectory;
  80. if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
  81. isDirectory:&hasSubDirectory]) {
  82. NSError *error;
  83. [[NSFileManager defaultManager] createDirectoryAtPath:subDirectoryPath
  84. withIntermediateDirectories:YES
  85. attributes:nil
  86. error:&error];
  87. if (error) {
  88. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore000,
  89. @"Cannot create directory %@, error: %@", subDirectoryPath, error);
  90. return NO;
  91. }
  92. } else {
  93. if (!hasSubDirectory) {
  94. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore001,
  95. @"Found file instead of directory at %@", subDirectoryPath);
  96. return NO;
  97. }
  98. }
  99. return YES;
  100. }
  101. + (BOOL)removeSubDirectory:(NSString *)subDirectoryName error:(NSError **)error {
  102. if ([self hasSubDirectory:subDirectoryName]) {
  103. NSString *subDirectoryPath = [self pathForSupportSubDirectory:subDirectoryName];
  104. BOOL isDirectory;
  105. if ([[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
  106. isDirectory:&isDirectory]) {
  107. return [[NSFileManager defaultManager] removeItemAtPath:subDirectoryPath error:error];
  108. }
  109. }
  110. return YES;
  111. }
  112. /**
  113. * Reset the keychain preferences if the app had been deleted earlier and then reinstalled.
  114. * Keychain preferences are not cleared in the above scenario so explicitly clear them.
  115. *
  116. * In case of an iCloud backup and restore the Keychain preferences should already be empty
  117. * since the Keychain items are marked with `*BackupThisDeviceOnly`.
  118. */
  119. - (void)resetCredentialsIfNeeded {
  120. BOOL checkinPlistExists = [self.checkinStore hasCheckinPlist];
  121. // Checkin info existed in backup excluded plist. Should not be a fresh install.
  122. if (checkinPlistExists) {
  123. return;
  124. }
  125. // Resets checkin in keychain if a fresh install.
  126. // Keychain can still exist even if app is uninstalled.
  127. FIRInstanceIDCheckinPreferences *oldCheckinPreferences =
  128. [self.checkinStore cachedCheckinPreferences];
  129. if (oldCheckinPreferences) {
  130. [self.checkinStore removeCheckinPreferencesWithHandler:^(NSError *error) {
  131. if (!error) {
  132. FIRInstanceIDLoggerDebug(
  133. kFIRInstanceIDMessageCodeStore002,
  134. @"Removed cached checkin preferences from Keychain because this is a fresh install.");
  135. } else {
  136. FIRInstanceIDLoggerError(
  137. kFIRInstanceIDMessageCodeStore003,
  138. @"Couldn't remove cached checkin preferences for a fresh install. Error: %@", error);
  139. }
  140. if (oldCheckinPreferences.deviceID.length && oldCheckinPreferences.secretToken.length) {
  141. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore006,
  142. @"App reset detected. Will delete server registrations.");
  143. // We don't really need to delete old FCM tokens created via IID auth tokens since
  144. // those tokens are already hashed by APNS token as the has so creating a new
  145. // token should automatically delete the old-token.
  146. [self.delegate store:self didDeleteFCMScopedTokensForCheckin:oldCheckinPreferences];
  147. } else {
  148. FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore009,
  149. @"App reset detected but no valid checkin auth preferences found."
  150. @" Will not delete server registrations.");
  151. }
  152. }];
  153. }
  154. }
  155. #pragma mark - Get
  156. - (FIRInstanceIDTokenInfo *)tokenInfoWithAuthorizedEntity:(NSString *)authorizedEntity
  157. scope:(NSString *)scope {
  158. // TODO(chliangGoogle): If we don't have the token plist we should delete all the tokens from
  159. // the keychain. This is because not having the plist signifies a backup and restore operation.
  160. // In case the keychain has any tokens these would now be stale and therefore should be
  161. // deleted.
  162. if (![authorizedEntity length] || ![scope length]) {
  163. return nil;
  164. }
  165. FIRInstanceIDTokenInfo *info = [self.tokenStore tokenInfoWithAuthorizedEntity:authorizedEntity
  166. scope:scope];
  167. return info;
  168. }
  169. - (NSArray<FIRInstanceIDTokenInfo *> *)cachedTokenInfos {
  170. return [self.tokenStore cachedTokenInfos];
  171. }
  172. #pragma mark - Save
  173. - (void)saveTokenInfo:(FIRInstanceIDTokenInfo *)tokenInfo
  174. handler:(void (^)(NSError *error))handler {
  175. [self.tokenStore saveTokenInfo:tokenInfo handler:handler];
  176. }
  177. #pragma mark - Delete
  178. - (void)removeCachedTokenWithAuthorizedEntity:(NSString *)authorizedEntity scope:(NSString *)scope {
  179. if (![authorizedEntity length] || ![scope length]) {
  180. FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore012,
  181. @"Will not delete token with invalid entity: %@, scope: %@",
  182. authorizedEntity, scope);
  183. return;
  184. }
  185. [self.tokenStore removeTokenWithAuthorizedEntity:authorizedEntity scope:scope];
  186. }
  187. - (void)removeAllCachedTokensWithHandler:(void (^)(NSError *error))handler {
  188. [self.tokenStore removeAllTokensWithHandler:handler];
  189. }
  190. #pragma mark - FIRInstanceIDCheckinCache protocol
  191. - (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences
  192. handler:(void (^)(NSError *error))handler {
  193. [self.checkinStore saveCheckinPreferences:preferences handler:handler];
  194. }
  195. - (FIRInstanceIDCheckinPreferences *)cachedCheckinPreferences {
  196. return [self.checkinStore cachedCheckinPreferences];
  197. }
  198. - (void)removeCheckinPreferencesWithHandler:(void (^)(NSError *))handler {
  199. [self.checkinStore removeCheckinPreferencesWithHandler:^(NSError *error) {
  200. if (handler) {
  201. handler(error);
  202. }
  203. }];
  204. }
  205. @end