Skip to content

Commit 26d984d

Browse files
M81 FIS cherry pick of #6570 (#6616)
* FIS: Additional FIRInstallationsItem validation (#6570) * FIS API tests for no FID in response * FIRInstallationsIDControllerTests: test names * FIRInstallationsIDControllerTests: corrupted storage tests * FIRInstallationsItem validation * Fix FIRInstallationsItem.IIDDefaultToken copy * Improve error description. * FIRInstallationsItem validation error * FIRInstallationsItem validation tests * ./scripts/style.sh * FIRInstallationsIDController: validate stored installation * FIRInstallationsAPIService installation validation * Changelog * Update versions * patch version * release manifest
1 parent 04d7e84 commit 26d984d

17 files changed

+359
-228
lines changed

FirebaseInstallations.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'FirebaseInstallations'
3-
s.version = '1.7.0'
3+
s.version = '1.7.1'
44
s.summary = 'Firebase Installations'
55

66
s.description = <<-DESC

FirebaseInstallations/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# v1.7.1 -- M81
2+
- [changed] Additional `FIRInstallationsItem` validation to catch potential storage issues. (#6570)
3+
14
# v1.7.0 -- M78
25
- [changed] Use ephemeral `NSURLSession` to prevent caching of request/response. (#6226)
36
- [changed] Backoff added for some error to prevent unnecessary API requests. (#6232)

FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ void FIRInstallationsItemSetErrorToPointer(NSError *error, NSError **pointer);
5656

5757
+ (FBLPromise *)rejectedPromiseWithError:(NSError *)error;
5858

59+
+ (NSError *)installationsErrorWithCode:(FIRInstallationsErrorCode)code
60+
failureReason:(nullable NSString *)failureReason
61+
underlyingError:(nullable NSError *)underlyingError;
62+
5963
@end
6064

6165
NS_ASSUME_NONNULL_END

FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.m

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,10 @@ + (NSError *)installationsErrorWithCode:(FIRInstallationsErrorCode)code
128128
underlyingError:(nullable NSError *)underlyingError {
129129
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
130130
userInfo[NSUnderlyingErrorKey] = underlyingError;
131-
userInfo[NSLocalizedFailureReasonErrorKey] = failureReason;
131+
userInfo[NSLocalizedFailureReasonErrorKey] =
132+
failureReason
133+
?: [NSString
134+
stringWithFormat:@"Underlying error: %@", underlyingError.localizedDescription];
132135

133136
return [NSError errorWithDomain:kFirebaseInstallationsErrorDomain code:code userInfo:userInfo];
134137
}

FirebaseInstallations/Source/Library/FIRInstallationsItem.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ NS_ASSUME_NONNULL_BEGIN
6767
*/
6868
- (NSString *)identifier;
6969

70+
/** Validates if all the required item fields are populated and values don't explicitly conflict
71+
* with each other.
72+
* @param outError A reference to be populated with an error containing validation failure details.
73+
* @return `YES` if the item it valid, `NO` otherwise.
74+
*/
75+
- (BOOL)isValid:(NSError *_Nullable *)outError;
76+
7077
/**
7178
* The installation identifier.
7279
* @param appID A `FirebaseApp` identifier.

FirebaseInstallations/Source/Library/FIRInstallationsItem.m

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredAuthToken.h"
2020
#import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredItem.h"
2121

22+
#import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h"
23+
2224
@implementation FIRInstallationsItem
2325

2426
- (instancetype)initWithAppID:(NSString *)appID firebaseAppName:(NSString *)firebaseAppName {
@@ -37,6 +39,7 @@ - (nonnull id)copyWithZone:(nullable NSZone *)zone {
3739
clone.refreshToken = [self.refreshToken copy];
3840
clone.authToken = [self.authToken copy];
3941
clone.registrationStatus = self.registrationStatus;
42+
clone.IIDDefaultToken = [self.IIDDefaultToken copy];
4043

4144
return clone;
4245
}
@@ -63,6 +66,60 @@ - (nonnull NSString *)identifier {
6366
return [[self class] identifierWithAppID:self.appID appName:self.firebaseAppName];
6467
}
6568

69+
- (BOOL)isValid:(NSError *_Nullable *)outError {
70+
NSMutableArray<NSString *> *validationIssues = [NSMutableArray array];
71+
72+
if (self.appID.length == 0) {
73+
[validationIssues addObject:@"`appID` must not be empty"];
74+
}
75+
76+
if (self.firebaseAppName.length == 0) {
77+
[validationIssues addObject:@"`firebaseAppName` must not be empty"];
78+
}
79+
80+
if (self.firebaseInstallationID.length == 0) {
81+
[validationIssues addObject:@"`firebaseInstallationID` must not be empty"];
82+
}
83+
84+
switch (self.registrationStatus) {
85+
case FIRInstallationStatusUnknown:
86+
[validationIssues addObject:@"invalid `registrationStatus`"];
87+
break;
88+
89+
case FIRInstallationStatusRegistered:
90+
if (self.refreshToken == 0) {
91+
[validationIssues addObject:@"registered installation must have non-empty `refreshToken`"];
92+
}
93+
94+
if (self.authToken.token == 0) {
95+
[validationIssues
96+
addObject:@"registered installation must have non-empty `authToken.token`"];
97+
}
98+
99+
if (self.authToken.expirationDate == nil) {
100+
[validationIssues
101+
addObject:@"registered installation must have non-empty `authToken.expirationDate`"];
102+
}
103+
104+
case FIRInstallationStatusUnregistered:
105+
break;
106+
}
107+
108+
BOOL isValid = validationIssues.count == 0;
109+
110+
if (!isValid && outError) {
111+
NSString *failureReason =
112+
[NSString stringWithFormat:@"FIRInstallationsItem validation errors: %@",
113+
[validationIssues componentsJoinedByString:@", "]];
114+
*outError =
115+
[FIRInstallationsErrorUtil installationsErrorWithCode:FIRInstallationsErrorCodeUnknown
116+
failureReason:failureReason
117+
underlyingError:nil];
118+
}
119+
120+
return isValid;
121+
}
122+
66123
+ (NSString *)identifierWithAppID:(NSString *)appID appName:(NSString *)appName {
67124
return [appID stringByAppendingString:appName];
68125
}

FirebaseInstallations/Source/Library/FIRInstallationsLogger.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ extern NSString *const kFIRInstallationsMessageCodeNewGetInstallationOperationCr
3636
extern NSString *const kFIRInstallationsMessageCodeNewGetAuthTokenOperationCreated;
3737
extern NSString *const kFIRInstallationsMessageCodeNewDeleteInstallationOperationCreated;
3838
extern NSString *const kFIRInstallationsMessageCodeInvalidFirebaseConfiguration;
39+
extern NSString *const kFIRInstallationsMessageCodeCorruptedStoredInstallation;
3940

4041
// FIRInstallationsStoredItem.m
4142
extern NSString *const kFIRInstallationsMessageCodeInstallationCoderVersionMismatch;

FirebaseInstallations/Source/Library/FIRInstallationsLogger.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
NSString *const kFIRInstallationsMessageCodeNewGetAuthTokenOperationCreated = @"I-FIS002001";
3535
NSString *const kFIRInstallationsMessageCodeNewDeleteInstallationOperationCreated = @"I-FIS002002";
3636
NSString *const kFIRInstallationsMessageCodeInvalidFirebaseConfiguration = @"I-FIS002003";
37+
NSString *const kFIRInstallationsMessageCodeCorruptedStoredInstallation = @"I-FIS002004";
3738

3839
// FIRInstallationsStoredItem.m
3940
NSString *const kFIRInstallationsMessageCodeInstallationCoderVersionMismatch = @"I-FIS003000";

FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsAPIService.m

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,10 @@ - (instancetype)initWithURLSession:(NSURLSession *)URLSession
9292
#pragma mark - Public
9393

9494
- (FBLPromise<FIRInstallationsItem *> *)registerInstallation:(FIRInstallationsItem *)installation {
95-
return [self registerRequestWithInstallation:installation]
95+
return [self validateInstallation:installation]
96+
.then(^id _Nullable(FIRInstallationsItem *_Nullable validInstallation) {
97+
return [self registerRequestWithInstallation:validInstallation];
98+
})
9699
.then(^id _Nullable(NSURLRequest *_Nullable request) {
97100
return [self sendURLRequest:request];
98101
})
@@ -138,7 +141,9 @@ - (instancetype)initWithURLSession:(NSURLSession *)URLSession
138141
NSURL *URL = [NSURL URLWithString:URLString];
139142

140143
NSDictionary *bodyDict = @{
141-
@"fid" : installation.firebaseInstallationID,
144+
// `firebaseInstallationID` is validated before but let's make sure it is not `nil` one more
145+
// time to prevent a crash.
146+
@"fid" : installation.firebaseInstallationID ?: @"",
142147
@"authVersion" : @"FIS_v2",
143148
@"appId" : installation.appID,
144149
@"sdkVersion" : [self SDKVersion]
@@ -346,6 +351,20 @@ - (NSString *)SDKVersion {
346351
return [NSString stringWithFormat:@"i:%s", FIRInstallationsVersionStr];
347352
}
348353

354+
#pragma mark - Validation
355+
356+
- (FBLPromise<FIRInstallationsItem *> *)validateInstallation:(FIRInstallationsItem *)installation {
357+
FBLPromise<FIRInstallationsItem *> *result = [FBLPromise pendingPromise];
358+
359+
NSError *validationError;
360+
if ([installation isValid:&validationError]) {
361+
[result fulfill:installation];
362+
} else {
363+
[result reject:validationError];
364+
}
365+
return result;
366+
}
367+
349368
#pragma mark - JSON
350369

351370
- (void)setJSONHTTPBody:(NSDictionary<NSString *, id> *)body

FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsIDController.m

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -180,18 +180,11 @@ - (instancetype)initWithGoogleAppID:(NSString *)appID
180180
- (FBLPromise<FIRInstallationsItem *> *)getStoredInstallation {
181181
return [self.installationsStore installationForAppID:self.appID appName:self.appName].validate(
182182
^BOOL(FIRInstallationsItem *installation) {
183-
BOOL isValid = NO;
184-
switch (installation.registrationStatus) {
185-
case FIRInstallationStatusUnregistered:
186-
case FIRInstallationStatusRegistered:
187-
isValid = YES;
188-
break;
189-
190-
case FIRInstallationStatusUnknown:
191-
isValid = NO;
192-
break;
193-
}
194-
183+
NSError *validationError;
184+
BOOL isValid = [installation isValid:&validationError];
185+
FIRLogWarning(
186+
kFIRLoggerInstallations, kFIRInstallationsMessageCodeCorruptedStoredInstallation,
187+
@"Stored installation validation error: %@", validationError.localizedDescription);
195188
return isValid;
196189
});
197190
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "projects/project-id/installations/eapzYQai_g8flVQyfKoGs7",
3+
"refreshToken": "aaaaaaabbbbbbbbcccccccccdddddddd00000000",
4+
"authToken": {
5+
"token": "aaaaaaaaaaaaaa.bbbbbbbbbbbbbbbbb.cccccccccccccccccccccccc",
6+
"expiresIn": "604800s"
7+
}
8+
}

0 commit comments

Comments
 (0)