Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start validating the sessionUserInfo type. #427

Merged
merged 1 commit into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 74 additions & 2 deletions Sources/Core/GTMSessionFetcher.m
Original file line number Diff line number Diff line change
Expand Up @@ -1587,7 +1587,7 @@ - (void)setSessionIdentifierInternal:(nullable NSString *)sessionIdentifier {
} // @synchronized(self)
}

- (nullable NSDictionary *)sessionUserInfo {
- (nullable NSDictionary<NSString *, NSString *> *)sessionUserInfo {
@synchronized(self) {
GTMSessionMonitorSynchronized(self);

Expand All @@ -1601,18 +1601,49 @@ - (nullable NSDictionary *)sessionUserInfo {
[metadata removeObjectsForKeys:[keysToRemove allObjects]];
if (metadata.count > 0) {
_sessionUserInfo = metadata;

#if DEBUG
[metadata enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj,
BOOL *_Nonnull stop) {
GTMSESSION_ASSERT_DEBUG([key isKindOfClass:[NSString class]],
@"sessionUserInfo keys must be NSStrings: %@", key);
if (![obj isKindOfClass:[NSString class]]) {
GTMSESSION_LOG_DEBUG(@"WARNING: sessionUserInfo included a non String value, this will "
@"be an error in the future: %@: %@",
key, obj);
}
}];
#endif // DEBUG
}
}
return _sessionUserInfo;
} // @synchronized(self)
}

- (void)setSessionUserInfo:(nullable NSDictionary *)dictionary {
- (void)setSessionUserInfo:(nullable NSDictionary<NSString *, NSString *> *)dictionary {
@synchronized(self) {
GTMSessionMonitorSynchronized(self);

GTMSESSION_ASSERT_DEBUG(_sessionIdentifier == nil, @"Too late to assign userInfo");
_sessionUserInfo = dictionary;

#if DEBUG
[dictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj,
BOOL *_Nonnull stop) {
GTMSESSION_ASSERT_DEBUG([key isKindOfClass:[NSString class]],
@"sessionUserInfo keys must be NSStrings: %@", key);
if ([key hasPrefix:@"_"]) {
GTMSESSION_LOG_DEBUG(@"WARNING: sessionUserInfo keys starting with an underscore are "
@"reserved for the library, this will become an error in the future: "
@"%@: %@", key, obj);
}
if (![obj isKindOfClass:[NSString class]]) {
GTMSESSION_LOG_DEBUG(@"WARNING: sessionUserInfo included a non String value, this will be "
@"an error in the future: %@: %@",
key, obj);
}
}];
#endif // DEBUG
} // @synchronized(self)
}

Expand Down Expand Up @@ -1702,15 +1733,56 @@ - (NSString *)createSessionIdentifierWithMetadata:(nullable NSDictionary *)metad
_sessionIdentifierUUID = [[NSUUID UUID] UUIDString];
_sessionIdentifier =
[NSString stringWithFormat:@"%@_%@", kGTMSessionIdentifierPrefix, _sessionIdentifierUUID];

#if DEBUG
// _sessionUserInfo was declared as `strong` (not `copy`, so it could have been modifed after
// having been set.
[_sessionUserInfo enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj,
BOOL *_Nonnull stop) {
GTMSESSION_ASSERT_DEBUG([key isKindOfClass:[NSString class]],
@"sessionUserInfo keys must be NSStrings: %@", key);
if ([key hasPrefix:@"_"]) {
GTMSESSION_LOG_DEBUG(@"WARNING: sessionUserInfo keys starting with an underscore are "
@"reserved for the library, this will become an error in the future: "
@"%@: %@", key, obj);
}
if (![obj isKindOfClass:[NSString class]]) {
GTMSESSION_LOG_DEBUG(@"WARNING: sessionUserInfo included a non String value, this will "
@"be an error in the future: %@: %@",
key, obj);
}
}];
#endif // DEBUG

// Start with user-supplied keys so they cannot accidentally override the fetcher's keys.
NSMutableDictionary *metadataDict =
[NSMutableDictionary dictionaryWithDictionary:(NSDictionary *_Nonnull)_sessionUserInfo];

if (metadataToInclude) {
#if DEBUG
[metadataToInclude enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj,
BOOL * _Nonnull stop) {
GTMSESSION_ASSERT_DEBUG([key isKindOfClass:[NSString class]],
@"metadataToInclude keys must be NSStrings: %@", key);
GTMSESSION_ASSERT_DEBUG([key hasPrefix:@"_"],
@"metadataToInclude should only have prefixed keys: %@ - %@",
key, obj);
}];
#endif
[metadataDict addEntriesFromDictionary:(NSDictionary *)metadataToInclude];
}
NSDictionary *defaultMetadataDict = [self sessionIdentifierDefaultMetadata];
if (defaultMetadataDict) {
#if DEBUG
[defaultMetadataDict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj,
BOOL * _Nonnull stop) {
GTMSESSION_ASSERT_DEBUG([key isKindOfClass:[NSString class]],
@"defaultMetadataDict keys must be NSStrings: %@", key);
GTMSESSION_ASSERT_DEBUG([key hasPrefix:@"_"],
@"defaultMetadataDict should only have prefixed keys: %@ - %@",
key, obj);
}];
#endif
[metadataDict addEntriesFromDictionary:defaultMetadataDict];
}
if (metadataDict.count > 0) {
Expand Down
2 changes: 2 additions & 0 deletions Sources/Core/Public/GTMSessionFetcher/GTMSessionFetcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,8 @@ __deprecated_msg("implement GTMSessionFetcherAuthorizer instead")
// The fetcher encodes information used to resume a session in the session identifier.
// This method, intended for internal use returns the encoded information. The sessionUserInfo
// dictionary is stored as identifier metadata.
// NOTE: This type is a lie and could be an issue for Swift. The values for private keys (prefixed
// with an underscore) aren't always strings; but changing the type is a breaking change.
- (nullable NSDictionary<NSString *, NSString *> *)sessionIdentifierMetadata;

#if TARGET_OS_IPHONE && !TARGET_OS_WATCH
Expand Down