diff --git a/ChangeLog b/ChangeLog index b16794b59..229527c5e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,47 @@ +2010-07-15 Wolfgang Sourdeau + + * SoObjects/SOGo/SOGoUser.m (-personalCalendarFolderInContext:): + we now make use of -[SOGoParentFolder + lookupPersonalFolder:ignoringRights:] with @"personal" and YES as + arguments. + + * SoObjects/Appointments/SOGoAppointmentFolder.m + (-lookupCalendarFolderForUID:): removed useless method, which can + be replaced with -[SOGoUser personalCalendarFolderInContext:]. + + * UI/Scheduler/UIxCalListingActions.m + (_aptFolder:withClientObject:): removed unused method. + + * UI/WebServerResources/UIxAppointmentEditor.js + (onComposeToAllAttendees): take the status image DIV into account + when detecting the user fullnames. + (onComposeToUndecidedAttendees): same as above. + + * Tests/Integration/test-davacl.py + (DAVCalendarPublicAclTest.testCollectionAccessNormalUser): print + the amount of received hrefs. + + * SoObjects/Appointments/SOGoAppointmentFolders.m + (-folderObjectKeys): we now check the "AccessObject" + right on the returned folders to determine whether their ICS or + XML version should be accessible. + + * SoObjects/SOGo/SOGoParentFolder.m + (_fetchPersonalFolders:withChannel:): we no longer check access + rights from here as this method is too low level and prevent other + mechanisms from working properly. + (-lookupPersonalFolder:ignoringRights:): new method enabling the + lookup of a user's personal folders only and offering the choice + of respecting (or not) the active user's permission before + returning it. + (-lookupName:inContext:acquire:): we now make use of the above + method when looking up personal folders ("personal" or not). + (-toManyRelationShipKeys): same as lookupName... above. + + * SoObjects/SOGo/SOGoObject.m (-ignoreRights): new utility method + that determines whether the current object must check access + rights on subobjects. + 2010-07-14 Wolfgang Sourdeau * SoObjects/SOGo/SOGoGCSFolder.m (-aclSQLListingFilter): return an diff --git a/Scripts/sql-update-1.2.2_to_1.3.0-mysql.sh b/Scripts/sql-update-1.2.2_to_1.3.0-mysql.sh new file mode 100755 index 000000000..4dbecf54a --- /dev/null +++ b/Scripts/sql-update-1.2.2_to_1.3.0-mysql.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# this script only works with MySQL + +defaultusername=$USER +defaulthostname=localhost +defaultdatabase=$USER +indextable=sogo_folder_info + +read -p "Username ($defaultusername): " username +read -p "Hostname ($defaulthostname): " hostname +read -p "Database ($defaultdatabase): " database + +if [ -z "$username" ] +then + username=$defaultusername +fi + +if [ -z "$hostname" ] +then + hostname=$defaulthostname +fi + +if [ -z "$database" ] +then + database=$defaultdatabase + fi + + sqlscript="" + +function addField() { + oldIFS="$IFS" + IFS=" " + part="`echo -e \"ALTER TABLE $table ADD COLUMN c_category VARCHAR(255);\\n\"`"; + sqlscript="$sqlscript$part" + IFS="$oldIFS" +} + +tables=`mysql -p -N -B -u $username -h $hostname $database -e "select SUBSTRING_INDEX(c_quick_location, '/', -1) from $indextable where c_folder_type = 'Appointment';"` + +for table in $tables; +do + addField +done +echo "$sqlscript" | mysql -p -s -u $username -h $hostname $database > /dev/null diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.h b/SoObjects/Appointments/SOGoAppointmentFolder.h index 7e88ebfb5..7c1e70f3b 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.h +++ b/SoObjects/Appointments/SOGoAppointmentFolder.h @@ -123,7 +123,6 @@ typedef enum { - (id) lookupHomeFolderForUID: (NSString *) _uid inContext: (id) _ctx; -- (SOGoAppointmentFolder *) lookupCalendarFolderForUID: (NSString *) uid; - (NSArray *) lookupCalendarFoldersForUID: (NSString *) theUID; - (NSArray *) lookupCalendarFoldersForUIDs: (NSArray *) _uids inContext: (id) _ctx; diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index ee1a9d3af..70a8e3aaf 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -2644,36 +2644,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir return result; } -// -// This method returns the personal calendar of a specific user. -// -- (SOGoAppointmentFolder *) lookupCalendarFolderForUID: (NSString *) uid -{ - SOGoFolder *currentContainer; - SOGoAppointmentFolders *parent; - NSException *error; - - currentContainer = [[container container] container]; - currentContainer = [currentContainer lookupName: uid - inContext: context - acquire: NO]; - parent = [currentContainer lookupName: @"Calendar" inContext: context - acquire: NO]; - currentContainer = [parent lookupName: @"personal" inContext: context - acquire: NO]; - if (!currentContainer) - { - error = [parent newFolderWithName: [parent defaultFolderName] - andNameInContainer: @"personal"]; - if (!error) - currentContainer = [parent lookupName: @"personal" - inContext: context - acquire: NO]; - } - - return (SOGoAppointmentFolder *) currentContainer; -} - // // This method returns an array containing all the calendar folders // of a specific user, excluding her/his subscriptions. @@ -2712,6 +2682,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir NSMutableArray *folders; NSEnumerator *e; NSString *uid, *ownerLogin; + SOGoUser *user; id folder; ownerLogin = [self ownerInContext: context]; @@ -2725,7 +2696,8 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir folder = self; else { - folder = [self lookupCalendarFolderForUID: uid]; + user = [SOGoUser userWithLogin: uid]; + folder = [user personalCalendarFolderInContext: context]; if (![folder isNotNull]) [self logWithFormat:@"Note: did not find folder for uid: '%@'", uid]; } diff --git a/SoObjects/Appointments/SOGoAppointmentFolders.m b/SoObjects/Appointments/SOGoAppointmentFolders.m index 13a7b5b8f..7e861f424 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolders.m +++ b/SoObjects/Appointments/SOGoAppointmentFolders.m @@ -29,6 +29,7 @@ #import #import #import +#import #import #import @@ -57,14 +58,48 @@ @interface SOGoParentFolder (Private) -- (NSException *) initSubscribedSubFolders; - (NSException *) _fetchPersonalFolders: (NSString *) sql withChannel: (EOAdaptorChannel *) fc; @end +static SoSecurityManager *sm = nil; + @implementation SOGoAppointmentFolders ++ (void) initialize +{ + if (!sm) + sm = [SoSecurityManager sharedSecurityManager]; +} + ++ (SOGoWebDAVAclManager *) webdavAclManager +{ + static SOGoWebDAVAclManager *aclManager = nil; + + if (!aclManager) + { + aclManager = [[super webdavAclManager] copy]; + [aclManager + registerDAVPermission: davElement (@"write", XMLNS_WEBDAV) + abstract: NO + withEquivalent: SoPerm_AddDocumentsImagesAndFiles + asChildOf: davElement (@"all", XMLNS_WEBDAV)]; + [aclManager + registerDAVPermission: davElement (@"write-properties", XMLNS_WEBDAV) + abstract: YES + withEquivalent: SoPerm_AddDocumentsImagesAndFiles + asChildOf: davElement (@"write", XMLNS_WEBDAV)]; + [aclManager + registerDAVPermission: davElement (@"write-content", XMLNS_WEBDAV) + abstract: YES + withEquivalent: SoPerm_AddDocumentsImagesAndFiles + asChildOf: davElement (@"write", XMLNS_WEBDAV)]; + } + + return aclManager; +} + - (id) init { if ((self = [super init])) @@ -166,9 +201,11 @@ SOGoAppointmentFolder *folder; NSString *folderObjectKey; int count, max; + BOOL ignoreRights; if (!folderObjectKeys) { + ignoreRights = [self ignoreRights]; folders = [self subFolders]; max = [folders count]; folderObjectKeys = [[NSMutableArray alloc] initWithCapacity: max]; @@ -176,7 +213,10 @@ { folder = [folders objectAtIndex: count]; if ([folder isMemberOfClass: [SOGoAppointmentFolder class]] - && ![folder isSubscription]) + && ![folder isSubscription] + && (ignoreRights || ![sm validatePermission: SOGoPerm_AccessObject + onObject: folder + inContext: context])) { folderObjectKey = [NSString stringWithFormat: @"%@.ics", [folder nameInContainer]]; @@ -475,33 +515,6 @@ return error; } -+ (SOGoWebDAVAclManager *) webdavAclManager -{ - static SOGoWebDAVAclManager *aclManager = nil; - - if (!aclManager) - { - aclManager = [[super webdavAclManager] copy]; - [aclManager - registerDAVPermission: davElement (@"write", XMLNS_WEBDAV) - abstract: NO - withEquivalent: SoPerm_AddDocumentsImagesAndFiles - asChildOf: davElement (@"all", XMLNS_WEBDAV)]; - [aclManager - registerDAVPermission: davElement (@"write-properties", XMLNS_WEBDAV) - abstract: YES - withEquivalent: SoPerm_AddDocumentsImagesAndFiles - asChildOf: davElement (@"write", XMLNS_WEBDAV)]; - [aclManager - registerDAVPermission: davElement (@"write-content", XMLNS_WEBDAV) - abstract: YES - withEquivalent: SoPerm_AddDocumentsImagesAndFiles - asChildOf: davElement (@"write", XMLNS_WEBDAV)]; - } - - return aclManager; -} - - (BOOL) hasProxyCalendarsWithWriteAccess: (BOOL) write forUserWithLogin: (NSString *) userLogin { diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 896c23d2e..5e43eafc7 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -163,7 +163,8 @@ if (!object) { // Create the event in the user's personal calendar. - folder = [container lookupCalendarFolderForUID: uid]; + folder = [[SOGoUser userWithLogin: uid] + personalCalendarFolderInContext: context]; object = [SOGoAppointmentObject objectWithName: nameInContainer inContainer: folder]; [object setIsNew: YES]; @@ -265,7 +266,8 @@ // Invitations are always written to the personal folder; it's not necessay // to look into all folders of the user - folder = [container lookupCalendarFolderForUID: theUID]; + folder = [[SOGoUser userWithLogin: theUID] + personalCalendarFolderInContext: context]; object = [folder lookupName: nameInContainer inContext: context acquire: NO]; if (![object isKindOfClass: [NSException class]]) diff --git a/SoObjects/SOGo/SOGoGCSFolder.m b/SoObjects/SOGo/SOGoGCSFolder.m index 74639d389..3f8388008 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.m +++ b/SoObjects/SOGo/SOGoGCSFolder.m @@ -739,9 +739,7 @@ static NSArray *childRecordFields = nil; NSDictionary *record; WORequest *request; - obj = [super lookupName: key - inContext: localContext - acquire: acquire]; + obj = [super lookupName: key inContext: localContext acquire: acquire]; if (!obj) { record = [childRecords objectForKey: key]; diff --git a/SoObjects/SOGo/SOGoObject.h b/SoObjects/SOGo/SOGoObject.h index 2daaa8abb..44e30bfa4 100644 --- a/SoObjects/SOGo/SOGoObject.h +++ b/SoObjects/SOGo/SOGoObject.h @@ -98,6 +98,10 @@ - (void) setOwner: (NSString *) newOwner; - (NSString *) ownerInContext: (id) _ctx; +/* a helper that determines whether access rights can be ignored on + an object */ +- (BOOL) ignoreRights; + /* looking up shared objects */ - (SOGoUserFolder *) lookupUserFolder; diff --git a/SoObjects/SOGo/SOGoObject.m b/SoObjects/SOGo/SOGoObject.m index da6547c84..f48aadd32 100644 --- a/SoObjects/SOGo/SOGoObject.m +++ b/SoObjects/SOGo/SOGoObject.m @@ -229,6 +229,25 @@ return owner; } +- (BOOL) ignoreRights +{ + SOGoUser *currentUser; + NSString *login; + BOOL ignoreRights; + + if (activeUserIsOwner) + ignoreRights = YES; + else + { + currentUser = [context activeUser]; + login = [currentUser login]; + ignoreRights = ([login isEqualToString: [self ownerInContext: context]] + || [currentUser isSuperUser]); + } + + return ignoreRights; +} + - (BOOL) isInPublicZone { if (!isInPublicZone) diff --git a/SoObjects/SOGo/SOGoParentFolder.h b/SoObjects/SOGo/SOGoParentFolder.h index bd5bee7de..cfd272121 100644 --- a/SoObjects/SOGo/SOGoParentFolder.h +++ b/SoObjects/SOGo/SOGoParentFolder.h @@ -54,6 +54,9 @@ - (NSException *) newFolderWithName: (NSString *) name nameInContainer: (NSString **) newNameInContainer; +- (id) lookupPersonalFolder: (NSString *) name + ignoringRights: (BOOL) ignoreRights; + @end #endif /* SOGOPARENTFOLDERS_H */ diff --git a/SoObjects/SOGo/SOGoParentFolder.m b/SoObjects/SOGo/SOGoParentFolder.m index 2066ba3fe..52982439a 100644 --- a/SoObjects/SOGo/SOGoParentFolder.m +++ b/SoObjects/SOGo/SOGoParentFolder.m @@ -175,26 +175,19 @@ static SoSecurityManager *sm = nil; { NSArray *attrs; NSDictionary *row; - BOOL hasPersonal, ignoreRights; SOGoGCSFolder *folder; NSString *key, *login; NSException *error; SOGoUser *currentUser; - SoSecurityManager *securityManager; if (!subFolderClass) subFolderClass = [[self class] subFolderClass]; - hasPersonal = NO; error = [fc evaluateExpressionX: sql]; if (!error) { currentUser = [context activeUser]; login = [currentUser login]; - ignoreRights = (activeUserIsOwner || [login isEqualToString: owner] - || [currentUser isSuperUser]); - if (!ignoreRights) - securityManager = [SoSecurityManager sharedSecurityManager]; attrs = [fc describeResults: NO]; while ((row = [fc fetchAttributes: attrs withZone: NULL])) @@ -203,19 +196,13 @@ static SoSecurityManager *sm = nil; if ([key isKindOfClass: [NSString class]]) { folder = [subFolderClass objectWithName: key inContainer: self]; - hasPersonal = (hasPersonal - || [key isEqualToString: @"personal"]); [folder setOCSPath: [NSString stringWithFormat: @"%@/%@", OCSPath, key]]; - if (ignoreRights - || ![securityManager validatePermission: SOGoPerm_AccessObject - onObject: folder - inContext: context]) - [subFolders setObject: folder forKey: key]; + [subFolders setObject: folder forKey: key]; } } - if (ignoreRights && !hasPersonal) + if (![subFolders objectForKey: @"personal"]) [self _createPersonalFolder]; } @@ -405,16 +392,8 @@ static SoSecurityManager *sm = nil; obj = [super lookupName: name inContext: lookupContext acquire: NO]; if (!obj) { - // Lookup in personal folders - error = [self initSubFolders]; - if (error) - { - [self errorWithFormat: @"a database error occured: %@", [error reason]]; - obj = [NSException exceptionWithHTTPStatus: 503]; - } - else - obj = [subFolders objectForKey: name]; - + obj = [self lookupPersonalFolder: name + ignoringRights: NO]; if (!obj) { // Lookup in subscribed folders @@ -432,6 +411,31 @@ static SoSecurityManager *sm = nil; return obj; } +- (id) lookupPersonalFolder: (NSString *) name + ignoringRights: (BOOL) ignoreRights +{ + NSException *error; + id obj; + + error = [self initSubFolders]; + if (error) + { + [self errorWithFormat: @"a database error occured: %@", [error reason]]; + obj = [NSException exceptionWithHTTPStatus: 503]; + } + else + { + obj = [subFolders objectForKey: name]; + if (obj && !ignoreRights && ![self ignoreRights] + && [sm validatePermission: SOGoPerm_AccessObject + onObject: obj + inContext: context]) + obj = nil; + } + + return obj; +} + - (NSArray *) subFolders { NSMutableArray *ma; @@ -475,7 +479,7 @@ static SoSecurityManager *sm = nil; #warning check error here error = [self initSubFolders]; - + subs = [subFolders allValues]; count = [subs count]; for (i = 0; !rc && i < count; i++) @@ -492,11 +496,20 @@ static SoSecurityManager *sm = nil; NSEnumerator *sortedSubFolders; NSMutableArray *keys; SOGoGCSFolder *currentFolder; + BOOL ignoreRights; + + ignoreRights = [self ignoreRights]; keys = [NSMutableArray array]; sortedSubFolders = [[self subFolders] objectEnumerator]; while ((currentFolder = [sortedSubFolders nextObject])) - [keys addObject: [currentFolder nameInContainer]]; + { + if (ignoreRights + || ![sm validatePermission: SOGoPerm_AccessObject + onObject: currentFolder + inContext: context]) + [keys addObject: [currentFolder nameInContainer]]; + } return keys; } diff --git a/SoObjects/SOGo/SOGoUser.m b/SoObjects/SOGo/SOGoUser.m index 9d124b7c1..15b217c28 100644 --- a/SoObjects/SOGo/SOGoUser.m +++ b/SoObjects/SOGo/SOGoUser.m @@ -599,9 +599,8 @@ - (SOGoAppointmentFolder *) personalCalendarFolderInContext: (WOContext *) context { - return [[self calendarsFolderInContext: context] lookupName: @"personal" - inContext: context - acquire: NO]; + return [[self calendarsFolderInContext: context] lookupPersonalFolder: @"personal" + ignoringRights: YES]; } // - (id) schedulingCalendarInContext: (id) _ctx diff --git a/Tests/Integration/test-davacl.py b/Tests/Integration/test-davacl.py index 5455929d6..87e47a7d3 100755 --- a/Tests/Integration/test-davacl.py +++ b/Tests/Integration/test-davacl.py @@ -983,12 +983,12 @@ class DAVCalendarPublicAclTest(unittest.TestCase): self.subscriber_client.execute(propfind) hrefs = propfind.response["document"] \ .findall("{DAV:}response/{DAV:}href") + self.assertEquals(len(hrefs), 1, - "expected only one href in response") + "expected 1 href in response instead of %d" % len(hrefs)) self.assertEquals(hrefs[0].text, parentColl, "the href must be the 'Calendar' parent coll.") - acl_utility = utilities.TestCalendarACLUtility(self, self.client, self.createdRsrc) diff --git a/UI/Scheduler/UIxCalListingActions.m b/UI/Scheduler/UIxCalListingActions.m index dd6ab8986..3329bb799 100644 --- a/UI/Scheduler/UIxCalListingActions.m +++ b/UI/Scheduler/UIxCalListingActions.m @@ -234,25 +234,6 @@ static NSArray *tasksFields = nil; } } -- (SOGoAppointmentFolder *) _aptFolder: (NSString *) folder - withClientObject: (SOGoAppointmentFolder *) clientObject -{ - SOGoAppointmentFolder *aptFolder; - NSArray *folderParts; - - if ([folder isEqualToString: @"/"]) - aptFolder = clientObject; - else - { - folderParts = [folder componentsSeparatedByString: @":"]; - aptFolder - = [clientObject lookupCalendarFolderForUID: - [folderParts objectAtIndex: 0]]; - } - - return aptFolder; -} - - (void) _fixComponentTitle: (NSMutableDictionary *) component withType: (NSString *) type { diff --git a/UI/WebServerResources/UIxAppointmentEditor.js b/UI/WebServerResources/UIxAppointmentEditor.js index b1700d83b..fd9e6aacc 100644 --- a/UI/WebServerResources/UIxAppointmentEditor.js +++ b/UI/WebServerResources/UIxAppointmentEditor.js @@ -115,8 +115,15 @@ function onComposeToAllAttendees() var attendees = $$("DIV#attendeesMenu LI.attendee"); var addresses = new Array(); attendees.each(function(item) { - var address = item.firstChild.nodeValue.trim() + " <" + item.readAttribute("email") + ">"; - addresses.push(address); + var textChild = null; + var childNodes = item.childNodes; + for (var i = 0; !textChild && i < childNodes.length; i++) { + if (childNodes[i].nodeType == 3) { + textChild = childNodes[i]; + var address = textChild.nodeValue.trim() + " <" + item.readAttribute("email") + ">"; + addresses.push(address); + } + } }); if (window.opener) window.opener.openMailTo(addresses.join(",")); @@ -130,8 +137,15 @@ function onComposeToUndecidedAttendees() var attendees = $$("DIV#attendeesMenu LI.attendee.needs-action"); var addresses = new Array(); attendees.each(function(item) { - var address = item.firstChild.nodeValue.trim() + " <" + item.readAttribute("email") + ">"; - addresses.push(address); + var textChild = null; + var childNodes = item.childNodes; + for (var i = 0; !textChild && i < childNodes.length; i++) { + if (childNodes[i].nodeType == 3) { + textChild = childNodes[i]; + var address = textChild.nodeValue.trim() + " <" + item.readAttribute("email") + ">"; + addresses.push(address); + } + } }); if (window.opener) window.opener.openMailTo(addresses.join(",")); diff --git a/debian/sogo.docs b/debian/sogo.docs index 6641930b8..a272e2d3b 100644 --- a/debian/sogo.docs +++ b/debian/sogo.docs @@ -2,3 +2,4 @@ NEWS README TODO Scripts/sql-update-1.2.2_to_1.3.0.sh +Scripts/sql-update-1.2.2_to_1.3.0-mysql.sh diff --git a/sogo.spec b/sogo.spec index 4dc50837d..09c64e6b1 100644 --- a/sogo.spec +++ b/sogo.spec @@ -188,7 +188,7 @@ rm -fr ${RPM_BUILD_ROOT} %config %{_sysconfdir}/httpd/conf.d/SOGo.conf %config %{_sysconfdir}/sysconfig/sogo -%doc ChangeLog README NEWS Scripts/sql-update-20070724.sh Scripts/sql-update-20070822.sh Scripts/sql-update-20080303.sh Scripts/sql-update-101_to_102.sh Scripts/sql-update-1.2.2_to_1.3.0.sh +%doc ChangeLog README NEWS Scripts/sql-update-20070724.sh Scripts/sql-update-20070822.sh Scripts/sql-update-20080303.sh Scripts/sql-update-101_to_102.sh Scripts/sql-update-1.2.2_to_1.3.0.sh sql-update-1.2.2_to_1.3.0-mysql.sh %files -n sogo-tool %{prefix}/Tools/Admin/sogo-tool