Skip to content

Commit bddf94e

Browse files
fix race condition that checkin can be deleted right after fetched (#2438) (#2631)
1 parent f28e768 commit bddf94e

File tree

8 files changed

+99
-35
lines changed

8 files changed

+99
-35
lines changed

Example/InstanceID/Tests/FIRInstanceIDCheckinStoreTest.m

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ - (NSString *)bundleIdentifierForKeychainAccount;
4545
static NSString *const kScope = @"test-scope";
4646
static NSString *const kSecret = @"test-secret";
4747
static NSString *const kToken = @"test-token";
48+
static NSString *const kFakeErrorDomain = @"fakeDomain";
49+
static const NSUInteger kFakeErrorCode = -1;
4850

4951
static int64_t const kLastCheckinTimestamp = 123456;
5052

@@ -111,7 +113,6 @@ - (void)testCheckinSaveFailsOnKeychainWriteFailure {
111113
FIRInstanceIDBackupExcludedPlist *checkinPlist =
112114
[[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:kFakeCheckinPlistName
113115
subDirectory:kSubDirectoryName];
114-
115116
FIRInstanceIDFakeKeychain *fakeKeychain = [[FIRInstanceIDFakeKeychain alloc] init];
116117
fakeKeychain.cannotWriteToKeychain = YES;
117118

@@ -134,6 +135,65 @@ - (void)testCheckinSaveFailsOnKeychainWriteFailure {
134135
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
135136
}
136137

138+
- (void)testCheckinSaveFailsOnPlistWriteFailure {
139+
XCTestExpectation *checkinSaveFailsExpectation =
140+
[self expectationWithDescription:@"Checkin save should fail after plist write failure"];
141+
FIRInstanceIDBackupExcludedPlist *checkinPlist =
142+
[[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:kFakeCheckinPlistName
143+
subDirectory:kSubDirectoryName];
144+
id plistMock = OCMPartialMock(checkinPlist);
145+
NSError *error = [NSError errorWithDomain:kFakeErrorDomain code:kFakeErrorCode userInfo:nil];
146+
OCMStub([plistMock writeDictionary:[OCMArg any] error:[OCMArg setTo:error]]).andReturn(NO);
147+
148+
FIRInstanceIDFakeKeychain *fakeKeychain = [[FIRInstanceIDFakeKeychain alloc] init];
149+
150+
FIRInstanceIDCheckinStore *checkinStore =
151+
[[FIRInstanceIDCheckinStore alloc] initWithCheckinPlist:plistMock keychain:fakeKeychain];
152+
153+
__block FIRInstanceIDCheckinPreferences *preferences =
154+
[[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:kAuthID secretToken:kSecret];
155+
[preferences updateWithCheckinPlistContents:[[self class] newCheckinPlistPreferences]];
156+
[checkinStore saveCheckinPreferences:preferences
157+
handler:^(NSError *error) {
158+
XCTAssertNotNil(error);
159+
XCTAssertEqual(error.code, kFakeErrorCode);
160+
161+
preferences = [checkinStore cachedCheckinPreferences];
162+
XCTAssertNil(preferences.deviceID);
163+
XCTAssertNil(preferences.secretToken);
164+
XCTAssertFalse([preferences hasValidCheckinInfo]);
165+
[checkinSaveFailsExpectation fulfill];
166+
}];
167+
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
168+
}
169+
170+
- (void)testCheckinSaveSuccess {
171+
XCTestExpectation *checkinSaveSuccessExpectation =
172+
[self expectationWithDescription:@"Checkin save should succeed"];
173+
FIRInstanceIDBackupExcludedPlist *checkinPlist =
174+
[[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:kFakeCheckinPlistName
175+
subDirectory:kSubDirectoryName];
176+
id plistMock = OCMPartialMock(checkinPlist);
177+
178+
FIRInstanceIDFakeKeychain *fakeKeychain = [[FIRInstanceIDFakeKeychain alloc] init];
179+
FIRInstanceIDCheckinStore *checkinStore =
180+
[[FIRInstanceIDCheckinStore alloc] initWithCheckinPlist:plistMock keychain:fakeKeychain];
181+
182+
__block FIRInstanceIDCheckinPreferences *preferences =
183+
[[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:kAuthID secretToken:kSecret];
184+
[preferences updateWithCheckinPlistContents:[[self class] newCheckinPlistPreferences]];
185+
[checkinStore saveCheckinPreferences:preferences
186+
handler:^(NSError *error) {
187+
XCTAssertNil(error);
188+
189+
preferences = [checkinStore cachedCheckinPreferences];
190+
XCTAssertEqualObjects(preferences.deviceID, kAuthID);
191+
XCTAssertEqualObjects(preferences.secretToken, kSecret);
192+
[checkinSaveSuccessExpectation fulfill];
193+
}];
194+
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
195+
}
196+
137197
// Write fake checkin data to legacy location, then test if migration worked.
138198
- (void)testCheckinMigrationMovesToNewLocationInKeychain {
139199
XCTestExpectation *checkinMigrationExpectation =

Firebase/InstanceID/FIRIMessageCode.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ typedef NS_ENUM(NSInteger, FIRInstanceIDMessageCode) {
7777
kFIRInstanceIDMessageCodeKeyPair002 = 9002,
7878
kFIRInstanceIDMessageCodeKeyPairMigrationError = 9004,
7979
kFIRInstanceIDMessageCodeKeyPairMigrationSuccess = 9005,
80+
kFIRInstanceIDMessageCodeKeyPairNoLegacyKeyPair = 9006,
81+
8082
// FIRInstanceIDKeyPairStore.m
8183
kFIRInstanceIDMessageCodeKeyPairStore000 = 10000,
8284
kFIRInstanceIDMessageCodeKeyPairStore001 = 10001,

Firebase/InstanceID/FIRInstanceIDCheckinService.m

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,9 @@ - (void)checkinWithExistingCheckin:(FIRInstanceIDCheckinPreferences *)existingCh
133133
// Somehow the server clock gets out of sync with the device clock.
134134
// Reset the last checkin timestamp in case this happens.
135135
if (lastCheckinTimestampMillis > currentTimestampMillis) {
136-
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeService002,
137-
@"Invalid last checkin timestamp in future.");
136+
FIRInstanceIDLoggerDebug(
137+
kFIRInstanceIDMessageCodeService002, @"Invalid last checkin timestamp %@ in future.",
138+
[NSDate dateWithTimeIntervalSince1970:lastCheckinTimestampMillis / 1000.0]);
138139
lastCheckinTimestampMillis = currentTimestampMillis;
139140
}
140141

Firebase/InstanceID/FIRInstanceIDCheckinStore.m

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,21 @@ - (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences
108108
return;
109109
}
110110

111+
// Save all other checkin preferences in a plist
112+
NSError *error;
113+
if (![self.plist writeDictionary:checkinPlistContents error:&error]) {
114+
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore003,
115+
@"Failed to save checkin plist contents."
116+
@"Will delete auth credentials");
117+
[self.keychain removeItemsMatchingService:kFIRInstanceIDCheckinKeychainService
118+
account:self.bundleIdentifierForKeychainAccount
119+
handler:nil];
120+
if (handler) {
121+
handler(error);
122+
}
123+
return;
124+
}
111125
// Save the deviceID and secret in the Keychain
112-
__block BOOL shouldContinue = YES;
113126
if (!preferences.hasPreCachedAuthCredentials) {
114127
NSData *data = [checkinKeychainContent dataUsingEncoding:NSUTF8StringEncoding];
115128
[self.keychain setData:data
@@ -121,30 +134,15 @@ - (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences
121134
if (handler) {
122135
handler(error);
123136
}
124-
shouldContinue = NO;
125137
return;
126138
}
139+
if (handler) {
140+
handler(nil);
141+
}
127142
}];
143+
} else {
144+
handler(nil);
128145
}
129-
if (!shouldContinue) {
130-
return;
131-
}
132-
133-
// Save all other checkin preferences in a plist
134-
NSError *error;
135-
if (![self.plist writeDictionary:checkinPlistContents error:&error]) {
136-
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore003,
137-
@"Failed to save checkin plist contents."
138-
@"Will delete auth credentials");
139-
[self.keychain removeItemsMatchingService:kFIRInstanceIDCheckinKeychainService
140-
account:self.bundleIdentifierForKeychainAccount
141-
handler:nil];
142-
if (handler) {
143-
handler(error);
144-
}
145-
return;
146-
}
147-
handler(nil);
148146
}
149147

150148
- (void)removeCheckinPreferencesWithHandler:(void (^)(NSError *error))handler {

Firebase/InstanceID/FIRInstanceIDKeyPairStore.m

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ - (BOOL)hasCachedKeyPairs {
159159
if ([self cachedKeyPairWithSubtype:kFIRInstanceIDKeyPairSubType error:&error] == nil) {
160160
if (error) {
161161
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeKeyPairStore000,
162-
@"Failed to get cached keyPair %@", error);
162+
@"Failed to get the cached keyPair %@", error);
163163
}
164164
error = nil;
165165
[self removeKeyPairCreationTimePlistWithError:&error];
@@ -316,7 +316,7 @@ + (FIRInstanceIDKeyPair *)keyPairForPrivateKeyTag:(NSString *)privateKeyTag
316316
*error = [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeMissingKeyPair];
317317
}
318318
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeKeyPair000,
319-
@"No keypair info is retrieved with tag %@", privateKeyTag);
319+
@"No keypair info is found with tag %@", privateKeyTag);
320320
return nil;
321321
}
322322

@@ -344,6 +344,8 @@ - (void)migrateKeyPairCacheIfNeededWithHandler:(void (^)(NSError *error))handler
344344
publicKeyTag:legacyPublicKeyTag
345345
error:&error];
346346
if (![keyPair isValid]) {
347+
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeKeyPairNoLegacyKeyPair,
348+
@"There's no legacy keypair so no need to do migration.");
347349
if (handler) {
348350
handler(nil);
349351
}

