From 8bd1cd9bfdc828ec4fbca0c94a1f2dd1b0d29d5e Mon Sep 17 00:00:00 2001 From: Syed Yusuf Date: Fri, 14 Feb 2020 15:15:47 +0530 Subject: [PATCH] Replacing UIWebView with WKWebView --- MASFoundation/Classes/MAS.m | 2 +- MASFoundation/Classes/models/MASApplication.h | 6 +- MASFoundation/Classes/models/MASApplication.m | 235 +++++++++++++++--- 3 files changed, 204 insertions(+), 39 deletions(-) diff --git a/MASFoundation/Classes/MAS.m b/MASFoundation/Classes/MAS.m index 0affb4a5..98d97c26 100644 --- a/MASFoundation/Classes/MAS.m +++ b/MASFoundation/Classes/MAS.m @@ -194,7 +194,7 @@ + (void)setGatewayNetworkActivityLogging:(BOOL)enabled + (void)start:(MASCompletionErrorBlock)completion { //DLog(@"called"); - [NSURLProtocol registerClass:[L7SBrowserURLProtocol class]]; + //[NSURLProtocol registerClass:[L7SBrowserURLProtocol class]]; // // Post the notification diff --git a/MASFoundation/Classes/models/MASApplication.h b/MASFoundation/Classes/models/MASApplication.h index abdee97a..58f76fb3 100644 --- a/MASFoundation/Classes/models/MASApplication.h +++ b/MASFoundation/Classes/models/MASApplication.h @@ -11,7 +11,7 @@ #import "MASObject.h" @class UIImageView; -@class UIWebView; +@class WKWebView; @protocol MASEnterpriseAppProtocol; @@ -198,7 +198,7 @@ typedef NS_ENUM(NSInteger, MASAuthenticationStatus) { /** - * Loads a web application representing this application into a UIWebView. + * Loads a web application representing this application into a WKWebView. * * @param webView a webView loading the web app. * @@ -206,7 +206,7 @@ typedef NS_ENUM(NSInteger, MASAuthenticationStatus) { * receive a YES or NO BOOL indicating the completion state and/or an NSError object if there * is a failure. */ -- (void)loadWebApp:(UIWebView *_Nonnull)webView completion:(MASCompletionErrorBlock _Nullable)completion; +- (void)loadWebApp:(WKWebView *_Nonnull)webView completion:(MASCompletionErrorBlock _Nullable)completion; @end diff --git a/MASFoundation/Classes/models/MASApplication.m b/MASFoundation/Classes/models/MASApplication.m index 43923b77..88571291 100644 --- a/MASFoundation/Classes/models/MASApplication.m +++ b/MASFoundation/Classes/models/MASApplication.m @@ -14,6 +14,10 @@ #import "MASConstantsPrivate.h" #import "MASModelService.h" #import "MASAccessService.h" +#import +#import "MASSecurityService.h" + + # pragma mark - Property Constants @@ -38,12 +42,13 @@ @interface MASApplication () - + { id _originalDelegate; + NSMutableURLRequest *mutableRequest; } -@property (nonatomic, strong, readonly) UIWebView *webView; +@property (nonatomic, strong, readonly) WKWebView *webView; @property (nonatomic, copy) MASCompletionErrorBlock errorBlock; @end @@ -247,7 +252,7 @@ - (void)enterpriseIconWithImageView:(UIImageView *)imageView completion:(MASComp } -- (void)loadWebApp:(UIWebView *)webView completion:(MASCompletionErrorBlock)completion +- (void)loadWebApp:(WKWebView *)webView completion:(MASCompletionErrorBlock)completion { // // Validate URL @@ -265,28 +270,33 @@ - (void)loadWebApp:(UIWebView *)webView completion:(MASCompletionErrorBlock)comp // // Create the URL request // - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.authUrl]]; + NSMutableURLRequest *request = + [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.authUrl]]; // - // If the UIWebView already has a delegate we must store it + // If user session is valid add the authorization header // - if(webView.delegate != nil) + if ([MASApplication currentApplication].isAuthenticated && + [self.class isProtectedResource:request.URL]) { - _originalDelegate = webView.delegate; + [self setAuthorization:request]; } - webView.delegate = self; + + // + // If the WKWebView already has a delegate we must store it + // + if(webView.UIDelegate != nil) + { + _originalDelegate = webView.UIDelegate; + } + + webView.UIDelegate = self; + webView.navigationDelegate = self; _webView = webView; if (completion) completion(YES,nil); - /* - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(didReceiveStatusUpdate:) - name:L7SDidReceiveStatusUpdateNotification - object:nil]; - */ - [webView loadRequest:request]; } @@ -382,50 +392,205 @@ - (void)singleTapping:(UIGestureRecognizer *)recognizer } -# pragma mark - UIWebViewDelegate +# pragma mark - WKNavigationDelegate -- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error +- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { DLog(@"didFailLoadWithError %@",error); - if(_originalDelegate != nil && [_originalDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) + + if(_originalDelegate != nil && + [_originalDelegate respondsToSelector:@selector(webView:didFailProvisionalNavigation:withError:)]) { - return [_originalDelegate webView:webView didFailLoadWithError:error]; + return [_originalDelegate webView:webView didFailProvisionalNavigation:navigation withError:error]; } } -- (void)webViewDidStartLoad:(UIWebView *)webView{ +- (void) webView: (WKWebView *) webView didStartProvisionalNavigation: (WKNavigation *) navigation { - if(_originalDelegate != nil && [_originalDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) + if(_originalDelegate != nil && + [_originalDelegate respondsToSelector:@selector(webView:didStartProvisionalNavigation:)]) { - return [_originalDelegate webViewDidStartLoad:webView]; + return [_originalDelegate webView:webView didStartProvisionalNavigation:navigation]; } } -- (void)webViewDidFinishLoad:(UIWebView *)webView{ +- (void) webView: (WKWebView *) webView didFinishNavigation: (WKNavigation *) navigation { - if(_originalDelegate != nil && [_originalDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) + if(_originalDelegate != nil + && [_originalDelegate respondsToSelector:@selector(webView:didFinishNavigation:)]) { - return [_originalDelegate webViewDidFinishLoad:webView]; + return [_originalDelegate webView:webView didFinishNavigation:navigation]; } } -- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType -{ - DLog(@"shouldStartLoadWithRequest %@",request); - return (_originalDelegate != nil && [_originalDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)] ? +- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction +decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { + + if ([MASApplication currentApplication].isAuthenticated && + [self.class isProtectedResource:navigationAction.request.URL]) { - // - // Call the original delegate - // - [_originalDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType] : + NSString *endPoint = @"/connect/enterprise/browser/websso/login"; + NSMutableURLRequest *request = [navigationAction.request mutableCopy]; // - // Just return YES + // If websso/login endpoint do not add the authorization header + // Gets into a infinite loop as the server refreshes the page. // - YES); + if ([request.allHTTPHeaderFields valueForKey:@"Authorization"] == nil && + ![request.URL.absoluteString hasSuffix:endPoint]) { + + [self setAuthorization:request]; + [webView loadRequest:request]; + } + } + + decisionHandler(WKNavigationActionPolicyAllow); + + return (_originalDelegate != nil && [_originalDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)] ? + + // + // Call the original delegate + // + [_originalDelegate webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler] : + + // + // Just return YES + // + YES); +} + + +-(void) setAuthorization: (NSMutableURLRequest *) request { + + NSString *authorization = [MASUser authorizationBearerWithAccessToken]; + [request setValue:authorization forHTTPHeaderField:@"Authorization"]; +} + + +- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +completionHandler: +(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { + + SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; + + if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { + + SecPolicyRef policy = SecPolicyCreateBasicX509(); + CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); + NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:certificateCount]; + + for (CFIndex i = 0; i < certificateCount; i++) { + SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); + + [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)]; + } + + CFRelease(policy); + + { + for (id serverCertificateData in trustChain) { + if ([[self.class pinnedCertificates] containsObject:serverCertificateData]) { + + completionHandler(NSURLSessionAuthChallengeUseCredential, + [NSURLCredential credentialForTrust:serverTrust]); + + return; + } + } + + SecTrustResultType result = 0; + SecTrustEvaluate(serverTrust, &result); + + if (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed) { + + completionHandler(NSURLSessionAuthChallengeUseCredential, + [NSURLCredential credentialForTrust:serverTrust]); + + } else { + + completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); + } + } + } + else { + + if ([challenge previousFailureCount] == 0) { + //client side authentication + NSURLCredential * credential = [[MASSecurityService sharedService] createUrlCredential]; + if (credential) { + + completionHandler(NSURLSessionAuthChallengeUseCredential, credential); + } else { + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); + } + } else { + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); + } + } +} + ++ (NSArray *)pinnedCertificates { + static NSMutableArray *_pinnedCertificates = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSBundle *bundle = [NSBundle mainBundle]; + NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."]; + + NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[paths count]]; + for (NSString *path in paths) { + NSData *certificateData = [NSData dataWithContentsOfFile:path]; + [certificates addObject:certificateData]; + } + + _pinnedCertificates = [[NSMutableArray alloc] initWithArray:certificates]; + //adding the certificates from Json configuration + [_pinnedCertificates addObjectsFromArray:[[MASConfiguration currentConfiguration] gatewayCertificatesAsDERData]]; + + }); + return _pinnedCertificates; +} + + ++ (NSArray *)pinnedPublicKeys { + static NSArray *_pinnedPublicKeys = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSArray *pinnedCertificates = [self pinnedCertificates]; + NSMutableArray *publicKeys = [NSMutableArray arrayWithCapacity:[pinnedCertificates count]]; + + for (NSData *data in pinnedCertificates) { + SecCertificateRef allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)data); + NSParameterAssert(allowedCertificate); + + SecCertificateRef allowedCertificates[] = {allowedCertificate}; + CFArrayRef certificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL); + + SecPolicyRef policy = SecPolicyCreateBasicX509(); + SecTrustRef allowedTrust = NULL; + OSStatus status = SecTrustCreateWithCertificates(certificates, policy, &allowedTrust); + NSAssert(status == errSecSuccess, @"SecTrustCreateWithCertificates error: %ld", (long int)status); + + SecTrustResultType result = 0; + status = SecTrustEvaluate(allowedTrust, &result); + NSAssert(status == errSecSuccess, @"SecTrustEvaluate error: %ld", (long int)status); + + SecKeyRef allowedPublicKey = SecTrustCopyPublicKey(allowedTrust); + NSParameterAssert(allowedPublicKey); + [publicKeys addObject:(__bridge_transfer id)allowedPublicKey]; + + CFRelease(allowedTrust); + CFRelease(policy); + CFRelease(certificates); + CFRelease(allowedCertificate); + } + + _pinnedPublicKeys = [[NSArray alloc] initWithArray:publicKeys]; + }); + + return _pinnedPublicKeys; }