diff options
author | rowanbeentje <rowan@beent.je> | 2009-10-16 01:24:45 +0000 |
---|---|---|
committer | rowanbeentje <rowan@beent.je> | 2009-10-16 01:24:45 +0000 |
commit | c75dd33c1ccb4d64fa413357b9cdbfca36ee2f6a (patch) | |
tree | 83f3fa356109b4b6c0c70f6da4c12fb6da67555b /Source | |
parent | b74377b6341d4b861b254ffcfb983d2cb27ec5b6 (diff) | |
download | sequelpro-c75dd33c1ccb4d64fa413357b9cdbfca36ee2f6a.tar.gz sequelpro-c75dd33c1ccb4d64fa413357b9cdbfca36ee2f6a.tar.bz2 sequelpro-c75dd33c1ccb4d64fa413357b9cdbfca36ee2f6a.zip |
Significant User Management improvements:
- When reading data from the server, correctly map values to the SPUser object values to prevent data loss
- Ask server for priv support; disable unsupported privs in the interface, and only try to grant/revoke supported privs. Fixes errors saving users on MySQL < 6.
- Make "check all" and "uncheck all" buttons functional
- Add checkboxes for MySQL 6 privs as we already map the data for them
- Fix double retain of priv mapping dictionary
- Error dialogs stay on screen for longer than half a second
- New hosts are now selected for editing when added
This addresses some of the issues in Issue #349.
Diffstat (limited to 'Source')
-rw-r--r-- | Source/SPUserManager.h | 6 | ||||
-rw-r--r-- | Source/SPUserManager.m | 156 | ||||
-rw-r--r-- | Source/SPUserManager.xcdatamodel/elements | bin | 50105 -> 62017 bytes | |||
-rw-r--r-- | Source/SPUserManager.xcdatamodel/layout | bin | 5719 -> 6447 bytes |
4 files changed, 117 insertions, 45 deletions
diff --git a/Source/SPUserManager.h b/Source/SPUserManager.h index 323f2fe7..d673226a 100644 --- a/Source/SPUserManager.h +++ b/Source/SPUserManager.h @@ -31,7 +31,7 @@ NSPersistentStoreCoordinator *persistentStoreCoordinator; NSManagedObjectModel *managedObjectModel; NSManagedObjectContext *managedObjectContext; - NSDictionary *privColumnsMODict; + NSDictionary *privColumnToGrantMap; BOOL isInitializing; @@ -40,6 +40,7 @@ IBOutlet NSOutlineView* outlineView; IBOutlet NSTabView *tabView; IBOutlet NSTreeController *treeController; + IBOutlet NSMutableDictionary *privsSupportedByServer; } - (NSPersistentStoreCoordinator *)persistentStoreCoordinator; @@ -56,11 +57,14 @@ - (IBAction)addUser:(id)sender; - (IBAction)removeUser:(id)sender; - (IBAction)addHost:(id)sender; +- (void)editNewHost; - (IBAction)removeHost:(id)sender; // General - (IBAction)doCancel:(id)sender; - (IBAction)doApply:(id)sender; +- (IBAction)checkAllPrivileges:(id)sender; +- (IBAction)uncheckAllPrivileges:(id)sender; // Core Data Notifications - (void)contextDidSave:(NSNotification *)notification; diff --git a/Source/SPUserManager.m b/Source/SPUserManager.m index 44f499be..aaa56b1a 100644 --- a/Source/SPUserManager.m +++ b/Source/SPUserManager.m @@ -58,14 +58,22 @@ } [self setConnection:connection]; - - privColumnsMODict = [[[NSDictionary alloc] initWithObjectsAndKeys: - @"grant_option_priv",@"Grant_priv", - @"show_databases_priv",@"Show_db_priv", - @"create_temporary_tables_priv",@"Create_tmp_tables_priv", - @"Replication_slave_priv",@"Repl_slave_priv", - @"Replication_client_priv",@"Repl_client_priv",nil] retain]; - + + // When reading privileges from the database, they are converted automatically to a + // lowercase key used in the user privileges stores, from which a GRANT syntax + // is derived automatically. While most keys can be automatically converted without + // any difficulty, some keys differ slightly in mysql column storage to GRANT syntax; + // this dictionary provides mappings for those values to ensure consistency. + privColumnToGrantMap = [[NSDictionary alloc] initWithObjectsAndKeys: + @"Grant_option_priv", @"Grant_priv", + @"Show_databases_priv", @"Show_db_priv", + @"Create_temporary_tables_priv", @"Create_tmp_table_priv", + @"Replication_slave_priv", @"Repl_slave_priv", + @"Replication_client_priv", @"Repl_client_priv", + nil]; + + privsSupportedByServer = [[NSMutableDictionary alloc] init]; + return self; } @@ -76,7 +84,8 @@ [managedObjectContext release], managedObjectContext = nil; [persistentStoreCoordinator release], persistentStoreCoordinator = nil; [managedObjectModel release], managedObjectModel = nil; - [privColumnsMODict release], privColumnsMODict = nil; + [privColumnToGrantMap release], privColumnToGrantMap = nil; + [privsSupportedByServer release], privsSupportedByServer = nil; [mySqlConnection release]; [super dealloc]; @@ -108,7 +117,8 @@ - (void)_initializeUsers { isInitializing = TRUE; - + NSMutableString *privKey; + NSArray *privRow; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSMutableArray *resultAsArray = [NSMutableArray array]; @@ -126,12 +136,36 @@ { [resultAsArray addObject:[result fetchRowAsDictionary]]; } - [usersResultArray addObjectsFromArray:resultAsArray]; [self _initializeTree:usersResultArray]; [result release]; + + // Set up the array of privs supported by this server. + [privsSupportedByServer removeAllObjects]; + + // Attempt to use SHOW PRIVILEGES syntax - supported since 4.1.0 + result = [[self connection] queryString:@"SHOW PRIVILEGES"]; + if ([result numOfRows]) { + while (privRow = [result fetchRowAsArray]) { + privKey = [NSMutableString stringWithString:[[privRow objectAtIndex:0] lowercaseString]]; + [privKey replaceOccurrencesOfString:@" " withString:@"_" options:NSLiteralSearch range:NSMakeRange(0, [privKey length])]; + [privKey appendString:@"_priv"]; + [privsSupportedByServer setValue:[NSNumber numberWithBool:YES] forKey:privKey]; + } + + // If that fails, base privilege support on the mysql.users columns + } else { + result = [[self connection] queryString:@"SHOW COLUMNS FROM `mysql`.`user`"]; + while (privRow = [result fetchRowAsArray]) { + privKey = [NSMutableString stringWithString:[privRow objectAtIndex:0]]; + if (![privKey hasSuffix:@"_priv"]) continue; + if ([privColumnToGrantMap objectForKey:privKey]) privKey = [privColumnToGrantMap objectForKey:privKey]; + [privsSupportedByServer setValue:[NSNumber numberWithBool:YES] forKey:[privKey lowercaseString]]; + } + } + [pool release]; isInitializing = FALSE; } @@ -186,13 +220,14 @@ NS_DURING if ([key hasSuffix:@"_priv"]) { + BOOL value = [[item objectForKey:key] boolValue]; + // Special case keys - if ([privColumnsMODict objectForKey:key] != nil) + if ([privColumnToGrantMap objectForKey:key]) { - key = [privColumnsMODict objectForKey:key]; + key = [privColumnToGrantMap objectForKey:key]; } - BOOL value = [[item objectForKey:key] boolValue]; [child setValue:[NSNumber numberWithBool:value] forKey:key]; } else if ([key hasPrefix:@"max"]) @@ -206,7 +241,7 @@ [child setValue:value forKey:key]; } NS_HANDLER - NSLog(@"%@ not implemented yet.", key); + DLog(@"%@ not implemented yet.", key); NS_ENDHANDLER } @@ -372,6 +407,41 @@ // [self _clearData]; } +- (IBAction)checkAllPrivileges:(id)sender +{ + id selectedUser = [[treeController selectedObjects] objectAtIndex:0]; + + // Iterate through the supported privs, setting the value of each to true + for (NSString *key in privsSupportedByServer) { + if (![key hasSuffix:@"_priv"]) continue; + + // Perform the change in a try/catch check to avoid exceptions for unhandled privs + @try { + [selectedUser setValue:[NSNumber numberWithBool:TRUE] forKey:key]; + } + @catch (NSException * e) { + } + } +} + +- (IBAction)uncheckAllPrivileges:(id)sender +{ + id selectedUser = [[treeController selectedObjects] objectAtIndex:0]; + + // Iterate through the supported privs, setting the value of each to false + for (NSString *key in privsSupportedByServer) { + if (![key hasSuffix:@"_priv"]) continue; + + // Perform the change in a try/catch check to avoid exceptions for unhandled privs + @try { + [selectedUser setValue:[NSNumber numberWithBool:FALSE] forKey:key]; + } + @catch (NSException * e) { + } + } + +} + - (IBAction)addUser:(id)sender { if ([[treeController selectedObjects] count] > 0) @@ -405,11 +475,15 @@ } } [treeController addChild:sender]; - // Need to figure out how to do this right. I want to be able to have the newly - // added item be in edit mode to change the host name. - NSLog(@"selectedRow: %d", [outlineView selectedRow]); - NSIndexPath *indexPath = [treeController selectionIndexPath]; - NSLog(@"selectedChild: %d", [indexPath indexAtPosition:[outlineView selectedRow]]); + + // The newly added item will be selected as it is added, but only after the next iteration of the + // run loop - edit it after a tiny delay. + [self performSelector:@selector(editNewHost) withObject:nil afterDelay:0.1]; +} + +// Perform a deferred edit of the currently selected row. +- (void)editNewHost +{ [outlineView editColumn:0 row:[outlineView selectedRow] withEvent:nil select:TRUE]; } @@ -488,7 +562,7 @@ - (void)contextDidChange:(NSNotification *)notification { - NSLog(@"contextDidChange:"); + DLog(@"contextDidChange:"); if (!isInitializing) { @@ -551,25 +625,26 @@ { if ([user valueForKey:@"parent"] != nil) { - NSDictionary *attributesDict = [[user entity] attributesByName]; NSMutableArray *grantPrivileges = [NSMutableArray array]; NSMutableArray *revokePrivileges = [NSMutableArray array]; - for(NSString *key in [attributesDict allKeys]) + for(NSString *key in privsSupportedByServer) { - if ([key hasSuffix:@"_priv"]) - { - NSString *privilege = [key stringByReplacingOccurrencesOfString:@"_priv" withString:@""]; - - if ([[user valueForKey:key] boolValue] == TRUE) - { + if (![key hasSuffix:@"_priv"]) continue; + NSString *privilege = [key stringByReplacingOccurrencesOfString:@"_priv" withString:@""]; + + + // Check the value of the priv and assign to grant or revoke query as appropriate; do this + // in a try/catch check to avoid exceptions for unhandled privs + @try { + if ([[user valueForKey:key] boolValue] == TRUE) { [grantPrivileges addObject:[NSString stringWithFormat:@"%@", [privilege replaceUnderscoreWithSpace]]]; - } - else - { + } else { [revokePrivileges addObject:[NSString stringWithFormat:@"%@", [privilege replaceUnderscoreWithSpace]]]; } } + @catch (NSException * e) { + } } // Grant privileges if ([grantPrivileges count] > 0) @@ -578,7 +653,7 @@ [grantPrivileges componentsJoinedByCommas], [[[user parent] valueForKey:@"user"] tickQuotedString], [[user valueForKey:@"host"] tickQuotedString]]; - NSLog(@"%@", grantStatement); + DLog(@"%@", grantStatement); [[self connection] queryString:[NSString stringWithFormat:grantStatement]]; [self checkAndDisplayMySqlError]; } @@ -590,7 +665,7 @@ [revokePrivileges componentsJoinedByCommas], [[[user parent] valueForKey:@"user"] tickQuotedString], [[user valueForKey:@"host"] tickQuotedString]]; - NSLog(@"%@", revokeStatement); + DLog(@"%@", revokeStatement); [[self connection] queryString:[NSString stringWithFormat:revokeStatement]]; [self checkAndDisplayMySqlError]; } @@ -630,16 +705,9 @@ { if (![[[self connection] getLastErrorMessage] isEqualToString:@""]) { - NSBeginAlertSheet(@"MySQL Error", - nil, - nil, - nil, - [self window], - self, - NULL, - NULL, - nil, - [[self connection] getLastErrorMessage]); + NSAlert *alert = [NSAlert alertWithMessageText:@"MySQL Error" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:[[self connection] getLastErrorMessage]]; + [alert runModal]; + return FALSE; } else { return TRUE; diff --git a/Source/SPUserManager.xcdatamodel/elements b/Source/SPUserManager.xcdatamodel/elements Binary files differindex 8bca9833..6ef34877 100644 --- a/Source/SPUserManager.xcdatamodel/elements +++ b/Source/SPUserManager.xcdatamodel/elements diff --git a/Source/SPUserManager.xcdatamodel/layout b/Source/SPUserManager.xcdatamodel/layout Binary files differindex 5c8f25a4..b1f295a4 100644 --- a/Source/SPUserManager.xcdatamodel/layout +++ b/Source/SPUserManager.xcdatamodel/layout |