From 80f757d7f217ae836f6c8de60134bc2a5d005687 Mon Sep 17 00:00:00 2001 From: mltownsend Date: Tue, 11 Aug 2009 17:00:46 +0000 Subject: User Manager feature --- Source/SPUserManager.m | 680 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 680 insertions(+) create mode 100644 Source/SPUserManager.m (limited to 'Source/SPUserManager.m') diff --git a/Source/SPUserManager.m b/Source/SPUserManager.m new file mode 100644 index 00000000..9ef3e8ba --- /dev/null +++ b/Source/SPUserManager.m @@ -0,0 +1,680 @@ +// +// $Id: SPUserManager.m 856 2009-06-12 05:31:39Z mltownsend $ +// +// SPUserManager.m +// sequel-pro +// +// Created by Mark Townsend on Jan 01, 2009 +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import "SPUserManager.h" +#import "MCPConnection.h" +#import "SPUserMO.h" +#import "MCPResult.h" +#import "ImageAndTextCell.h" +#import "SPArrayAdditions.h" +#import "SPStringAdditions.h" + +#define COLUMNIDNAME @"NameColumn" + +@interface SPUserManager (PrivateMethods) +- (void)_initializeTree:(NSArray *)items; +- (void)_initializeUsers; +- (void)_selectParentFromSelection; +- (NSArray *)_fetchUserWithUserName:(NSString *)username; +- (NSManagedObject *)_createNewSPUser; +- (BOOL)checkAndDisplayMySqlError; +- (void)_clearData; +@end + +@implementation SPUserManager + +- (id)init +{ + [self dealloc]; + @throw [NSException exceptionWithName:@"BadInitCall" reason:@"Can't call init here" userInfo:nil]; + return nil; +} + +- (id)initWithConnection:(MCPConnection*) connection +{ + if (![super init]) { + return nil; + } + + [self setConnection:connection]; + + + privColumnsMODict = [[[NSDictionary alloc] initWithObjectsAndKeys: + @"grant_option_priv",@"Grant_priv", + @"show_databases_priv",@"Show_db_priv", + @"create_temporary_table_priv",@"Create_tmp_table_priv", + @"Replication_slave_priv",@"Repl_slave_priv", + @"Replication_client_priv",@"Repl_client_priv",nil] retain]; + + if (!outlineView) { + [NSBundle loadNibNamed:@"UserManagerView" owner:self]; + } + + return self; +} + +- (void)dealloc +{ + NSLog(@"SPUserManager dealloc."); + + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [managedObjectContext release], managedObjectContext = nil; + [persistentStoreCoordinator release], persistentStoreCoordinator = nil; + [managedObjectModel release], managedObjectModel = nil; + [privColumnsMODict release], privColumnsMODict = nil; + + [mySqlConnection release]; + [super dealloc]; +} + +- (void)awakeFromNib +{ + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(contextDidSave:) + name:NSManagedObjectContextDidSaveNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(contextDidChange:) + name:NSManagedObjectContextObjectsDidChangeNotification + object:nil]; + [tabView selectTabViewItemAtIndex:0]; + + NSTableColumn *tableColumn = [outlineView tableColumnWithIdentifier:COLUMNIDNAME]; + ImageAndTextCell *imageAndTextCell = [[[ImageAndTextCell alloc] init] autorelease]; + + [imageAndTextCell setEditable:NO]; + [tableColumn setDataCell:imageAndTextCell]; + + [NSThread detachNewThreadSelector:@selector(_initializeUsers) toTarget:self withObject:nil]; +} + +- (void)_initializeUsers +{ + isInitializing = TRUE; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSMutableArray *resultAsArray = [NSMutableArray array]; + NSMutableArray *usersResultArray = [NSMutableArray array]; + + [[self connection] selectDB:@"mysql"]; + MCPResult *result = [[[self connection] queryString:@"select * from user order by user"] retain]; + int rows = [result numOfRows]; + if (rows > 0) + { + // Go to the beginning + [result dataSeek:0]; + } + + for(int i = 0; i < rows; i++) + { + [resultAsArray addObject:[result fetchRowAsDictionary]]; + } + + [usersResultArray addObjectsFromArray:resultAsArray]; + + [self _initializeTree:usersResultArray]; + + [result release]; + [pool release]; + isInitializing = FALSE; +} + +- (void)_initializeTree:(NSArray *)items +{ + + for(int i = 0; i < [items count]; i++) + { + NSString *username = [[items objectAtIndex:i] objectForKey:@"User"]; + NSArray *array = [[self _fetchUserWithUserName:username] retain]; + NSDictionary *item = [items objectAtIndex:i]; + + if (array != nil && [array count] > 0) + { + // Add Children + NSManagedObject *parent = [array objectAtIndex:0]; + NSManagedObject *child = [self _createNewSPUser]; + [child setParent:parent]; + [parent addChildrenObject:child]; + + [self initializeChild:child withItem:item]; + + } else { + // Add Parent + NSManagedObject *parent = [self _createNewSPUser]; + NSManagedObject *child = [self _createNewSPUser]; + + [parent setValue:username forKey:@"user"]; + [parent setValue:[item objectForKey:@"Password"] forKey:@"password"]; + [parent addChildrenObject:child]; + [child setParent:parent]; + + [self initializeChild:child withItem:item]; + } + // Save the initialized objects so that any new changes will be tracked. + NSError *error = nil; + [[self managedObjectContext] save:&error]; + if (error != nil) + { + [[NSApplication sharedApplication] presentError:error]; + } + [array release]; + } +} + +- (void)initializeChild:(NSManagedObject *)child withItem:(NSDictionary *)item +{ + for (NSString *key in item) + { + NS_DURING + if ([key hasSuffix:@"_priv"]) + { + // Special case keys + if ([privColumnsMODict objectForKey:key] != nil) + { + key = [privColumnsMODict objectForKey:key]; + } + + BOOL value = [[item objectForKey:key] boolValue]; + [child setValue:[NSNumber numberWithBool:value] forKey:key]; + } + else if ([key hasPrefix:@"max"]) + { + NSNumber *value = [NSNumber numberWithInt:[[item objectForKey:key] intValue]]; + [child setValue:value forKey:key]; + } + else if (![key isEqualToString:@"User"] && ![key isEqualToString:@"Password"]) + { + NSString *value = [item objectForKey:key]; + [child setValue:value forKey:key]; + } + NS_HANDLER + NSLog(@"%@ not implemented yet.", key); + NS_ENDHANDLER + } + +} + +/** + Creates, retains, and returns the managed object model for the application + by merging all of the models found in the application bundle. + */ + +- (NSManagedObjectModel *)managedObjectModel { + + if (managedObjectModel != nil) { + return managedObjectModel; + } + + managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain]; + return managedObjectModel; +} + +/** + Returns the persistent store coordinator for the application. This + implementation will create and return a coordinator, having added the + store for the application to it. (The folder for the store is created, + if necessary.) + */ + +- (NSPersistentStoreCoordinator *) persistentStoreCoordinator { + + if (persistentStoreCoordinator != nil) { + return persistentStoreCoordinator; + } + + NSError *error; + + persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]]; + if (![persistentStoreCoordinator addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:&error]) + { + [[NSApplication sharedApplication] presentError:error]; + } + + return persistentStoreCoordinator; +} + +/** + Returns the managed object context for the application (which is already + bound to the persistent store coordinator for the application.) + */ + +- (NSManagedObjectContext *) managedObjectContext { + + if (managedObjectContext != nil) { + return managedObjectContext; + } + + NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; + if (coordinator != nil) { + managedObjectContext = [[NSManagedObjectContext alloc] init]; + [managedObjectContext setPersistentStoreCoordinator: coordinator]; + } + + return managedObjectContext; +} + +- (void)setConnection:(MCPConnection *)connection +{ + [connection retain]; + [mySqlConnection release]; + mySqlConnection = connection; +} + +- (MCPConnection* )connection +{ + return mySqlConnection; +} + +- (void)show +{ +// [NSThread detachNewThreadSelector:@selector(_initializeUsers) toTarget:self withObject:nil]; + [window makeKeyAndOrderFront:nil]; +} + +#pragma mark - +#pragma mark OutlineView Delegate Methods +- (void)outlineView:(NSOutlineView *)olv willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + if ([cell isKindOfClass:[ImageAndTextCell class]]) + { + if ([(NSManagedObject *)[item representedObject] parent] != nil) + { + NSImage *image1 = [[NSImage imageNamed:NSImageNameNetwork] retain]; + [image1 setScalesWhenResized:YES]; + [image1 setSize:(NSSize){16,16}]; + [(ImageAndTextCell*)cell setImage:image1]; + [image1 release]; + + } + else + { + NSImage *image1 = [[NSImage imageNamed:NSImageNameUser] retain]; + [image1 setScalesWhenResized:YES]; + [image1 setSize:(NSSize){16,16}]; + [(ImageAndTextCell*)cell setImage:image1]; + [image1 release]; + } + } +} + +- (BOOL)outlineView:(NSOutlineView *)olv isGroupItem:(id)item +{ + return FALSE; +} + +- (BOOL)outlineView:(NSOutlineView *)olv shouldSelectItem:(id)item +{ + return TRUE; +} + +- (BOOL)outlineView:(NSOutlineView *)olv shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + if ([[[item representedObject] children] count] == 0) + { + return TRUE; + } + return FALSE; + +} + +- (void)outlineViewSelectionDidChange:(NSNotification *)notification +{ + id selectedObject = [[treeController selectedObjects] objectAtIndex:0]; + if ([selectedObject parent] == nil && !([[[tabView selectedTabViewItem] identifier] isEqualToString:@"General"])) + { + [tabView selectTabViewItemWithIdentifier:@"General"]; + } + else + { + if ([[[tabView selectedTabViewItem] identifier] isEqualToString:@"General"]) + { + [tabView selectTabViewItemWithIdentifier:@"Global Privileges"]; + } + } +} + +// General Action Methods +- (IBAction)doCancel:(id)sender +{ + [[self managedObjectContext] rollback]; + [window close]; +} + +- (IBAction)doApply:(id)sender +{ + NSError *error = nil; + [[self managedObjectContext] save:&error]; + if (error != nil) + { + [[NSApplication sharedApplication] presentError:error]; + } + else + { + [window close]; + } +// [self _clearData]; +} + +- (IBAction)addUser:(id)sender +{ + if ([[treeController selectedObjects] count] > 0) + { + if ([[[treeController selectedObjects] objectAtIndex:0] parent] != nil) + { + [self _selectParentFromSelection]; + } + } + NSManagedObject *newItem = [self _createNewSPUser]; + NSManagedObject *newChild = [self _createNewSPUser]; + [newChild setValue:@"localhost" forKey:@"host"]; + [newItem addChildrenObject:newChild]; + + [treeController addObject:newItem]; + [outlineView expandItem:[outlineView itemAtRow:[outlineView selectedRow]]]; +} + +- (IBAction)removeUser:(id)sender +{ + [treeController remove:sender]; +} + +- (IBAction)addHost:(id)sender +{ + if ([[treeController selectedObjects] count] > 0) + { + if ([[[treeController selectedObjects] objectAtIndex:0] parent] != nil) + { + [self _selectParentFromSelection]; + } + } + [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. +// [outlineView editColumn:0 row:[outlineView selectedRow] withEvent:nil select:TRUE]; +} + +- (IBAction)removeHost:(id)sender +{ + [treeController remove:sender]; +} + + +- (void)_clearData +{ + [managedObjectContext reset]; + [managedObjectContext release]; + managedObjectContext = nil; +} + +- (BOOL)validateMenuItem:(NSMenuItem *)menuItem +{ + if ([menuItem action] == @selector(addHost:) || + [menuItem action] == @selector(removeHost:)) + { + return (([[treeController selectedObjects] count] > 0) && + [[[treeController selectedObjects] objectAtIndex:0] parent] != nil); + } + return TRUE; +} + +- (void)_selectParentFromSelection +{ + if ([[treeController selectedObjects] count] > 0) + { + NSTreeNode *firstSelectedNode = [[treeController selectedNodes] objectAtIndex:0]; + NSTreeNode *parentNode = [firstSelectedNode parentNode]; + if (parentNode) + { + NSIndexPath *parentIndex = [parentNode indexPath]; + [treeController setSelectionIndexPath:parentIndex]; + } + else + { + NSArray *selectedIndexPaths = [treeController selectionIndexPaths]; + [treeController removeSelectionIndexPaths:selectedIndexPaths]; + } + } +} + +#pragma mark - +#pragma mark Notifications +- (void)contextDidSave:(NSNotification *)notification +{ + if (!isInitializing) + { + NSArray *updated = [[notification userInfo] valueForKey:NSUpdatedObjectsKey]; + NSArray *inserted = [[notification userInfo] valueForKey:NSInsertedObjectsKey]; + NSArray *deleted = [[notification userInfo] valueForKey:NSDeletedObjectsKey]; + + if ([inserted count] > 0) + { + [self insertUsers:inserted]; + } + + if ([updated count] > 0) + { + [self updateUsers:updated]; + } + + if ([deleted count] > 0) + { + [self deleteUsers:deleted]; + } + } +} + +- (void)contextDidChange:(NSNotification *)notification +{ + NSLog(@"contextDidChange:"); + + if (!isInitializing) + { + [outlineView reloadData]; + } +} + +- (BOOL)updateUsers:(NSArray *)updatedUsers +{ + for (NSManagedObject *user in updatedUsers) { + [self grantPrivilegesToUser:user]; + } + return TRUE; +} + +- (BOOL)deleteUsers:(NSArray *)deletedUsers +{ + [[self connection] selectDB:@"mysql"]; + NSMutableString *droppedUsers = [NSMutableString string]; + for (NSManagedObject *user in deletedUsers) + { + if ([user host] != nil) + { + [droppedUsers appendString:[NSString stringWithFormat:@"%@@%@, ", + [[user valueForKey:@"user"] backtickQuotedString], + [[user valueForKey:@"host"] backtickQuotedString]]]; + } + + } + droppedUsers = [[droppedUsers substringToIndex:[droppedUsers length]-2] mutableCopy]; + [[self connection] queryString:[NSString stringWithFormat:@"DROP USER %@", droppedUsers]]; + + return TRUE; +} + +- (BOOL)insertUsers:(NSArray *)insertedUsers +{ + [[self connection] selectDB:@"mysql"]; + for(NSManagedObject *user in insertedUsers) + { + NSString *createStatement = [NSString stringWithFormat:@"CREATE USER %@@%@ IDENTIFIED BY %@;", + [[[user parent] valueForKey:@"user"] tickQuotedString], + [[user valueForKey:@"host"] tickQuotedString], + [[[user parent] valueForKey:@"password"] tickQuotedString]]; + // Create user in database + [[self connection] queryString:[NSString stringWithFormat:createStatement]]; + + if ([self checkAndDisplayMySqlError]) + { + [self grantPrivilegesToUser:user]; + } + } + + return TRUE; + +} + +// Grant or Revoke privileges to the given user +- (BOOL)grantPrivilegesToUser:(NSManagedObject *)user +{ + if ([user valueForKey:@"parent"] != nil) + { + NSDictionary *attributesDict = [[user entity] attributesByName]; + NSMutableArray *grantPrivileges = [NSMutableArray array]; + NSMutableArray *revokePrivileges = [NSMutableArray array]; + + for(NSString *key in [attributesDict allKeys]) + { + if ([key hasSuffix:@"_priv"]) + { + NSString *privilege = [key stringByReplacingOccurrencesOfString:@"_priv" withString:@""]; + + if ([[user valueForKey:key] boolValue] == TRUE) + { + [grantPrivileges addObject:[NSString stringWithFormat:@"%@", [privilege replaceUnderscoreWithSpace]]]; + } + else + { + [revokePrivileges addObject:[NSString stringWithFormat:@"%@", [privilege replaceUnderscoreWithSpace]]]; + } + } + } + // Grant privileges + if ([grantPrivileges count] > 0) + { + NSString *grantStatement = [NSString stringWithFormat:@"GRANT %@ ON *.* TO %@@%@;", + [grantPrivileges componentsJoinedByCommas], + [[[user parent] valueForKey:@"user"] tickQuotedString], + [[user valueForKey:@"host"] tickQuotedString]]; + NSLog(@"%@", grantStatement); + [[self connection] queryString:[NSString stringWithFormat:grantStatement]]; + [self checkAndDisplayMySqlError]; + } + + // Revoke privileges + if ([revokePrivileges count] > 0) + { + NSString *revokeStatement = [NSString stringWithFormat:@"REVOKE %@ ON *.* TO %@@%@;", + [revokePrivileges componentsJoinedByCommas], + [[[user parent] valueForKey:@"user"] tickQuotedString], + [[user valueForKey:@"host"] tickQuotedString]]; + NSLog(@"%@", revokeStatement); + [[self connection] queryString:[NSString stringWithFormat:revokeStatement]]; + [self checkAndDisplayMySqlError]; + } + } + return TRUE; +} +- (NSArray *)_fetchUserWithUserName:(NSString *)username +{ + NSManagedObjectContext *moc = [self managedObjectContext]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"user == %@ AND parent == nil", username]; + NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"SPUser" + inManagedObjectContext:moc]; + NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease]; + + [request setEntity:entityDescription]; + [request setPredicate:predicate]; + + NSError *error = nil; + NSArray *array = [moc executeFetchRequest:request error:&error]; + if (error != nil) + { + [[NSApplication sharedApplication] presentError:error]; + } + + return array; +} + +- (NSManagedObject *)_createNewSPUser +{ + NSManagedObject *user = [[NSEntityDescription insertNewObjectForEntityForName:@"SPUser" + inManagedObjectContext:[self managedObjectContext]] autorelease]; + + return user; +} + +- (BOOL)checkAndDisplayMySqlError +{ + if (![[[self connection] getLastErrorMessage] isEqualToString:@""]) + { + NSBeginAlertSheet(@"MySQL Error", + nil, + nil, + nil, + window, + self, + NULL, + NULL, + nil, + [[self connection] getLastErrorMessage]); + return FALSE; + } else { + return TRUE; + } +} + +#pragma mark - +#pragma mark Tab View Delegate methods +- (BOOL)tabView:(NSTabView *)tabView shouldSelectTabViewItem:(NSTabViewItem *)tabViewItem +{ + if ([[treeController selectedObjects] count] == 0) + return FALSE; + + id selectedObject = [[treeController selectedObjects] objectAtIndex:0]; + if ([[tabViewItem identifier] isEqualToString:@"General"]) + { + if ([selectedObject parent] == nil) { + return TRUE; + } else { + return FALSE; + } + } + else if ([[tabViewItem identifier] isEqualToString:@"Global Privileges"] || + [[tabViewItem identifier] isEqualToString:@"Resources"]) + { + if ([selectedObject parent] != nil) + { + return TRUE; + } + else + { + return FALSE; + } + } + + return TRUE; +} +@end -- cgit v1.2.3