721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * The contents of this file are subject to the terms of the Common Development and
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * Distribution License (the License). You may not use this file except in compliance with the
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * specific language governing permission and limitations under the License.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * When distributing Covered Software, include this CDDL Header Notice in each file and include
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * Header, with the fields enclosed by brackets [] replaced by your own identifying
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * information: "Portions copyright [year] [name of copyright owner]".
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * Copyright 2016 ForgeRock AS.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott#import "FMDatabase.h"
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott#import "FRAError.h"
2ff6fc247a59e7bebd6f71595469a511fe7456efRobert Wapshott#import "FRAFMDatabaseConnectionHelper.h"
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni#import "FRAOathCode.h"
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott#import "FRAIdentity.h"
2ff6fc247a59e7bebd6f71595469a511fe7456efRobert Wapshott#import "FRAModelObjectProtected.h"
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott#import "FRAModelsFromDatabase.h"
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott#import "FRANotification.h"
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni#import "FRAHotpOathMechanism.h"
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni#import "FRATotpOathMechanism.h"
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott#import "FRAPushMechanism.h"
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott#import "FRASerialization.h"
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * The parsing logic operates as follows:
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * Perform SQL query to fetch all Identities, Mechanisms and Notifications where each
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * Identity may have multiple associated Mechanisms and PushMechanisms may have multiple
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * associated Notifications.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * The function will re-use matching Identities and Push Mechanisms as appropriate.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott * TODO: Split into smaller functions.
6a2ae9c7fb4d2c40d75cab0edaf940f22c18224fDiego Colantoni+ (NSArray<FRAIdentity*> *)allIdentitiesWithDatabase:(FRAFMDatabaseConnectionHelper *)sqlDatabase identityDatabase:(FRAIdentityDatabase *)identityDatabase identityModel:(FRAIdentityModel *)identityModel error:(NSError *__autoreleasing *)error {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *sql = [FRAFMDatabaseConnectionHelper readSchema:@"read_all" withError:error];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Open Database
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott database = [sqlDatabase getConnectionWithError:error];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Perform update
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott FMResultSet *results = [database executeQuery:sql];
78c07714ec1113f7f21c75b818f2bf6a7021618aDiego Colantoni *error = [FRAError createErrorForLastFailure:database];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Output rows for debugging purposes.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSLog(@"Reading all rows from the database:");
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSMutableArray* identities = [[NSMutableArray alloc] init];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *issuer = [FRASerialization nullToEmpty:[results stringForColumn:@"issuer"]];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *accountName = [FRASerialization nullToEmpty:[results stringForColumn:@"accountName"]];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *imageURL = [FRASerialization nullToEmpty:[results stringForColumn:@"imageURL"]];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *bgColor = [FRASerialization nullToEmpty:[results stringForColumn:@"bgColor"]];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *type = [FRASerialization nullToEmpty:[results stringForColumn:@"type"]];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSInteger version = [results intForColumn:@"version"];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *mechanismUID = [FRASerialization nullToEmpty:[results stringForColumn:@"mechanismUID"]];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *optionsJSON = [FRASerialization nullToEmpty:[results stringForColumn:@"options"]];
2ff6fc247a59e7bebd6f71595469a511fe7456efRobert Wapshott // Notification
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *timeReceived = [FRASerialization nullToEmpty:[results stringForColumn:@"timeReceived"]];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *timeExpired = [FRASerialization nullToEmpty:[results stringForColumn:@"timeExpired"]];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *data = [FRASerialization nullToEmpty:[results stringForColumn:@"data"]];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott int pending = [results intForColumn:@"pending"];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott int approved = [results intForColumn:@"approved"];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSLog(@"[%d] %@ %@ %@ %@ %@ %ld %@ %@ %@ %d %d",
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Create an Identity
f877f6ca2428244a6d0954a1dbef471577b32c60Diego Colantoni image:[[NSURL alloc]initWithString:imageURL]
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Check if we already have generated this identity, in which case re-use.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if ([newIdentity.issuer isEqualToString:identity.issuer] && [newIdentity.accountName isEqualToString:identity.accountName]) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Formatter for parsing numbers from Strings.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Create the Mechanism
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni if ([type isEqualToString:[FRAHotpOathMechanism mechanismType]]) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Options Map is a String to String mapping stored in JSON.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (![FRASerialization deserializeJSON:optionsJSON intoDictionary:&optionsMap error:error]) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Secret Key - Base 64 encoded bytes
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *secretValue = [optionsMap objectForKey:OATH_MECHANISM_SECRET];
0fc1bec9becddcbec0d26541dd7ef40b43c0a67bKen Stubbings NSData* secret = [FRASerialization deserializeSecret:secretValue];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Algorithm - String enumeration
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *algorithmValue = [optionsMap objectForKey:OATH_MECHANISM_ALGORITHM];
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni CCHmacAlgorithm algorithm = [FRAOathCode fromString:algorithmValue];
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni // Code Length - String value of Integer
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni NSString *codeLengthValue = [optionsMap objectForKey:OATH_MECHANISM_DIGITS];
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni int codeLength = [[numberFormatter numberFromString:codeLengthValue] intValue];
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni // Counter - String value of unsigned Long Long
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni NSString *counterValue = [optionsMap objectForKey:OATH_MECHANISM_COUNTER];
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni u_int64_t counter = [[numberFormatter numberFromString:counterValue] unsignedLongLongValue];
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni FRAHotpOathMechanism *newMechanism = [FRAHotpOathMechanism mechanismWithDatabase:identityDatabase
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni // Note: We are not de-duplicating OATH Mechanism because they will not be duplicated in the SQL results.
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni if (![newIdentity addMechanism:newMechanism error:error]) {
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni } else if ([type isEqualToString:[FRATotpOathMechanism mechanismType]]) {
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni // Options Map is a String to String mapping stored in JSON.
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni if (![FRASerialization deserializeJSON:optionsJSON intoDictionary:&optionsMap error:error]) {
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni // Secret Key - Base 64 encoded bytes
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni NSString *secretValue = [optionsMap objectForKey:OATH_MECHANISM_SECRET];
6ca6bef8bd63e8c1fd132e38f8e3904b8d2ad172Ken Stubbings NSData* secret = [FRASerialization deserializeSecret:secretValue];
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni // Algorithm - String enumeration
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni NSString *algorithmValue = [optionsMap objectForKey:OATH_MECHANISM_ALGORITHM];
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni CCHmacAlgorithm algorithm = [FRAOathCode fromString:algorithmValue];
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni // Code Length - String value of Integer
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni NSString *codeLengthValue = [optionsMap objectForKey:OATH_MECHANISM_DIGITS];
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni int codeLength = [[numberFormatter numberFromString:codeLengthValue] intValue];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Period - String value of unsigned Integer
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *periodValue = [optionsMap objectForKey:OATH_MECHANISM_PERIOD];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott u_int32_t period = [[numberFormatter numberFromString:periodValue] unsignedIntValue];
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni FRATotpOathMechanism *newMechanism = [FRATotpOathMechanism mechanismWithDatabase:identityDatabase
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni // Note: We are not de-duplicating OATH Mechanism because they will not be duplicated in the SQL results.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (![newIdentity addMechanism:newMechanism error:error]) {
caa9e77dc369fea8df9ae2c598d3c83b7214c1cfDiego Colantoni } else if ([type isEqualToString:[FRAPushMechanism mechanismType]]) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Options Map is a String to String mapping stored in JSON.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (![FRASerialization deserializeJSON:optionsJSON intoDictionary:&optionsMap error:error]) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Secret stored as String (Base64?)
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *secretValue = [optionsMap objectForKey:PUSH_MECHANISM_SECRET];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Auth Endpoint as string
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *authEndpointValue = [optionsMap objectForKey:PUSH_MECHANISM_AUTH_END_POINT];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Version as string
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSString *versionString = [optionsMap objectForKey:PUSH_MECHANISM_VERSION];
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott NSInteger version = [[numberFormatter numberFromString:versionString] integerValue];
f877f6ca2428244a6d0954a1dbef471577b32c60Diego Colantoni FRAPushMechanism *newMechanism = [FRAPushMechanism pushMechanismWithDatabase:identityDatabase
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Check to see if we already have this PushMechanism present, otherwise add it in.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott for (FRAMechanism *mechanism in newIdentity.mechanisms) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if ([mechanism isKindOfClass:[FRAPushMechanism class]]) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott FRAPushMechanism *pushMechanism = (FRAPushMechanism *)mechanism;
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if ([pushMechanism.mechanismUID isEqualToString:newMechanism.mechanismUID]) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott if (![newIdentity addMechanism:newMechanism error:error]) {
2ff6fc247a59e7bebd6f71595469a511fe7456efRobert Wapshott // If we have a notification, parse and create the Notification for the Push Mechanism.
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott // Note: Notifications can only currently exist for PushMechanisms.
2ff6fc247a59e7bebd6f71595469a511fe7456efRobert Wapshott if (timeReceived != nil && [timeReceived length] > 0) {
5d37db6a3aca50ba14cba8909d3ae44f7d43e407Ken Stubbings // Extract notifcation data from database
5d37db6a3aca50ba14cba8909d3ae44f7d43e407Ken Stubbings NSDate *dateTimeReceived = [NSDate dateWithTimeIntervalSince1970:[timeReceived doubleValue]];
2ff6fc247a59e7bebd6f71595469a511fe7456efRobert Wapshott if (![FRASerialization deserializeJSON:data intoDictionary:&dataMap error:error]) {
2ff6fc247a59e7bebd6f71595469a511fe7456efRobert Wapshott NSString *messageId = [dataMap valueForKey:NOTIFICATION_MESSAGE_ID];
2ff6fc247a59e7bebd6f71595469a511fe7456efRobert Wapshott NSString *challenge = [[NSString alloc] initWithData:[FRASerialization deserializeBytes:[dataMap valueForKey:NOTIFICATION_PUSH_CHALLENGE]] encoding:NSUTF8StringEncoding];
2ff6fc247a59e7bebd6f71595469a511fe7456efRobert Wapshott NSTimeInterval ttl = [[dataMap valueForKey:NOTIFICATION_TIME_TO_LIVE] doubleValue];
5d37db6a3aca50ba14cba8909d3ae44f7d43e407Ken Stubbings NSString *loadBalancerCookieData = [dataMap valueForKey:NOTIFICATION_LOAD_BALANCER_COOKIE];
5d37db6a3aca50ba14cba8909d3ae44f7d43e407Ken Stubbings // recreate notificaiton
2ff6fc247a59e7bebd6f71595469a511fe7456efRobert Wapshott FRANotification *notification = [FRANotification notificationWithDatabase:identityDatabase
2ff6fc247a59e7bebd6f71595469a511fe7456efRobert Wapshott if (![newMechanism addNotification:notification error:error]) {
721bb987c406979bcfe705fa1ca8d54497d40fcbRobert Wapshott @throw [FRAError createIllegalStateException:@"Invalid mechanism"];
2ff6fc247a59e7bebd6f71595469a511fe7456efRobert Wapshott // As we have read the objects from the database, marked them as stored.
2ff6fc247a59e7bebd6f71595469a511fe7456efRobert Wapshott for (FRAMechanism *mechanism in identity.mechanisms) {
2ff6fc247a59e7bebd6f71595469a511fe7456efRobert Wapshott for (FRANotification *notification in mechanism.notifications) {