diff --git a/modoboa/core/factories.py b/modoboa/core/factories.py index df37f5a47..d4dd9d6ad 100644 --- a/modoboa/core/factories.py +++ b/modoboa/core/factories.py @@ -21,6 +21,7 @@ class GroupFactory(factory.DjangoModelFactory): class UserFactory(PermissionFactory): FACTORY_FOR = models.User + FACTORY_DJANGO_GET_OR_CREATE = ("username", ) email = factory.LazyAttribute(lambda a: a.username) password = '{PLAIN}toto' diff --git a/modoboa/extensions/admin/factories.py b/modoboa/extensions/admin/factories.py index 306d29914..9097f1b2c 100644 --- a/modoboa/extensions/admin/factories.py +++ b/modoboa/extensions/admin/factories.py @@ -1,9 +1,15 @@ +""" +Fixtures factories. +""" +import factory + from modoboa.core.factories import PermissionFactory, UserFactory from . import models class DomainFactory(PermissionFactory): FACTORY_FOR = models.Domain + FACTORY_DJANGO_GET_OR_CREATE = ("name", ) quota = 10 enabled = True @@ -12,6 +18,8 @@ class DomainFactory(PermissionFactory): class MailboxFactory(PermissionFactory): FACTORY_FOR = models.Mailbox + domain = factory.SubFactory(DomainFactory) + user = factory.SubFactory(UserFactory) quota = 10 enabled = True @@ -21,6 +29,7 @@ class AliasFactory(PermissionFactory): enabled = True + def populate_database(): """Create test data. diff --git a/modoboa/extensions/limits/tests.py b/modoboa/extensions/limits/tests.py index cf529f8ae..52e572b3d 100644 --- a/modoboa/extensions/limits/tests.py +++ b/modoboa/extensions/limits/tests.py @@ -4,9 +4,9 @@ from modoboa.lib import parameters from modoboa.core.models import User from modoboa.core.factories import UserFactory from modoboa.extensions.admin.factories import ( - DomainFactory, MailboxFactory, populate_database + populate_database ) -from modoboa.extensions.admin.models import Alias, Domain, DomainAlias +from modoboa.extensions.admin.models import Alias, Domain from modoboa.extensions.limits.models import LimitTemplates diff --git a/modoboa/extensions/radicale/factories.py b/modoboa/extensions/radicale/factories.py new file mode 100644 index 000000000..a164df7db --- /dev/null +++ b/modoboa/extensions/radicale/factories.py @@ -0,0 +1,31 @@ +""" +Fixtures factories. +""" +import factory + +from modoboa.extensions.admin.factories import ( + DomainFactory, MailboxFactory +) + +from . import models + + +class UserCalendarFactory(factory.DjangoModelFactory): + FACTORY_FOR = models.UserCalendar + + name = factory.Sequence(lambda n: 'User calendar %s' % n) + mailbox = factory.SubFactory(MailboxFactory) + + +class SharedCalendarFactory(factory.DjangoModelFactory): + FACTORY_FOR = models.SharedCalendar + + name = factory.Sequence(lambda n: 'Shared calendar %s' % n) + domain = factory.SubFactory(DomainFactory) + + +class AccessRuleFactory(factory.DjangoModelFactory): + FACTORY_FOR = models.AccessRule + + mailbox = factory.SubFactory(MailboxFactory) + calendar = factory.SubFactory(UserCalendarFactory) diff --git a/modoboa/extensions/radicale/management/commands/generate_rights.py b/modoboa/extensions/radicale/management/commands/generate_rights.py index 23f5e0c97..81c37e73c 100644 --- a/modoboa/extensions/radicale/management/commands/generate_rights.py +++ b/modoboa/extensions/radicale/management/commands/generate_rights.py @@ -4,7 +4,9 @@ from optparse import make_option from django.core.management.base import BaseCommand +from modoboa.core.models import User from modoboa.lib import parameters +from modoboa.extensions.admin import Domain from modoboa.extensions.radicale import Radicale from modoboa.extensions.radicale.models import ( AccessRule @@ -24,14 +26,14 @@ class Command(BaseCommand): def _generate_acr(self, name, user, collection, perm="rw", comment=None): """Write a new access control rule to the config file. """ + if comment is not None: + self._cfgfile.write("\n# %s" % comment) self._cfgfile.write(""" -%s [%s] user = %s collection = %s permission = %s - """ % ("" if comment is None else ("# %s" % comment), - name, user, collection, perm) +""" % (name, user, collection, perm) ) def _user_access_rules(self): @@ -39,7 +41,7 @@ permission = %s """ for acr in AccessRule.objects.select_related().all(): section = "%s-to-%s-acr" % ( - acr.calendar.mailbox, acr.mailbox + acr.mailbox, acr.calendar ) permission = "" if acr.read: @@ -54,7 +56,7 @@ permission = %s """Generate access rules for super administrators. """ for sa in User.objects.filter(is_superuser=True): - section = "[sa-%s-acr]" % sa.username + section = "sa-%s-acr" % sa.username self._generate_acr( section, sa.username, ".*" ) @@ -64,7 +66,7 @@ permission = %s """ for da in User.objects.filter(groups__name="DomainAdmins"): for domain in Domain.objects.get_for_admin(da): - section = "[da-%s-to-%s-acr]" % (da.email, da.name) + section = "da-%s-to-%s-acr" % (da.email, domain.name) self._generate_acr( section, da.email, "%s/user/.*" % domain.name ) diff --git a/modoboa/extensions/radicale/tests.py b/modoboa/extensions/radicale/tests.py index 501deb776..b95fece87 100644 --- a/modoboa/extensions/radicale/tests.py +++ b/modoboa/extensions/radicale/tests.py @@ -1,16 +1,269 @@ """ -This file demonstrates writing tests using the unittest module. These will pass -when you run "manage.py test". - -Replace this with more appropriate tests for your application. +Radicale extension unit tests. """ +import os +import tempfile +from ConfigParser import SafeConfigParser -from django.test import TestCase +from django.core.exceptions import ObjectDoesNotExist +from django.core.urlresolvers import reverse +from django.core import management + +from modoboa.lib import parameters +from modoboa.lib.tests import ExtTestCase +from modoboa.extensions.admin.factories import ( + MailboxFactory, populate_database +) +from modoboa.extensions.admin.models import ( + Domain, Mailbox +) + +from .factories import ( + UserCalendarFactory, SharedCalendarFactory, AccessRuleFactory +) +from .models import UserCalendar, SharedCalendar, AccessRule -class SimpleTest(TestCase): - def test_basic_addition(self): - """ - Tests that 1 + 1 always equals 2. - """ - self.assertEqual(1 + 1, 2) +class UserCalendarTestCase(ExtTestCase): + fixtures = ["initial_users.json"] + + def setUp(self): + super(UserCalendarTestCase, self).setUp() + self.activate_extensions('radicale') + populate_database() + + def assertRuleEqual(self, calname, username, read=False, write=False): + acr = AccessRule.objects.get( + mailbox__user__username=username, + calendar__name=calname) + self.assertEqual(acr.read, read) + self.assertEqual(acr.write, write) + + def test_add_calendar(self): + MailboxFactory( + address="polo", domain__name="test.com", + user__username="polo@test.com") + + # As a super administrator + mbox = Mailbox.objects.get(address='user', domain__name='test.com') + values = { + "name": "Test calendar", + "mailbox": mbox.pk, + "username": "admin@test.com", + "read_access": 1, + "stepid": "step2" + } + self.ajax_post( + reverse("new_user_calendar"), values) + self.clt.logout() + self.assertRuleEqual("Test calendar", "admin@test.com", read=True) + + # As a domain administrator + self.clt.login(username="admin@test.com", password="toto") + values = { + "name": "Test calendar 2", + "mailbox": mbox.pk, + "username": "admin@test.com", + "read_access": 1, + "write_access": 1, + "stepid": "step2" + } + self.ajax_post( + reverse("new_user_calendar"), values) + self.clt.logout() + self.assertRuleEqual( + "Test calendar 2", "admin@test.com", read=True, write=True + ) + + # As a user + self.clt.login(username="user@test.com", password="toto") + values = { + "name": "My calendar", + "username": "admin@test.com", + "read_access": 1, + "write_access": 1, + "username_1": "polo@test.com", + "write_access_1": 1, + "stepid": "step2" + } + self.ajax_post( + reverse("new_user_calendar"), values) + UserCalendar.objects.get(name="My calendar") + self.assertRuleEqual( + "My calendar", "admin@test.com", read=True, write=True) + self.assertRuleEqual("My calendar", "polo@test.com", write=True) + + def test_edit_calendar(self): + cal = UserCalendarFactory( + mailbox__user__username='test@modoboa.org', + mailbox__address='test', mailbox__domain__name='modoboa.org') + values = { + "name": "Modified", "mailbox": cal.mailbox.pk + } + self.ajax_post( + reverse("user_calendar", args=[cal.pk]), values + ) + + def test_del_calendar(self): + cal = UserCalendarFactory( + mailbox__user__username='test@modoboa.org', + mailbox__address='test', mailbox__domain__name='modoboa.org') + self.ajax_delete( + reverse("user_calendar", args=[cal.pk]) + ) + with self.assertRaises(ObjectDoesNotExist): + UserCalendar.objects.get(pk=cal.pk) + + def test_del_calendar_denied(self): + cal = UserCalendarFactory( + mailbox__user__username='test@modoboa.org', + mailbox__address='test', mailbox__domain__name='modoboa.org') + self.clt.logout() + self.clt.login(username="admin@test.com", password="toto") + self.ajax_delete( + reverse("user_calendar", args=[cal.pk]), status=403 + ) + + def test_add_calendar_denied(self): + self.clt.logout() + self.clt.login(username="admin@test.com", password="toto") + values = { + "name": "Test calendar", + "mailbox": Mailbox.objects.get( + address="user", domain__name="test2.com").pk, + "stepid": "step2" + } + self.ajax_post( + reverse("new_user_calendar"), values, status=400) + + +class SharedCalendarTestCase(ExtTestCase): + fixtures = ["initial_users.json"] + + def setUp(self): + super(SharedCalendarTestCase, self).setUp() + self.activate_extensions('radicale') + populate_database() + + def test_add_calendar(self): + # As a super administrator + domain = Domain.objects.get(name="test.com") + values = { + "name": "Test calendar", + "domain": domain.pk, + } + self.ajax_post( + reverse("new_shared_calendar"), values) + self.clt.logout() + + # As a domain administrator + self.clt.login(username="admin@test.com", password="toto") + values = { + "name": "Test calendar 2", + "domain": domain.pk + } + self.ajax_post( + reverse("new_shared_calendar"), values) + self.clt.logout() + + def test_add_calendar_denied(self): + self.clt.logout() + self.clt.login(username="admin@test.com", password="toto") + values = { + "name": "Test calendar", + "domain": Domain.objects.get(name="test2.com") + } + self.ajax_post( + reverse("new_shared_calendar"), values, status=400) + + def test_edit_calendar(self): + cal = SharedCalendarFactory( + domain__name='modoboa.org') + values = { + "name": "Modified", "domain": cal.domain.pk + } + self.ajax_post( + reverse("shared_calendar", args=[cal.pk]), values + ) + cal = SharedCalendar.objects.get(pk=cal.pk) + self.assertEqual(cal.name, "Modified") + + def test_del_calendar(self): + cal = SharedCalendarFactory(domain__name='modoboa.org') + self.ajax_delete( + reverse("shared_calendar", args=[cal.pk]) + ) + with self.assertRaises(ObjectDoesNotExist): + SharedCalendar.objects.get(pk=cal.pk) + + def test_del_calendar_denied(self): + cal = SharedCalendarFactory(domain__name='modoboa.org') + self.clt.logout() + self.clt.login(username="admin@test.com", password="toto") + self.ajax_delete( + reverse("shared_calendar", args=[cal.pk]), status=403 + ) + + +class AccessRuleTestCase(ExtTestCase): + fixtures = ["initial_users.json"] + + def setUp(self): + super(AccessRuleTestCase, self).setUp() + self.activate_extensions('radicale') + populate_database() + self.rights_file_path = tempfile.mktemp() + parameters.save_admin( + "RIGHTS_FILE_PATH", self.rights_file_path, app="radicale") + + def tearDown(self): + os.unlink(self.rights_file_path) + + def test_rights_file_generation(self): + mbox = Mailbox.objects.get(address="admin", domain__name="test.com") + cal = UserCalendarFactory(mailbox=mbox) + + AccessRuleFactory( + mailbox=Mailbox.objects.get( + address="user", domain__name="test.com"), + calendar=cal, read=True) + management.call_command("generate_rights", verbosity=False) + + cfg = SafeConfigParser() + with open(self.rights_file_path) as fpo: + cfg.readfp(fpo) + + # Check mandatory rules + self.assertTrue(cfg.has_section("domain-shared-calendars")) + self.assertTrue(cfg.has_section("owners-access")) + + # Check user-defined rules + section = "user@test.com-to-User calendar 1-acr" + self.assertTrue(cfg.has_section(section)) + self.assertEqual(cfg.get(section, "user"), "user@test.com") + self.assertEqual( + cfg.get(section, "collection"), + "test.com/user/admin/User calendar 1" + ) + self.assertEqual(cfg.get(section, "permission"), "r") + + def test_rights_file_generation_with_admin(self): + parameters.save_admin( + "ALLOW_CALENDARS_ADMINISTRATION", "yes", app="radicale") + management.call_command("generate_rights", verbosity=False) + cfg = SafeConfigParser() + with open(self.rights_file_path) as fpo: + cfg.readfp(fpo) + + # Check mandatory rules + self.assertTrue(cfg.has_section("domain-shared-calendars")) + self.assertTrue(cfg.has_section("owners-access")) + + # Super admin rules + section = "sa-admin-acr" + self.assertTrue(cfg.has_section(section)) + + # Domain admin rules + for section in ["da-admin@test.com-to-test.com-acr", + "da-admin@test2.com-to-test2.com-acr"]: + self.assertTrue(cfg.has_section(section)) diff --git a/modoboa/lib/tests.py b/modoboa/lib/tests.py index f8fbf139a..eacbbee53 100644 --- a/modoboa/lib/tests.py +++ b/modoboa/lib/tests.py @@ -13,7 +13,9 @@ class ModoTestCase(TestCase): self.clt = Client() self.assertEqual(self.clt.login(username=username, password=password), True) - def ajax_request(self, method, url, params, status=200): + def ajax_request(self, method, url, params=None, status=200): + if params is None: + params = {} response = getattr(self.clt, method) \ (url, params, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, status) @@ -22,6 +24,12 @@ class ModoTestCase(TestCase): def ajax_post(self, *args, **kwargs): return self.ajax_request('post', *args, **kwargs) + def ajax_put(self, *args, **kwargs): + return self.ajax_request('put', *args, **kwargs) + + def ajax_delete(self, *args, **kwargs): + return self.ajax_request('delete', *args, **kwargs) + def ajax_get(self, *args, **kwargs): return self.ajax_request('get', *args, **kwargs) diff --git a/requirements.txt b/requirements.txt index 9c7b48790..ca93f777b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,5 +8,5 @@ sievelib chardet argparse rfc6266 -factory-boy +factory-boy<2.4 passlib