merge of '13eac3b68ef15a1ef039875fa0d5b9c4c2662728'

and '4520bccbe080f6685e84dc4ecd3ae2c5d9741940'

Monotone-Parent: 13eac3b68ef15a1ef039875fa0d5b9c4c2662728
Monotone-Parent: 4520bccbe080f6685e84dc4ecd3ae2c5d9741940
Monotone-Revision: 1239295b979f0ffa4eaaaac3d20eda7873f1606d

Monotone-Author: flachapelle@inverse.ca
Monotone-Date: 2010-07-14T22:30:00
Monotone-Branch: ca.inverse.sogo
This commit is contained in:
Francis Lachapelle 2010-07-14 22:30:00 +00:00
commit 12ab7913f3
8 changed files with 300 additions and 24 deletions

View file

@ -1,3 +1,21 @@
2010-07-14 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* SoObjects/SOGo/SOGoGCSFolder.m (-aclSQLListingFilter): return an
empty string when the active user is a super user.
(-initializeQuickTablesAclsInContext:): set
"userCanAccessAllObjects" to YES also when the active user is a
super user.
* Tests/Integration/test-davacl.py
(DAVCalendarAclTest._testEventIsSecureVersion): accept a differing
SUMMARY since it will always change depending on the user's
language and is a pain to adapt.
(DAVCalendarPublicAclTest.testCollectionAccessNormalUser): added
code to accept and handle the XML and ICS calendar variants.
(DAVCalendarSuperUserAclTest.__init__): new test access class for
ensuring that super users have full, inconditionnal and complete
access to simple user's collections
2010-07-13 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Tests/Integration/test-put.py: new test module executing X puts

View file

@ -594,14 +594,20 @@ static NSArray *childRecordFields = nil;
[self _subscriberRenameTo: newName];
}
/* Returns an empty string to indicate that the filter is empty and nil when
the query should not even be performed. */
- (NSString *) aclSQLListingFilter
{
NSString *filter, *login;
NSArray *roles;
SOGoUser *activeUser;
login = [[context activeUser] login];
activeUser = [context activeUser];
login = [activeUser login];
if (activeUserIsOwner
|| [[self ownerInContext: nil] isEqualToString: login])
|| [[self ownerInContext: nil] isEqualToString: login]
|| ([activeUser respondsToSelector: @selector (isSuperUser)]
&& [activeUser isSuperUser]))
filter = @"";
else
{
@ -613,9 +619,6 @@ static NSArray *childRecordFields = nil;
filter = nil;
}
/* An empty string indicates that the filter is empty while a return value
of nil indicates that the query should not even be performed. */
return filter;
}
@ -1349,17 +1352,21 @@ static NSArray *childRecordFields = nil;
- (void) initializeQuickTablesAclsInContext: (WOContext *) localContext
{
NSString *login;
SOGoUser *activeUser;
activeUser = [localContext activeUser];
if (activeUserIsOwner)
userCanAccessAllObjects = activeUserIsOwner;
else
{
login = [[localContext activeUser] login];
login = [activeUser login];
/* we only grant "userCanAccessAllObjects" for role "ObjectEraser" and
not "ObjectCreator" because the latter doesn't imply we can read
properties from subobjects or even know their existence. */
userCanAccessAllObjects
= [[self ownerInContext: localContext] isEqualToString: login];
= ([[self ownerInContext: localContext] isEqualToString: login]
|| ([activeUser respondsToSelector: @selector (isSuperUser)]
&& [activeUser isSuperUser]));
}
}

View file

