Files
MapLibreParticleLab/platform/ios/src/MGLAPIClient.m
Jesse Bounds c9520713ca [ios] Update telem domain and certs (#5181)
Change domain from api -> events
Replace old certs with new ones with the same name. The new certs
have an expiry that is further out.
2016-05-27 18:43:39 -07:00

212 lines
10 KiB
Objective-C

#import "MGLAPIClient.h"
#import "NSBundle+MGLAdditions.h"
#import "MGLAccountManager.h"
static NSString * const MGLAPIClientUserAgentBase = @"MapboxEventsiOS";
static NSString * const MGLAPIClientBaseURL = @"https://events.mapbox.com";
static NSString * const MGLAPIClientEventsPath = @"events/v2";
static NSString * const MGLAPIClientHeaderFieldUserAgentKey = @"User-Agent";
static NSString * const MGLAPIClientHeaderFieldContentTypeKey = @"Content-Type";
static NSString * const MGLAPIClientHeaderFieldContentTypeValue = @"application/json";
static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
@interface MGLAPIClient ()
@property (nonatomic, copy) NSURLSession *session;
@property (nonatomic, copy) NSURL *baseURL;
@property (nonatomic, copy) NSData *digicertCert;
@property (nonatomic, copy) NSData *geoTrustCert;
@property (nonatomic, copy) NSData *testServerCert;
@property (nonatomic, copy) NSString *userAgent;
@property (nonatomic) NSMutableArray *dataTasks;
@property (nonatomic) BOOL usesTestServer;
@end
@implementation MGLAPIClient
- (instancetype)init {
self = [super init];
if (self) {
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
delegate:self delegateQueue:nil];
_dataTasks = [NSMutableArray array];
[self loadCertificates];
[self setupBaseURL];
[self setupUserAgent];
}
return self;
}
#pragma mark Public API
- (void)postEvents:(nonnull NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler {
__block NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:[self requestForEvents:events]
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSError *statusError = nil;
if (httpResponse.statusCode >= 400) {
NSString *description = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"API_CLIENT_400_DESC", nil, nil, @"The session data task failed. Original request was: %@", nil), dataTask.originalRequest];
NSString *reason = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"API_CLIENT_400_REASON", nil, nil, @"The status code was %ld", nil), (long)httpResponse.statusCode];
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description,
NSLocalizedFailureReasonErrorKey: reason};
statusError = [NSError errorWithDomain:MGLErrorDomain code:1 userInfo:userInfo];
}
if (completionHandler) {
error = error ?: statusError;
completionHandler(error);
}
[self.dataTasks removeObject:dataTask];
dataTask = nil;
}];
[dataTask resume];
[self.dataTasks addObject:dataTask];
}
- (void)postEvent:(nonnull MGLMapboxEventAttributes *)event completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler {
[self postEvents:@[event] completionHandler:completionHandler];
}
- (void)cancelAll {
[self.dataTasks makeObjectsPerformSelector:@selector(cancel)];
[self.dataTasks removeAllObjects];
}
#pragma mark Utilities
- (NSURLRequest *)requestForEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events {
NSString *path = [NSString stringWithFormat:@"%@?access_token=%@", MGLAPIClientEventsPath, [MGLAccountManager accessToken]];
NSURL *url = [NSURL URLWithString:path relativeToURL:self.baseURL];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setValue:self.userAgent forHTTPHeaderField:MGLAPIClientHeaderFieldUserAgentKey];
[request setValue:MGLAPIClientHeaderFieldContentTypeValue forHTTPHeaderField:MGLAPIClientHeaderFieldContentTypeKey];
[request setHTTPMethod:MGLAPIClientHTTPMethodPost];
NSData *jsonData = [self serializedDataForEvents:events];
[request setHTTPBody:jsonData];
return [request copy];
}
- (void)setupBaseURL {
NSString *testServerURLString = [[NSUserDefaults standardUserDefaults] stringForKey:@"MGLTelemetryTestServerURL"];
NSURL *testServerURL = [NSURL URLWithString:testServerURLString];
if (testServerURL && [testServerURL.scheme isEqualToString:@"https"]) {
self.baseURL = testServerURL;
self.usesTestServer = YES;
} else {
self.baseURL = [NSURL URLWithString:MGLAPIClientBaseURL];
}
}
- (void)loadCertificates {
NSData *certificate;
[self loadCertificate:&certificate withResource:@"api_mapbox_com-geotrust"];
self.geoTrustCert = certificate;
[self loadCertificate:&certificate withResource:@"api_mapbox_com-digicert"];
self.digicertCert = certificate;
[self loadCertificate:&certificate withResource:@"star_tilestream_net"];
self.testServerCert = certificate;
}
- (void)loadCertificate:(NSData **)certificate withResource:(NSString *)resource {
NSBundle *frameworkBundle = [NSBundle mgl_frameworkBundle];
NSString *cerPath = [frameworkBundle pathForResource:resource ofType:@"der" inDirectory:frameworkBundle.mgl_resourcesDirectory];
if (cerPath != nil) {
*certificate = [NSData dataWithContentsOfFile:cerPath];
}
}
- (void)setupUserAgent {
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"];
NSString *appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
NSString *appBuildNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
NSString *semanticVersion = [NSBundle mgl_frameworkInfoDictionary][@"MGLSemanticVersionString"];
NSString *shortVersion = [NSBundle mgl_frameworkInfoDictionary][@"CFBundleShortVersionString"];
NSString *sdkVersion = semanticVersion ?: shortVersion;
_userAgent = [NSString stringWithFormat:@"%@/%@/%@ %@/%@", appName, appVersion, appBuildNumber, MGLAPIClientUserAgentBase, sdkVersion];
}
#pragma mark - JSON Serialization
- (NSData *)serializedDataForEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events {
return [NSJSONSerialization dataWithJSONObject:events options:0 error:nil];
}
#pragma mark NSURLSessionDelegate
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^) (NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
SecTrustResultType trustResult;
// Validate the certificate chain with the device's trust store anyway
// This *might* give use revocation checking
SecTrustEvaluate(serverTrust, &trustResult);
if (trustResult == kSecTrustResultUnspecified)
{
// Look for a pinned certificate in the server's certificate chain
long numKeys = SecTrustGetCertificateCount(serverTrust);
BOOL found = NO;
// Try GeoTrust Cert First
for (int lc = 0; lc < numKeys; lc++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc);
NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
// Compare Remote Key With Local Version
if ([remoteCertificateData isEqualToData:_geoTrustCert]) {
// Found the certificate; continue connecting
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
found = YES;
break;
}
}
if (!found) {
// Fallback to Digicert Cert
for (int lc = 0; lc < numKeys; lc++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc);
NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
// Compare Remote Key With Local Version
if ([remoteCertificateData isEqualToData:_digicertCert]) {
// Found the certificate; continue connecting
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
found = YES;
break;
}
}
if (!found && _usesTestServer) {
// See if this is test server
for (int lc = 0; lc < numKeys; lc++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc);
NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
// Compare Remote Key With Local Version
if ([remoteCertificateData isEqualToData:_testServerCert]) {
// Found the certificate; continue connecting
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
found = YES;
break;
}
}
}
if (!found) {
// The certificate wasn't found in GeoTrust nor Digicert. Cancel the connection.
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}
}
}
else
{
// Certificate chain validation failed; cancel the connection
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}
}
}
@end