aboutsummaryrefslogtreecommitdiffstats
path: root/Source/SPUserManager.m
diff options
context:
space:
mode:
authormltownsend <mltownsend@gmail.com>2009-08-11 17:00:46 +0000
committermltownsend <mltownsend@gmail.com>2009-08-11 17:00:46 +0000
commit80f757d7f217ae836f6c8de60134bc2a5d005687 (patch)
tree6969e36a3bd1f63227d67d2f2bdb224c36b1aeed /Source/SPUserManager.m
parentf6afd1e9699b891f1deee9058a1737e8c5744bd7 (diff)
downloadsequelpro-80f757d7f217ae836f6c8de60134bc2a5d005687.tar.gz
sequelpro-80f757d7f217ae836f6c8de60134bc2a5d005687.tar.bz2
sequelpro-80f757d7f217ae836f6c8de60134bc2a5d005687.zip
User Manager feature
Diffstat (limited to 'Source/SPUserManager.m')
-rw-r--r--Source/SPUserManager.m680
1 files changed, 680 insertions, 0 deletions
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 <http://code.google.com/p/sequel-pro/>
+
+#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