From fa522147e69364e6a163bed1e36a5d07ed58a44c Mon Sep 17 00:00:00 2001 From: Ludovic Marcotte Date: Fri, 24 Sep 2010 15:24:24 +0000 Subject: [PATCH] See ChangeLog Monotone-Parent: a21045504db4729f0a32f67e4bba1f563ea16625 Monotone-Revision: e1912bf4962632854ed7898edea3ca2ba3a70406 Monotone-Author: ludovic@Sophos.ca Monotone-Date: 2010-09-24T15:24:24 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 4 ++ SoObjects/SOGo/SOGoCache.h | 9 ++++ SoObjects/SOGo/SOGoCache.m | 37 ++++++++++++---- SoObjects/SOGo/SOGoGCSFolder.m | 11 ++++- SoObjects/SOGo/SOGoGroup.h | 3 +- SoObjects/SOGo/SOGoGroup.m | 79 ++++++++++++++++++++++++++-------- SoObjects/SOGo/SQLSource.m | 2 +- 7 files changed, 114 insertions(+), 31 deletions(-) diff --git a/ChangeLog b/ChangeLog index d80566756..5c74638c9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2010-09-24 Ludovic Marcotte + + * Added caching support for LDAP-based groups + 2010-09-22 Francis Lachapelle * UI/WebServerResources/ContactsUI.js diff --git a/SoObjects/SOGo/SOGoCache.h b/SoObjects/SOGo/SOGoCache.h index f9b6cb744..73d49c8d2 100644 --- a/SoObjects/SOGo/SOGoCache.h +++ b/SoObjects/SOGo/SOGoCache.h @@ -32,6 +32,7 @@ @class NSString; @class NSUserDefaults; +@class SOGoGroup; @class SOGoObject; @class SOGoUser; @class SOGoUserDefaults; @@ -41,6 +42,7 @@ NSMutableDictionary *localCache; NSMutableDictionary *cache; NSMutableDictionary *users; + NSMutableDictionary *groups; float cleanupInterval; NSString *memcachedServerName; } @@ -59,6 +61,13 @@ withName: (NSString *) userName; - (id) userNamed: (NSString *) name; +- (void) registerGroup: (SOGoGroup *) group + withName: (NSString *) groupName + inDomain: (NSString *) domainName; + +- (id) groupNamed: (NSString *) groupName + inDomain: (NSString *) domainName; + /* NSDictionary-like methods */ - (void) setValue: (NSString *) value forKey: (NSString *) key; - (NSString *) valueForKey: (NSString *) key; diff --git a/SoObjects/SOGo/SOGoCache.m b/SoObjects/SOGo/SOGoCache.m index 9d6da8159..917bb7173 100644 --- a/SoObjects/SOGo/SOGoCache.m +++ b/SoObjects/SOGo/SOGoCache.m @@ -1,6 +1,6 @@ /* SOGoCache.m - this file is part of SOGo * - * Copyright (C) 2008-2009 Inverse inc. + * Copyright (C) 2008-2010 Inverse inc. * * Author: Wolfgang Sourdeau * Ludovic Marcotte @@ -24,10 +24,11 @@ /* * [ Cache Structure ] * - * users value = instances of SOGoUser > flushed after the completion of every SOGo requests - * +defaults value = NSDictionary instance > user's defaults - * +settings value = NSDictionary instance > user's settings - * +attributes value = NSMutableDictionary instance > user's LDAP attributes + * users value = instances of SOGoUser > flushed after the completion of every SOGo requests + * +defaults value = NSDictionary instance > user's defaults + * +settings value = NSDictionary instance > user's settings + * +attributes value = NSMutableDictionary instance > user's LDAP attributes + * + value = NSString instance (array components separated by ",") or group member logins for a specific group in domain */ #import @@ -78,6 +79,7 @@ static memcached_st *handle = NULL; // This is essential for refetching the cached values in case something has changed // accross various sogod processes [users removeAllObjects]; + [groups removeAllObjects]; [localCache removeAllObjects]; } @@ -89,8 +91,9 @@ static memcached_st *handle = NULL; { memcached_return error; - cache = [NSMutableDictionary new]; - users = [NSMutableDictionary new]; + cache = [[NSMutableDictionary alloc] init]; + users = [[NSMutableDictionary alloc] init]; + groups = [[NSMutableDictionary alloc] init]; // localCache is used to avoid going all the time to the memcached // server during each request. We'll cache the value we got from @@ -137,6 +140,7 @@ static memcached_st *handle = NULL; [memcachedServerName release]; [cache release]; [users release]; + [groups release]; [localCache release]; [super dealloc]; } @@ -197,8 +201,6 @@ static memcached_st *handle = NULL; withName: name]; return [cache objectForKey: fullPath]; - // if (object) - // NSLog (@"found cached object '%@'", fullPath); } - (void) registerUser: (SOGoUser *) user @@ -212,6 +214,23 @@ static memcached_st *handle = NULL; return [users objectForKey: name]; } +- (void) registerGroup: (SOGoGroup *) group + withName: (NSString *) groupName + inDomain: (NSString *) domainName + +{ + if (group) + [groups setObject: group forKey: [NSString stringWithFormat: @"%@+%@", groupName, domainName]]; +} + +- (id) groupNamed: (NSString *) groupName + inDomain: (NSString *) domainName + +{ + return [groups objectForKey: [NSString stringWithFormat: @"%@+%@", groupName, domainName]]; +} + + // // For non-blocking cache method, see memcached_behavior_set and MEMCACHED_BEHAVIOR_NO_BLOCK // memcached is thread-safe so no need to lock here. diff --git a/SoObjects/SOGo/SOGoGCSFolder.m b/SoObjects/SOGo/SOGoGCSFolder.m index 4343c9e46..fc1cf49b5 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.m +++ b/SoObjects/SOGo/SOGoGCSFolder.m @@ -1431,8 +1431,15 @@ static NSArray *childRecordFields = nil; currentUID = [record valueForKey: @"c_uid"]; if ([currentUID hasPrefix: @"@"]) { - group = [SOGoGroup groupWithIdentifier: currentUID - inDomain: domain]; + group = [[SOGoCache sharedCache] groupNamed: currentUID inDomain: domain]; + + if (!group) + { + group = [SOGoGroup groupWithIdentifier: currentUID + inDomain: domain]; + [[SOGoCache sharedCache] registerGroup: group withName: currentUID inDomain: domain]; + } + if (group && [group hasMemberWithUID: uid]) [acls addObject: [record valueForKey: @"c_role"]]; } diff --git a/SoObjects/SOGo/SOGoGroup.h b/SoObjects/SOGo/SOGoGroup.h index ba928712e..e28cc075f 100644 --- a/SoObjects/SOGo/SOGoGroup.h +++ b/SoObjects/SOGo/SOGoGroup.h @@ -1,6 +1,6 @@ /* SOGoGroup.h - this file is part of SOGo * - * Copyright (C) 2009 Inverse inc. + * Copyright (C) 2009-2010 Inverse inc. * * Author: Ludovic Marcotte * @@ -37,6 +37,7 @@ { @private NSString *_identifier; + NSString *_domain; NGLdapEntry *_entry; NSObject *_source; NSMutableArray *_members; diff --git a/SoObjects/SOGo/SOGoGroup.m b/SoObjects/SOGo/SOGoGroup.m index 7b03a127e..3f15cda4c 100644 --- a/SoObjects/SOGo/SOGoGroup.m +++ b/SoObjects/SOGo/SOGoGroup.m @@ -50,6 +50,7 @@ #import #import +#include "SOGoCache.h" #include "SOGoSource.h" #include "SOGoUserManager.h" #include "SOGoUser.h" @@ -61,6 +62,7 @@ @implementation SOGoGroup - (id) initWithIdentifier: (NSString *) theID + domain: (NSString *) theDomain source: (NSObject *) theSource entry: (NGLdapEntry *) theEntry { @@ -69,6 +71,7 @@ if (self) { ASSIGN(_identifier, theID); + ASSIGN(_domain, theDomain); ASSIGN(_source, theSource); ASSIGN(_entry, theEntry); _members = nil; @@ -80,6 +83,7 @@ - (void) dealloc { RELEASE(_identifier); + RELEASE(_domain); RELEASE(_source); RELEASE(_entry); RELEASE(_members); @@ -153,7 +157,6 @@ // We check to see if it's a group classes = [[entry attributeWithName: @"objectClass"] allStringValues]; - // NSLog(@"classes for %@ = %@", theValue, classes); // Found a group, let's return it. if ([classes containsObject: @"group"] || @@ -162,6 +165,7 @@ [classes containsObject: @"posixGroup"]) { o = [[self alloc] initWithIdentifier: theValue + domain: domain source: source entry: entry]; AUTORELEASE(o); @@ -177,7 +181,7 @@ // - (NSArray *) members { - NSMutableArray *dns, *uids; + NSMutableArray *dns, *uids, *logins; NSString *dn, *login; SOGoUser *user; NSArray *o; @@ -189,10 +193,9 @@ _members = [NSMutableArray new]; uids = [NSMutableArray array]; dns = [NSMutableArray array]; + logins = [NSMutableArray array]; - // We check if it's a static group - //NSLog(@"attributes = %@", [_entry attributes]); - + // We check if it's a static group // Fetch "members" - we get DNs o = [[_entry attributeWithName: @"member"] allStringValues]; if (o) [dns addObjectsFromArray: o]; @@ -207,8 +210,6 @@ c = [dns count] + [uids count]; - //NSLog(@"members count (static group): %d", c); - // We deal with a static group, let's add the members if (c) { @@ -219,22 +220,32 @@ { dn = [dns objectAtIndex: i]; login = [um getLoginForDN: [dn lowercaseString]]; - //NSLog(@"member = %@", login); user = [SOGoUser userWithLogin: login roles: nil]; if (user) - [_members addObject: user]; + { + [logins addObject: login]; + [_members addObject: user]; + } } // We add members for whom we have their associated login name for (i = 0; i < [uids count]; i++) { login = [uids objectAtIndex: i]; - //NSLog(@"member = %@", login); user = [SOGoUser userWithLogin: login roles: nil]; if (user) - [_members addObject: user]; + { + [logins addObject: login]; + [_members addObject: user]; + } } + + + // We are done fetching members, let's cache the members of the group + // (ie., their UIDs) in memcached to speed up -hasMemberWithUID. + [[SOGoCache sharedCache] setValue: [logins componentsJoinedByString: @","] + forKey: [NSString stringWithFormat: @"%@+%@", _identifier, _domain]]; } else { @@ -247,20 +258,52 @@ return _members; } +// +// +// - (BOOL) hasMemberWithUID: (NSString *) memberUID { - int count, max; - NSString *currentUID; + BOOL rc; rc = NO; - [self members]; - max = [_members count]; - for (count = 0; !rc && count < max; count++) + // If _members is initialized, we use it as it's very accurate. + // Otherwise, we fallback on memcached in order to avoid + // decomposing the group all the time just to see if a user + // is a member of it. + if (_members) { - currentUID = [[_members objectAtIndex: count] login]; - rc = [memberUID isEqualToString: currentUID]; + NSString *currentUID; + + int count, max; + max = [_members count]; + for (count = 0; !rc && count < max; count++) + { + currentUID = [[_members objectAtIndex: count] login]; + rc = [memberUID isEqualToString: currentUID]; + } + + } + else + { + NSString *key, *value;; + NSArray *a; + + key = [NSString stringWithFormat: @"%@+%@", _identifier, _domain]; + value = [[SOGoCache sharedCache] valueForKey: key]; + + + // If the value isn't in memcached, that probably means -members was never called. + // We call it only once here. + if (!value) + { + [self members]; + value = [[SOGoCache sharedCache] valueForKey: key]; + } + + a = [value componentsSeparatedByString: @","]; + rc = [a containsObject: memberUID]; } return rc; diff --git a/SoObjects/SOGo/SQLSource.m b/SoObjects/SOGo/SQLSource.m index 07333739d..7d7f5a9a3 100644 --- a/SoObjects/SOGo/SQLSource.m +++ b/SoObjects/SOGo/SQLSource.m @@ -52,7 +52,7 @@ * c_name - which can be identical to c_uid - will be used to uniquely identify entries) * c_password - password of the user, plain-text or md5 encoded for now * c_cn - the user's common name - * mail - hte user's mail address + * mail - the user's mail address * * Other columns can be defined - see LDAPSource.m for the complete list. *