6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell/*
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell * The contents of this file are subject to the terms of the Common Development and
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell * Distribution License (the License). You may not use this file except in compliance with the
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell * License.
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell *
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell * specific language governing permission and limitations under the License.
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell *
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell * When distributing Covered Software, include this CDDL Header Notice in each file and include
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell * Header, with the fields enclosed by brackets [] replaced by your own identifying
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell * information: "Portions copyright [year] [name of copyright owner]".
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell *
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell * Copyright 2016 ForgeRock AS.
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell */
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell#import "FRABlockAlertView.h"
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell#import "FRAIdentity.h"
415243fbc81341293a852ff6aa14e9608d08685cCraig McDonnell#import "FRAIdentityModel.h"
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell#import "FRAPushMechanism.h"
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell#import "FRANotification.h"
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell#import "FRANotificationHandler.h"
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell#import "FRANotificationViewController.h"
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbings#import "FRAMessageUtils.h"
c0a2f8c6e84ddf9a597f19e5f161382b0e2cf81bDiego Colantoni#import "FRAQRUtils.h"
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbings#import <JWT.h>
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell/*!
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell * Private interface.
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell */
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell@interface FRANotificationHandler ()
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell/*!
415243fbc81341293a852ff6aa14e9608d08685cCraig McDonnell * The identity model.
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell */
415243fbc81341293a852ff6aa14e9608d08685cCraig McDonnell@property (nonatomic, strong, readonly) FRAIdentityModel *identityModel;
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell/*!
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell * The database to which notifications should be persisted.
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell */
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell@property (strong, nonatomic) FRAIdentityDatabase *database;
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell@end
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell@implementation FRANotificationHandler
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbingsstatic NSString * const TTL_KEY = @"t";
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbingsstatic NSString * const MESSAGE_ID_KEY_PATH = @"aps.messageId";
a0f7bc4425f8e8f863d60ccd0cd909d341bb4f9eKen Stubbingsstatic NSString * const MESSAGE_DATA_PATH = @"aps.data";
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbingsstatic NSString * const CHALLENGE_KEY = @"c";
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbingsstatic NSString * const MECHANISM_UID_KEY = @"u";
5d37db6a3aca50ba14cba8909d3ae44f7d43e407Ken Stubbingsstatic NSString * const LOAD_BALANCE_KEY = @"l";
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell
415243fbc81341293a852ff6aa14e9608d08685cCraig McDonnell- (instancetype)initWithDatabase:(FRAIdentityDatabase *)database identityModel:(FRAIdentityModel *)identityModel {
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell self = [super init];
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell if (self) {
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell _database = database;
415243fbc81341293a852ff6aa14e9608d08685cCraig McDonnell _identityModel = identityModel;
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell }
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell return self;
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell}
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell
415243fbc81341293a852ff6aa14e9608d08685cCraig McDonnell+ (instancetype)handlerWithDatabase:(FRAIdentityDatabase *)database identityModel:(FRAIdentityModel *)identityModel {
415243fbc81341293a852ff6aa14e9608d08685cCraig McDonnell return [[FRANotificationHandler alloc] initWithDatabase:database identityModel:identityModel];
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell}
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)messageData {
415243fbc81341293a852ff6aa14e9608d08685cCraig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell FRANotification *notification = [self notificationFromRemoteNotification:messageData];
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell if (!notification || !notification.pending) {
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell // if the notification is nil then there was a problem looking up the mechanism
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell // if the notification is not pending, then the notification timed out or was dealt with by opening the app
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell // from the homescreen and navigating to the notification; either way, there's nothing further to do here
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell return;
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell }
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell [self showNotification:notification ifForegroundApplication:application];
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell}
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell- (FRANotification *)notificationFromRemoteNotification:(NSDictionary *)messageData {
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni NSError* error;
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbings // Decode JWT from modessage
5d37db6a3aca50ba14cba8909d3ae44f7d43e407Ken Stubbings NSString* dataJwt = [messageData valueForKeyPath:MESSAGE_DATA_PATH];
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbings
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni NSDictionary *payload = [FRAMessageUtils extractJTWBodyFromString:dataJwt error:&error];
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni if (!payload) {
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni [self showAlertWithTitle:NSLocalizedString(@"notification_request_error_title", nil)
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni message:nil
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni handler:nil];
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni }
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbings
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbings NSLog(@"message data: %@", payload);
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbings
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell // Lookup push mechanism to which the notification should be added
1d464fc27913cfdeb6e09c8ab5d06ada64f94d55Ken Stubbings
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbings FRAPushMechanism *mechanism = [self pushMechanismTargetForRemoteNotification:payload];
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell if (!mechanism) {
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell return nil;
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell }
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell // Try to look up an existing notification (in case this message has already been handled)
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbings FRANotification *notification = [mechanism notificationWithMessageId:[messageData valueForKeyPath:MESSAGE_ID_KEY_PATH]];
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell if (notification) {
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell return notification;
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell }
1d464fc27913cfdeb6e09c8ab5d06ada64f94d55Ken Stubbings
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell // otherwise, create the notification from the message and add it to the mechanism
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbings NSTimeInterval timeToLive = [[payload objectForKey:TTL_KEY] doubleValue];
5d37db6a3aca50ba14cba8909d3ae44f7d43e407Ken Stubbings
5d37db6a3aca50ba14cba8909d3ae44f7d43e407Ken Stubbings NSString * loadBalancerCookieData = [[NSString alloc] initWithData:[FRAQRUtils decodeBase64:[payload objectForKey:LOAD_BALANCE_KEY]] encoding:NSUTF8StringEncoding];
465ea459a87d4605e145d8f45b6a9c104b696e3bCraig McDonnell notification = [FRANotification notificationWithDatabase:self.database
f877f6ca2428244a6d0954a1dbef471577b32c60Diego Colantoni identityModel:_identityModel
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbings messageId:[messageData valueForKeyPath:MESSAGE_ID_KEY_PATH]
465ea459a87d4605e145d8f45b6a9c104b696e3bCraig McDonnell challenge:[payload objectForKey:CHALLENGE_KEY]
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell timeReceived:[NSDate date]
5d37db6a3aca50ba14cba8909d3ae44f7d43e407Ken Stubbings timeToLive:timeToLive
5d37db6a3aca50ba14cba8909d3ae44f7d43e407Ken Stubbings loadBalancerCookieData:loadBalancerCookieData];
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbings
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni if (![mechanism addNotification:notification error:&error]) {
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni [self showAlertWithTitle:NSLocalizedString(@"notification_request_error_title", nil)
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni message:nil
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni handler:nil];
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni }
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell return notification;
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell}
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell- (FRAPushMechanism *)pushMechanismTargetForRemoteNotification:(NSDictionary *)messageData {
a1e92b2783be4bfeb0c7e267223cc7779a6f324cKen Stubbings NSString *mechanismId = [messageData objectForKey:MECHANISM_UID_KEY];
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell FRAMechanism *mechanism = [self.identityModel mechanismWithId:mechanismId];
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell if ([mechanism isKindOfClass:[FRAPushMechanism class]]) {
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell return (FRAPushMechanism *)mechanism;
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell } else {
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell return nil;
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell }
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell}
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell- (void)showNotification:(FRANotification *)notification ifForegroundApplication:(UIApplication *)application {
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell // jump to the notification if the app was in the foreground when the notification arrived or has
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell // been opened by clicking on the notification
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell void (^showNotification)() = ^{
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell UIViewController *rootViewController = application.delegate.window.rootViewController;
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell UIStoryboard *storyboard = rootViewController.storyboard;
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell FRANotificationViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:FRANotificationViewControllerStoryboardIdentifer];
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell viewController.notification = notification;
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell [rootViewController presentViewController:viewController animated:YES completion:NULL];
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell };
1d464fc27913cfdeb6e09c8ab5d06ada64f94d55Ken Stubbings
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell switch (application.applicationState) {
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell case UIApplicationStateBackground: {
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell // don't show notification if app is running in background
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell }
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell break;
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell case UIApplicationStateActive: {
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell // the notification arrived while the app was in the foreground
d43eb4b85c3a056c9e70826253d9a9090239d500Craig McDonnell showNotification();
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell }
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell break;
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell case UIApplicationStateInactive: {
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell // the application is being opened from the notification
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell showNotification();
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell }
916ef74523ecddc8140815c084ab6971ee366bcfCraig McDonnell break;
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell }
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell}
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message handler:(void (^)(NSInteger))handler {
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni FRABlockAlertView *alertView = [[FRABlockAlertView alloc] initWithTitle:title
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni message:message
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni delegate:nil
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni cancelButtonTitle:nil
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni otherButtonTitle:NSLocalizedString(@"ok", nil)
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni handler:handler];
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni [alertView show];
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni}
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni
6c1420dd55f69d09f39dd213ee6c97ba901b8d92Craig McDonnell@end