@ -22,6 +22,149 @@ import utilities
# originally
# - test "current-user-acl-set"
class DAVCalendarSuperUserAclTest(unittest.TestCase):
def __init__(self, arg):
self.client = webdavlib.WebDAVClient(hostname, port,
username, password)
self.resource = "/SOGo/dav/%s/Calendar/test-dav-superuser-acl/" % subscriber_username
self.filename = "suevent.ics"
self.url = "%s%s" % (self.resource, self.filename)
unittest.TestCase.__init__(self, arg)
def setUp(self):
delete = webdavlib.WebDAVDELETE(self.resource)
self.client.execute(delete)
mkcol = webdavlib.WebDAVMKCOL(self.resource)
self.client.execute(mkcol)
self.assertEquals(mkcol.response["status"], 201,
"preparation: failure creating collection"
"(code = %d)" % mkcol.response["status"])
def tearDown(self):
delete = webdavlib.WebDAVDELETE(self.resource)
self.client.execute(delete)
def _getEvent(self):
get = webdavlib.HTTPGET(self.url)
self.client.execute(get)
if get.response["status"] == 200:
event = get.response["body"]
else:
event = None
return event
def _calendarDataInMultistatus(self, query, response_tag = "{DAV:}response"):
event = None
# print "\n\n\n%s\n\n" % query.response["body"]
# print "\n\n"
response_nodes = query.response["document"].findall(response_tag)
for response_node in response_nodes:
href_node = response_node.find("{DAV:}href")
href = href_node.text
if href.endswith(self.filename):
propstat_node = response_node.find("{DAV:}propstat")
if propstat_node is not None:
status_node = propstat_node.find("{DAV:}status")
status = status_node.text
if status.endswith("200 OK"):
data_node = propstat_node.find("{DAV:}prop/{urn:ietf:params:xml:ns:caldav}calendar-data")
event = data_node.text
elif not (status.endswith("404 Resource Not Found")
or status.endswith("404 Not Found")):
self.fail("%s: unexpected status code: '%s'"
% (self.filename, status))
return event
def _propfindEvent(self):
propfind = webdavlib.WebDAVPROPFIND(self.resource,
["{urn:ietf:params:xml:ns:caldav}calendar-data"],
1)
self.client.execute(propfind)
if propfind.response["status"] != 404:
event = self._calendarDataInMultistatus(propfind)
return event
def _multigetEvent(self):
event = None
multiget = webdavlib.CalDAVCalendarMultiget(self.resource,
["{urn:ietf:params:xml:ns:caldav}calendar-data"],
[ self.url ])
self.client.execute(multiget)
if multiget.response["status"] != 404:
event = self._calendarDataInMultistatus(multiget)
return event
def _webdavSyncEvent(self):
event = None
sync_query = webdavlib.WebDAVSyncQuery(self.resource, None,
["{urn:ietf:params:xml:ns:caldav}calendar-data"])
self.client.execute(sync_query)
if sync_query.response["status"] != 404:
event = self._calendarDataInMultistatus(sync_query, "{DAV:}sync-response")
return event
def testSUAccess(self):
"""create, read, modify, delete for superuser"""
event = event_template % { "class": "PUBLIC",
"filename": self.filename,
"organizer_line": "",
"attendee_line": "" }
# 1. Create
put = webdavlib.HTTPPUT(self.url, event)
put.content_type = "text/calendar; charset=utf-8"
self.client.execute(put)
self.assertEquals(put.response["status"], 201,
"%s: event creation/modification:"
" expected status code '201' (received '%d')"
% (self.filename, put.response["status"]))
# 2. Read
readEvent = self._getEvent()
self.assertEquals(readEvent, event,
"GET: returned event does not match")
readEvent = self._propfindEvent()
self.assertEquals(readEvent, event,
"PROPFIND: returned event does not match")
readEvent = self._multigetEvent()
self.assertEquals(readEvent, event,
"MULTIGET: returned event does not match")
readEvent = self._webdavSyncEvent()
self.assertEquals(readEvent, event,
"WEBDAV-SYNC: returned event does not match")
# 3. Modify
for eventClass in [ "CONFIDENTIAL", "PRIVATE", "PUBLIC" ]:
event = event_template % { "class": eventClass,
"filename": self.filename,
"organizer_line": "",
"attendee_line": "" }
put = webdavlib.HTTPPUT(self.url, event)
put.content_type = "text/calendar; charset=utf-8"
self.client.execute(put)
self.assertEquals(put.response["status"], 204,
"%s: event modification failed"
" expected status code '204' (received '%d')"
% (self.filename, put.response["status"]))
# 4. Delete
delete = webdavlib.WebDAVDELETE(self.url)
self.client.execute(delete)
self.assertEquals(delete.response["status"], 204,
"%s: event deletion failed"
" expected status code '204' (received '%d')"
% (self.filename, put.response["status"]))
class DAVAclTest(unittest.TestCase):
resource = None
@ -484,7 +627,8 @@ class DAVCalendarAclTest(DAVAclTest):
for key in event_dict.keys():
self.assertTrue(expected_dict.has_key(key),
"key '%s' of secure event not expected" % key)
self.assertTrue(expected_dict[key] == event_dict[key],
self.assertTrue(expected_dict[key] == event_dict[key]
or key == "SUMMARY",
"value for key '%s' of secure does not match"
" (exp: '%s', obtained: '%s'"
% (key, expected_dict[key], event_dict[key] ))
@ -853,13 +997,22 @@ class DAVCalendarPublicAclTest(unittest.TestCase):
acl_utility.setupRights(subscriber_username, { "c": True })
self.subscriber_client.execute(propfind)
hrefs = propfind.response["document"] \
.findall("{DAV:}response/{DAV:}href")
self.assertEquals(len(hrefs), 2, "expected two hrefs in response")
hrefs = propfind.response["document"].findall("{DAV:}response/{DAV:}href")
self.assertEquals(len(hrefs), 4,
"expected 4 hrefs in response, got %d: %s"
% (len(hrefs), ", ".join([ x.text for x in hrefs ])))
self.assertEquals(hrefs[0].text, parentColl,
"the first href is not a 'Calendar' parent coll.")
self.assertEquals(hrefs[1].text, resource,
"the 2nd href is not the accessible coll.")
resourceHrefs = { resource: False,
"%s.xml" % resource[:-1]: False,
"%s.ics" % resource[:-1]: False }
for href in hrefs[1:]:
self.assertTrue(resourceHrefs.has_key(href.text),
"received unexpected href: %s" % href.text)
self.assertFalse(resourceHrefs[href.text],
"href was returned more than once: %s" % href.text)
resourceHrefs[href.text] = True
acl_utility.setupRights(subscriber_username, {})
@ -870,13 +1023,21 @@ class DAVCalendarPublicAclTest(unittest.TestCase):
self.subscriber_client.execute(propfind)
hrefs = propfind.response["document"] \
.findall("{DAV:}response/{DAV:}href")
self.assertEquals(len(hrefs), 2,
"expected two hrefs in response: %d received" \
% len(hrefs))
self.assertEquals(len(hrefs), 4,
"expected 4 hrefs in response, got %d: %s"
% (len(hrefs), ", ".join([ x.text for x in hrefs ])))
self.assertEquals(hrefs[0].text, parentColl,
"the first href is not a 'Calendar' parent coll.")
self.assertEquals(hrefs[1].text, resource,
"the 2nd href is not the accessible coll.")
resourceHrefs = { resource: False,
"%s.xml" % resource[:-1]: False,
"%s.ics" % resource[:-1]: False }
for href in hrefs[1:]:
self.assertTrue(resourceHrefs.has_key(href.text),
"received unexpected href: %s" % href.text)
self.assertFalse(resourceHrefs[href.text],
"href was returned more than once: %s" % href.text)
resourceHrefs[href.text] = True
anonParentColl = '/SOGo/dav/public/%s/Calendar/' % username
anon_propfind = webdavlib.WebDAVPROPFIND(anonParentColl,
@ -898,13 +1059,23 @@ class DAVCalendarPublicAclTest(unittest.TestCase):
self.anon_client.execute(anon_propfind)
hrefs = anon_propfind.response["document"] \
.findall("{DAV:}response/{DAV:}href")
self.assertEquals(len(hrefs), 2, "expected 2 hrefs in response")
self.assertEquals(len(hrefs), 4,
"expected 4 hrefs in response, got %d: %s"
% (len(hrefs), ", ".join([ x.text for x in hrefs ])))
self.assertEquals(hrefs[0].text, anonParentColl,
"the first href is not a 'Calendar' parent coll.")
anonResource = '%stest-dav-acl/' % anonParentColl
self.assertEquals(hrefs[1].text, anonResource,
"expected href '%s' instead of '%s'."\
% (anonResource, hrefs[1].text))
resourceHrefs = { anonResource: False,
"%s.xml" % anonResource[:-1]: False,
"%s.ics" % anonResource[:-1]: False }
for href in hrefs[1:]:
self.assertTrue(resourceHrefs.has_key(href.text),
"received unexpected href: %s" % href.text)
self.assertFalse(resourceHrefs[href.text],
"href was returned more than once: %s" % href.text)
resourceHrefs[href.text] = True
self.subscriber_client.execute(propfind)
hrefs = propfind.response["document"] \

80
Tests/Stress/webdavsync.py Executable file
View file

@ -0,0 +1,80 @@
#!/usr/bin/python
from config import hostname, port
import webdavlib
import random
import time
import threading
base=1127
userscount=100
password=""
batchcount=10
sleeptime=3
durationHeader="sogorequestduration"
#durationHeader="sogo-request-duration"
class StressIteration(threading.Thread):
def __init__(self, username):
threading.Thread.__init__(self)
self.username = username
self.time = 0.0
self.sogoTime = 0.0
def run(self):
client = webdavlib.WebDAVClient(hostname, port,
self.username, password)
resource = "/SOGo/dav/%s/Calendar/personal/" % self.username
startTime = time.time()
query = webdavlib.WebDAVSyncQuery(resource, None,
[ "getetag", "calendar-data" ])
client.execute(query)
if query.response["status"] != 207:
print "*** received unexpected code: %d (%s)" \
% (query.response["status"], resource)
endTime = time.time()
headers = query.response["headers"]
if headers.has_key(durationHeader):
self.sogoTime = float(headers[durationHeader])
self.time = endTime - startTime
# print "%f, %f" % (self.time, self.sogoTime)
class StressTest:
def __init__(self):
self.usernames = [ "invite%d" % (base + x)
for x in xrange(userscount) ]
self.random = random.Random()
def iteration(self):
usernames = self.random.sample(self.usernames, batchcount)
startTime = time.time()
threads = []
for username in usernames:
iteration = StressIteration(username)
iteration.start()
threads.append(iteration)
for thread in threads:
thread.join()
endTime = time.time()
programTime = endTime - startTime
requestsTime = 0.0
sogoTime = 0.0
for thread in threads:
requestsTime = requestsTime + thread.time
sogoTime = sogoTime + thread.sogoTime
print "Iteration time: %f, Total Requests Time: %f, Total SOGo Time: %f" \
% (programTime, requestsTime, sogoTime)
def start(self):
while True:
self.iteration()
time.sleep(sleeptime)
if __name__ == "__main__":
test = StressTest()
test.start()

2
debian/sogo.docs vendored
View file

@ -1,4 +1,4 @@
NEWS
README
TODO
Scripts/sql-update-1.2.2_to_1.2.3.sh
Scripts/sql-update-1.2.2_to_1.3.0.sh

View file

@ -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.2.3.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
%files -n sogo-tool
%{prefix}/Tools/Admin/sogo-tool