A quota can be specified while importing an account.

fixes #454
This commit is contained in:
Antoine Nguyen
2013-11-22 14:05:40 +01:00
parent 77e20291a4
commit 4effce65c7
11 changed files with 74 additions and 31 deletions
+1 -1
View File
@@ -1 +1 @@
[{"pk": 1, "model": "core.user", "fields": {"username": "admin", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": false, "last_login": "2012-01-26 15:19:25", "groups": [], "user_permissions": [], "password": "{CRYPT}dTTsGDkA5ZHKg", "email": "", "date_joined": "2012-01-26 15:19:25"}},{"pk": 1, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["core", "user"], "object_id": 1}}]
[{"pk": 1, "model": "core.user", "fields": {"username": "admin", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": false, "last_login": "2012-01-26T15:19:26+01:00", "groups": [], "user_permissions": [], "password": "{CRYPT}dTTsGDkA5ZHKg", "email": "", "date_joined": "2012-01-26T15:19:26+01:00"}},{"pk": 1, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["core", "user"], "object_id": 1}}]
+10 -2
View File
@@ -304,11 +304,13 @@ class User(PermissionsMixin):
self.post_create(creator)
def from_csv(self, user, row, crypt_password=True):
"""Create a new account from a CSV file entry
"""Create a new account from a CSV file entry.
The expected order is the following::
"account", loginname, password, first name, last name, enabled, group, address[, domain, ...]
"account", loginname, password, first name, last name, enabled, group
Additional fields can be added using the *AccountImported* event.
:param user: a ``core.User`` instance
:param row: a list containing the expected information
@@ -346,6 +348,12 @@ class User(PermissionsMixin):
events.raiseEvent("AccountImported", user, self, row[7:])
def to_csv(self, csvwriter):
"""Export this account.
The CSV format is used to export.
:param csvwriter: csv object
"""
row = ["account", self.username.encode("utf-8"), self.password.encode("utf-8"),
self.first_name.encode("utf-8"), self.last_name.encode("utf-8"),
self.is_active, self.group, self.email.encode("utf-8")]
+19 -5
View File
@@ -133,13 +133,25 @@ def grant_access_to_all_objects(user, role):
@events.observe("AccountExported")
def export_admin_domains(admin):
result = [admin.mailbox_set.all()[0].quota] \
if admin.mailbox_set.count() else ['']
if admin.group != "DomainAdmins":
return []
return [dom.name for dom in Domain.objects.get_for_admin(admin)]
return result
return result + [dom.name for dom in Domain.objects.get_for_admin(admin)]
@events.observe("AccountImported")
def import_account_mailbox(user, account, row):
"""Handle extra fields when an account is imported.
Expected fields:
email address; quota; [domain; ...]
:param User user: user importing the account
:param User account: account being imported
:param list rom: list of fields (strings)
"""
account.email = row[0].strip()
if account.email != "":
account.save()
@@ -152,12 +164,14 @@ def import_account_mailbox(user, account, row):
)
if not user.can_access(domain):
raise PermDeniedException
quota = int(row[1].strip())
use_domain_quota = True if not quota else False
mb = Mailbox(address=mailbox, domain=domain,
user=account, use_domain_quota=True)
mb.set_quota(override_rules=user.has_perm("admin.change_domain"))
user=account, use_domain_quota=use_domain_quota)
mb.set_quota(quota, override_rules=user.has_perm("admin.change_domain"))
mb.save(creator=user)
if account.group == "DomainAdmins":
for domname in row[1:]:
for domname in row[2:]:
try:
dom = Domain.objects.get(name=domname.strip())
except Domain.DoesNotExist:
+2
View File
@@ -13,11 +13,13 @@ class MailboxFactory(PermissionFactory):
FACTORY_FOR = models.Mailbox
quota = 10
enabled = True
class AliasFactory(PermissionFactory):
FACTORY_FOR = models.Alias
enabled = True
def populate_database():
"""Create test data.
@@ -1 +0,0 @@
[{"pk": 1, "model": "core.user", "fields": {"username": "admin", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": false, "is_local": true, "last_login": "2012-01-26 15:19:25", "groups": [], "user_permissions": [], "password": "{CRYPT}dTTsGDkA5ZHKg", "email": "", "date_joined": "2012-01-26 15:19:25"}},{"pk": 1, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["core", "user"], "object_id": 1}}]
@@ -1 +0,0 @@
[{"pk": 2, "model": "admin.user", "fields": {"username": "user@test.com", "is_active": "True", "is_superuser": "False", "password": "{PLAIN}toto", "groups": [2]}},{"pk": 3, "model": "admin.user", "fields": {"username": "admin@test.com", "is_active": "True", "is_superuser": "False", "password": "{PLAIN}toto", "groups": [1]}},{"pk": 4, "model": "admin.user", "fields": {"username": "superadmin@test.com", "is_active": "True", "is_superuser": "True", "password": "{PLAIN}toto", "groups": []}},{"pk": 1, "model": "admin.ObjectDates", "fields": {"creation": "2011-09-07 09:27:07.579603", "last_modification": "2011-09-07 09:27:07.579603"}}, {"pk": 2, "model": "admin.ObjectDates", "fields": {"creation": "2011-09-07 09:27:07.579603", "last_modification": "2011-09-07 09:27:07.579603"}}, {"pk": 3, "model": "admin.ObjectDates", "fields": {"creation": "2011-09-07 09:27:07.579603", "last_modification": "2011-09-07 09:27:07.579603"}}, {"pk": 1, "model": "admin.Domain", "fields": {"name" : "test.com", "quota" : "100", "dates": "1"}}, {"pk": 1, "model": "admin.Mailbox", "fields": {"address": "user", "domain": "1", "dates": "1", "quota": "10", "user": "2"}}, {"pk": 2, "model": "admin.Mailbox", "fields": {"address": "admin", "domain": "1", "dates": "2", "quota": "10", "user": "3"}}, {"pk": 3, "model": "admin.Mailbox", "fields": {"address": "superadmin", "domain": "1", "dates": "3", "quota": "10", "user": "4"}}, {"pk": "user@test.com", "model": "admin.Quota", "fields": {"mbox": 1, "username": "user@test.com", "bytes": 0, "messages": 0}}, {"pk": "admin@test.com", "model": "admin.Quota", "fields": {"mbox": 2, "username": "admin@test.com", "bytes": 0, "messages": 0}}, {"pk": "superadmin@test.com", "model": "admin.Quota", "fields": {"mbox": 3, "username": "superadmin@test.com", "bytes": 0, "messages": 0}},{"pk": 2, "model": "admin.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "user"], "object_id": 2}},{"pk": 3, "model": "admin.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "user"], "object_id": 3}}]
+7 -7
View File
@@ -22,7 +22,7 @@ class ExportTestCase(ModoTestCase):
def test_export_identities(self):
response = self.__export_identities()
self.assertEqual(response.content, "account;admin;{CRYPT}dTTsGDkA5ZHKg;;;True;SuperAdmins;\r\naccount;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;test2.com\r\naccount;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com\r\nalias;alias@test.com;False;user@test.com\r\nforward;forward@test.com;False;user@external.com\r\ndlist;postmaster@test.com;False;toto@titi.com;test@truc.fr\r\n")
self.assertEqual(response.content, "account;admin;{CRYPT}dTTsGDkA5ZHKg;;;True;SuperAdmins;;\r\naccount;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;10;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;10;test2.com\r\naccount;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com;10\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com;10\r\nalias;alias@test.com;True;user@test.com\r\nforward;forward@test.com;True;user@external.com\r\ndlist;postmaster@test.com;True;toto@titi.com;test@truc.fr\r\n")
def test_export_simpleusers(self):
response = self.__export_identities(
@@ -30,7 +30,7 @@ class ExportTestCase(ModoTestCase):
)
self.assertEqual(
response.content.strip(),
"account;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com"
"account;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com;10\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com;10"
)
def test_export_superadmins(self):
@@ -39,7 +39,7 @@ class ExportTestCase(ModoTestCase):
)
self.assertEqual(
response.content.strip(),
"account;admin;{CRYPT}dTTsGDkA5ZHKg;;;True;SuperAdmins;"
"account;admin;{CRYPT}dTTsGDkA5ZHKg;;;True;SuperAdmins;;"
)
def test_export_domainadmins(self):
@@ -48,26 +48,26 @@ class ExportTestCase(ModoTestCase):
)
self.assertEqual(
response.content.strip(),
"account;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;test2.com"
"account;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;10;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;10;test2.com"
)
def test_export_aliases(self):
response = self.__export_identities(idtfilter="alias")
self.assertEqual(
response.content.strip(),
"alias;alias@test.com;False;user@test.com"
"alias;alias@test.com;True;user@test.com"
)
def test_export_forwards(self):
response = self.__export_identities(idtfilter="forward")
self.assertEqual(
response.content.strip(),
"forward;forward@test.com;False;user@external.com"
"forward;forward@test.com;True;user@external.com"
)
def test_export_dlists(self):
response = self.__export_identities(idtfilter="dlist")
self.assertEqual(
response.content.strip(),
"dlist;postmaster@test.com;False;toto@titi.com;test@truc.fr"
"dlist;postmaster@test.com;True;toto@titi.com;test@truc.fr"
)
+26 -8
View File
@@ -53,8 +53,8 @@ domainalias;test.alias;test.com;True
def test_identities_import(self):
f = ContentFile(b"""
account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com
account; truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; test.com
account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com; 0
account; truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; 5; test.com
alias; alias1@test.com; True; user1@test.com
forward; fwd1@test.com; True; user@extdomain.com
dlist; dlist@test.com; True; user1@test.com; user@extdomain.com
@@ -65,20 +65,26 @@ dlist; dlist@test.com; True; user1@test.com; user@extdomain.com
)
admin = User.objects.get(username="admin")
u1 = User.objects.get(username="user1@test.com")
mb1 = u1.mailbox_set.all()[0]
self.assertTrue(admin.is_owner(u1))
self.assertEqual(u1.email, "user1@test.com")
self.assertEqual(u1.first_name, "User")
self.assertEqual(u1.last_name, "One")
self.assertTrue(u1.is_active)
self.assertEqual(u1.group, "SimpleUsers")
self.assertTrue(admin.is_owner(u1.mailbox_set.all()[0]))
self.assertEqual(u1.mailbox_set.all()[0].full_address, "user1@test.com")
self.assertTrue(mb1.use_domain_quota)
self.assertEqual(mb1.quota, 0)
self.assertTrue(admin.is_owner(mb1))
self.assertEqual(mb1.full_address, "user1@test.com")
self.assertTrue(self.clt.login(username="user1@test.com", password="toto"))
da = User.objects.get(username="truc@test.com")
damb = da.mailbox_set.all()[0]
self.assertEqual(da.first_name, u"René")
self.assertEqual(da.group, "DomainAdmins")
self.assertEqual(da.mailbox_set.all()[0].full_address, "truc@test.com")
self.assertEqual(damb.quota, 5)
self.assertFalse(damb.use_domain_quota)
self.assertEqual(damb.full_address, "truc@test.com")
dom = Domain.objects.get(name="test.com")
self.assertIn(da, dom.admins)
u = User.objects.get(username="user@test.com")
@@ -97,10 +103,22 @@ dlist; dlist@test.com; True; user1@test.com; user@extdomain.com
self.assertIn("user@extdomain.com", dlist.extmboxes)
self.assertTrue(admin.is_owner(dlist))
def test_import_invalid_quota(self):
self.clt.logout()
self.clt.login(username="admin@test.com", password="toto")
f = ContentFile(b"""
account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com; 20
""", name="identities.csv")
resp = self.clt.post(
reverse("modoboa.extensions.admin.views.import.import_identities"),
{"sourcefile": f, "crypt_password": True}
)
self.assertIn('Quota is greater than the allowed', resp.content)
def test_import_duplicate(self):
f = ContentFile(b"""
account; admin@test.com; toto; Admin; ; True; DomainAdmins; admin@test.com; test.com
account; truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; test.com
account; admin@test.com; toto; Admin; ; True; DomainAdmins; admin@test.com; 0; test.com
account; truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; 0; test.com
""", name="identities.csv")
self.clt.post(
reverse("modoboa.extensions.admin.views.import.import_identities"),
@@ -119,7 +137,7 @@ account; truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; te
self.clt.logout()
self.assertTrue(self.clt.login(username="admin@test.com", password="toto"))
f = ContentFile(b"""
account; sa@test.com; toto; Super; Admin; True; SuperAdmins; superadmin@test.com
account; sa@test.com; toto; Super; Admin; True; SuperAdmins; superadmin@test.com; 50
""", name="identities.csv")
self.clt.post(
reverse("modoboa.extensions.admin.views.import.import_identities"),
+1 -1
View File
@@ -152,7 +152,7 @@ def import_identities(request):
helptext = _("""Provide a CSV file where lines respect one of the following formats:
<ul>
<li><em>account; loginname; password; first name; last name; enabled; group; address[, domain, ...]</em></li>
<li><em>account; loginname; password; first name; last name; enabled; group; address; quota; [, domain, ...]</em></li>
<li><em>alias; address; enabled; internal recipient</em></li>
<li><em>forward; address; enabled; external recipient</em></li>
<li><em>dlist; address; enabled; recipient; recipient; ...</em></li>
+6 -3
View File
@@ -58,6 +58,9 @@ class GenericParametersForm(forms.Form):
def _load_initial_values(self):
raise NotImplementedError
def _decode_value(self, value):
return value.decode('unicode_escape').replace('\\r\\n', '\n')
def _load_extra_parameters(self, level):
params = events.raiseDictEvent('GetExtraParameters', self.app, level)
for pname, pdef in params.items():
@@ -85,7 +88,7 @@ class AdminParametersForm(GenericParametersForm):
names = ["%s.%s" % (self.app, name.upper()) for name in self.fields.keys()]
for p in Parameter.objects.filter(name__in=names):
self.fields[p.shortname].initial = p.value
self.fields[p.shortname].initial = self._decode_value(p.value)
def save(self):
from .models import Parameter
@@ -127,7 +130,7 @@ class UserParametersForm(GenericParametersForm):
names = ["%s.%s" % (self.app, name.upper()) for name in self.fields.keys()]
for p in UserParameter.objects.filter(user=self.user, name__in=names):
self.fields[p.shortname].initial = p.value
self.fields[p.shortname].initial = self._decode_value(p.value)
@staticmethod
def has_access(user):
@@ -256,7 +259,7 @@ def get_admin(name, app=None, raise_error=True):
p = Parameter.objects.get(name="%s.%s" % (app, name))
except Parameter.DoesNotExist:
return _params["A"][app]["defaults"][name]
return p.value.decode("unicode_escape")
return p.value.decode("unicode_escape").replace('\\r\\n', '\n')
def get_user(user, name, app=None, raise_error=True):
+2 -2
View File
@@ -1,7 +1,7 @@
# coding: utf-8
import json
from django.test import TestCase
from django.test.client import Client
from django.utils import simplejson
from django import forms
from django.core.urlresolvers import reverse
from modoboa.lib import parameters
@@ -17,7 +17,7 @@ class ModoTestCase(TestCase):
response = getattr(self.clt, method) \
(url, params, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, status)
return simplejson.loads(response.content)
return json.loads(response.content)
def ajax_post(self, *args, **kwargs):
return self.ajax_request('post', *args, **kwargs)