From d16df3ff0221e9cec304b3208b11f72f67fb3223 Mon Sep 17 00:00:00 2001 From: michael starke Date: Tue, 26 Aug 2014 18:59:26 +0200 Subject: [PATCH] Implemented password enforce and recommendation system --- MacPass/Base.lproj/DatePickingView.xib | 8 +-- MacPass/MPDatePickingViewController.m | 4 +- MacPass/MPDocument.h | 3 + MacPass/MPDocument.m | 20 ++++-- MacPass/MPDocumentWindowController.m | 89 +++++++++++++++++++++---- MacPass/en.lproj/Localizable.strings | Bin 9656 -> 10528 bytes 6 files changed, 99 insertions(+), 25 deletions(-) diff --git a/MacPass/Base.lproj/DatePickingView.xib b/MacPass/Base.lproj/DatePickingView.xib index 14f557e7..8144837e 100644 --- a/MacPass/Base.lproj/DatePickingView.xib +++ b/MacPass/Base.lproj/DatePickingView.xib @@ -1,8 +1,8 @@ - + - + @@ -49,7 +49,7 @@ AQABAAEAAQAB//+dkAEA//+PgAAE//+dkAEI//+dkAEMUERUAFBTVABQV1QAUFBUAAAAAAEAAAABA - + @@ -87,7 +87,6 @@ AQABAAEAAQAB//+dkAEA//+PgAAE//+dkAEI//+dkAEMUERUAFBTVABQV1QAUFBUAAAAAAEAAAABA - @@ -96,6 +95,7 @@ AQABAAEAAQAB//+dkAEA//+PgAAE//+dkAEI//+dkAEMUERUAFBTVABQV1QAUFBUAAAAAAEAAAABA + diff --git a/MacPass/MPDatePickingViewController.m b/MacPass/MPDatePickingViewController.m index c6c110e8..8e268cd8 100644 --- a/MacPass/MPDatePickingViewController.m +++ b/MacPass/MPDatePickingViewController.m @@ -50,9 +50,7 @@ typedef NS_ENUM(NSUInteger, MPDatePreset) { [presetMenu addItem:item]; } - MPDocument *document = [[self windowController] document]; - - [self.datePicker setDateValue:document.selectedItem.timeInfo.expiryTime]; + [self.datePicker setDateValue:[NSDate date]]; [self.presetPopupButton setAction:@selector(setDatePreset:)]; [self.presetPopupButton setMenu:presetMenu]; } diff --git a/MacPass/MPDocument.h b/MacPass/MPDocument.h index cee549a7..ad256add 100644 --- a/MacPass/MPDocument.h +++ b/MacPass/MPDocument.h @@ -161,6 +161,9 @@ typedef NS_OPTIONS(NSUInteger, MPEntrySearchFlags) { - (NSArray *)allEntries; - (NSArray *)allGroups; +- (BOOL)shouldRecommendPasswordChange; +- (BOOL)shouldEnforcePasswordChange; + /** * Determines, whether the given item is inside the trash. * The trash group itself is not considered as trashed. diff --git a/MacPass/MPDocument.m b/MacPass/MPDocument.m index 6ec8f755..fdd496a5 100644 --- a/MacPass/MPDocument.m +++ b/MacPass/MPDocument.m @@ -282,6 +282,7 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGroupKey BOOL isUnlocked = (nil != self.tree); if(isUnlocked) { + self.unlockCount += 1; [[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidUnlockDatabaseNotification object:self]; /* Make sure to only store */ MPAppDelegate *delegate = [NSApp delegate]; @@ -292,11 +293,6 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGroupKey else { self.compositeKey = nil; // clear the key? } - if(isUnlocked) { - - self.unlockCount += 1; - [[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidUnlockDatabaseNotification object:self]; - } return isUnlocked; } @@ -312,6 +308,8 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGroupKey [self.compositeKey setPassword:password andKeyfile:keyFileURL]; } self.tree.metaData.masterKeyChanged = [NSDate date]; + /* Key change is not undoable so just recored the change as done */ + [self updateChangeCount:NSChangeDone]; /* We need to store the key file once the user actually writes the database */ return YES; } @@ -438,6 +436,18 @@ NSString *const MPDocumentGroupKey = @"MPDocumentGroupKey return NO; } +- (BOOL)shouldEnforcePasswordChange { + KPKMetaData *metaData = self.tree.metaData; + if(!metaData.enforceMasterKeyChange) { return NO; } + return ( (24*60*60*metaData.masterKeyChangeEnforcementInterval) < -[metaData.masterKeyChanged timeIntervalSinceNow]); +} + +- (BOOL)shouldRecommendPasswordChange { + KPKMetaData *metaData = self.tree.metaData; + if(!metaData.recommendMasterKeyChange) { return NO; } + return ( (24*60*60*metaData.masterKeyChangeRecommendationInterval) < -[metaData.masterKeyChanged timeIntervalSinceNow]); +} + #pragma mark Data manipulation - (KPKEntry *)createEntry:(KPKGroup *)parent { if(!parent) { diff --git a/MacPass/MPDocumentWindowController.m b/MacPass/MPDocumentWindowController.m index 107ce2f0..fb4d248e 100644 --- a/MacPass/MPDocumentWindowController.m +++ b/MacPass/MPDocumentWindowController.m @@ -30,7 +30,7 @@ typedef NS_ENUM(NSUInteger, MPAlertContext) { MPAlertLossySaveWarning, }; -typedef void (^MPPasswordChangedBlock)(void); +typedef void (^MPPasswordChangedBlock)(BOOL didChangePassword); @interface MPDocumentWindowController () { @private @@ -88,7 +88,7 @@ typedef void (^MPPasswordChangedBlock)(void); MPDocument *document = [self document]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didRevertDocument:) name:MPDocumentDidRevertNotifiation object:document]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showEntries) name:MPDocumentDidUnlockDatabaseNotification object:document]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didUnlockDatabase:) name:MPDocumentDidUnlockDatabaseNotification object:document]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didAddEntry:) name:MPDocumentDidAddEntryNotification object:document]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didAddGroup:) name:MPDocumentDidAddGroupNotification object:document]; @@ -181,6 +181,12 @@ typedef void (^MPPasswordChangedBlock)(void); [self showInspector:self]; } +- (void)_didUnlockDatabase:(NSNotification *)notification { + [self showEntries]; + /* Show password reminders */ + [self _presentPasswordIntervalAlters]; +} + #pragma mark Actions - (void)saveDocument:(id)sender { self.passwordChangedBlock = nil; @@ -204,8 +210,12 @@ typedef void (^MPPasswordChangedBlock)(void); } else if(!document.compositeKey) { __weak MPDocument *weakDocument = [self document]; - self.passwordChangedBlock = ^void(void){[weakDocument saveDocument:sender];}; - [self editPassword:sender]; + self.passwordChangedBlock = ^void(BOOL didChangePassword){ + if(didChangePassword) { + [weakDocument saveDocument:sender]; + } + }; + [self _editPasswordRequiringValidInput:YES]; return; } /* All set and good ready to save */ @@ -216,8 +226,12 @@ typedef void (^MPPasswordChangedBlock)(void); MPDocument *document = [self document]; if(!document.compositeKey) { __weak MPDocument *weakDocument = [self document]; - self.passwordChangedBlock = ^void(void){[weakDocument saveDocumentAs:sender];}; - [self editPassword:sender]; + self.passwordChangedBlock = ^void(BOOL didChangePassword){ + if(didChangePassword) { + [weakDocument saveDocumentAs:sender]; + } + }; + [self _editPasswordRequiringValidInput:YES]; return; } [[self document] saveDocumentAs:sender]; @@ -269,12 +283,16 @@ typedef void (^MPPasswordChangedBlock)(void); } - (void)editPassword:(id)sender { + [self _editPasswordRequiringValidInput:YES]; +} + +- (void)_editPasswordRequiringValidInput:(BOOL)canCancel { if(!self.passwordEditWindowController) { self.passwordEditWindowController = [[MPPasswordEditWindowController alloc] initWithDocument:[self document]]; self.passwordEditWindowController.delegate = self; } /* Disallow empty password if we want to save afterwards, otherwise the dialog keeps poping up */ - self.passwordEditWindowController.allowsEmptyPasswordOrKey = (self.passwordChangedBlock == nil); + self.passwordEditWindowController.allowsEmptyPasswordOrKey = canCancel; [NSApp beginSheet:[self.passwordEditWindowController window] modalForWindow:[self window] modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; } @@ -327,9 +345,9 @@ typedef void (^MPPasswordChangedBlock)(void); else { [self.splitView addSubview:inspectorView]; [self.splitView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"[inspectorView(>=200)]" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(inspectorView)]]; + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(inspectorView)]]; [self.inspectorViewController updateResponderChain]; } [[NSUserDefaults standardUserDefaults] setBool:!inspectorWasVisible forKey:kMPSettingsKeyShowInspector]; @@ -422,13 +440,35 @@ typedef void (^MPPasswordChangedBlock)(void); #pragma mark MPPasswordEditWindowDelegate - (void)didFinishPasswordEditing:(BOOL)changedPasswordOrKey { - if(changedPasswordOrKey && self.passwordChangedBlock) { - self.passwordChangedBlock(); + if(self.passwordChangedBlock) { + self.passwordChangedBlock(changedPasswordOrKey); } self.passwordChangedBlock = nil; } -#pragma mark Alert Delegate +#pragma mark NSAlert handling +- (void)_presentPasswordIntervalAlters { + MPDocument *document = [self document]; + if(document.shouldEnforcePasswordChange) { + NSAlert *alert = [[NSAlert alloc] init]; + [alert setAlertStyle:NSCriticalAlertStyle]; + [alert setMessageText:NSLocalizedString(@"ENFORCE_PASSWORD_CHANGE_ALERT_TITLE", "")]; + [alert setInformativeText:NSLocalizedString(@"ENFORCE_PASSWORD_CHANGE_ALERT_DESCRIPTION", "")]; + [alert addButtonWithTitle:NSLocalizedString(@"CHANGE_PASSWORD", "")]; + [alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(_enforcePasswordChangeAlertDidEnd:returnCode:contextInfo:) contextInfo:NULL]; + } + else if(document.shouldRecommendPasswordChange) { + NSAlert *alert = [[NSAlert alloc] init]; + [alert setAlertStyle:NSInformationalAlertStyle]; + [alert setMessageText:NSLocalizedString(@"RECOMMEND_PASSWORD_CHANGE_ALERT_TITLE", "")]; + [alert setInformativeText:NSLocalizedString(@"RECOMMEND_PASSWORD_CHANGE_ALERT_DESCRIPTION", "")]; + [alert addButtonWithTitle:NSLocalizedString(@"CHANGE_PASSWORD", "")]; + [alert addButtonWithTitle:NSLocalizedString(@"CANCEL", "")]; + [[alert buttons][1] setKeyEquivalent:[NSString stringWithFormat:@"%c", 0x1b]]; + [alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(_recommentPasswordChangeAlertDidEnd:returnCode:contextInfo:) contextInfo:NULL]; + } +} + - (void)_dataLossOnSaveAlertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { switch(returnCode) { @@ -450,6 +490,29 @@ typedef void (^MPPasswordChangedBlock)(void); } } +- (void)_recommentPasswordChangeAlertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { + if(returnCode == NSAlertFirstButtonReturn) { + id __weak welf = self; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [welf _editPasswordRequiringValidInput:YES]; + }); + + } +} + +- (void)_enforcePasswordChangeAlertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { + NSAssert(returnCode == NSAlertFirstButtonReturn, @"Return for password change should always be NSAlertFirstButtonReturn"); + id __weak welf = self; + self.passwordChangedBlock = ^(BOOL didChangePassword){ + if(!didChangePassword) { + [welf _presentPasswordIntervalAlters]; + } + }; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [welf _editPasswordRequiringValidInput:NO]; + }); +} + #pragma mark - #pragma mark UI Helper diff --git a/MacPass/en.lproj/Localizable.strings b/MacPass/en.lproj/Localizable.strings index 6dc93a9be3cc28cd020c5f652e342910eedbc9f7..f4ea55fe9129cca6a240e25de25c633cc7ecd0b6 100644 GIT binary patch delta 616 zcmb7?%}N6?6orrALaWe96?J81s)8HEwG@%IR$Ha4HTVs1 zZeNxbZ9`4vH6kROmz*(--7PJw89btg8}wnI;lalaa$LvBzoAO?ChjqS!UJ=4I25ZG zt^;&k#g6++=%rXY!tmRgxClkEcFHspF-BvHzI{8lS}eC6Z<^82rqH6VcwIZ{%@MV5 z2PCghmtzj;q58iz(HVJ-t0BbM+#@QU{?d*oJr@zPV{-n`Zd?ZHS3YF_ib}*>4lS@ DQqgVb delta 12 TcmZ1ww8MKtoZ{wVDp4!|C3poO