Firebase/InstanceID/FIRInstanceIDKeychain.m

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,9 @@ - (CFTypeRef)itemWithQuery:(NSDictionary *)keychainQuery {
6060
if (keyRef) {
6161
CFRelease(keyRef);
6262
}
63-
FIRInstanceIDLoggerDebug(
64-
kFIRInstanceIDKeychainReadItemError,
65-
@"No info is retrieved from Keychain OSStatus: %d with the keychain query %@",
66-
(int)status, keychainQuery);
63+
FIRInstanceIDLoggerDebug(kFIRInstanceIDKeychainReadItemError,
64+
@"Info is not found in Keychain. OSStatus: %d. Keychain query: %@",
65+
(int)status, keychainQuery);
6766
}
6867
});
6968
return keyRef;

Firebase/InstanceID/FIRInstanceIDStore.m

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,13 @@ - (void)resetCredentialsIfNeeded {
153153
if (oldCheckinPreferences) {
154154
[self.checkinStore removeCheckinPreferencesWithHandler:^(NSError *error) {
155155
if (!error) {
156-
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore002,
157-
@"Removed cached checkin preferences from Keychain.");
156+
FIRInstanceIDLoggerDebug(
157+
kFIRInstanceIDMessageCodeStore002,
158+
@"Removed cached checkin preferences from Keychain because this is a fresh install.");
158159
} else {
159-
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeStore003,
160-
@"Couldn't remove cached checkin preferences. Error: %@", error);
160+
FIRInstanceIDLoggerError(
161+
kFIRInstanceIDMessageCodeStore003,
162+
@"Couldn't remove cached checkin preferences for a fresh install. Error: %@", error);
161163
}
162164
if (oldCheckinPreferences.deviceID.length && oldCheckinPreferences.secretToken.length) {
163165
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeStore006,

ZipBuilder/Sources/ZipBuilder/FrameworkBuilder.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ struct FrameworkBuilder {
101101
// }
102102
//
103103
// // TODO: Figure out if we need the MD5 at all.
104-
let md5 = podName
104+
let md5 = podName
105105
// let md5 = Shell.calculateMD5(for: podInfo.installedLocation)
106106

107107
// Get (or create) the cache directory for storing built frameworks.

0 commit comments

Comments
 (0)