Merge pull request #925 from tonioo/fix/reseller_permissions

Fixes about Resellers permissions.
This commit is contained in:
Antoine Nguyen
2016-09-30 12:03:47 +02:00
committed by GitHub
19 changed files with 142 additions and 118 deletions
+3 -3
View File
@@ -85,7 +85,7 @@ def grant_access_to_all_objects(user, role):
def export_admin_domains(admin):
result = [admin.mailbox.quota] \
if hasattr(admin, "mailbox") else [""]
if admin.group != "DomainAdmins":
if admin.role != "DomainAdmins":
return result
return result + [dom.name for dom in Domain.objects.get_for_admin(admin)]
@@ -139,7 +139,7 @@ def import_account_mailbox(user, account, row):
quota, override_rules=user.has_perm("admin.change_domain")
)
mb.save(creator=user)
if account.group == "DomainAdmins":
if account.role == "DomainAdmins":
for domname in row[2:]:
try:
dom = Domain.objects.get(name=domname.strip())
@@ -157,7 +157,7 @@ def account_auto_created(user):
if parameters.get_admin("AUTO_CREATE_DOMAIN_AND_MAILBOX") == "no":
return
localpart, domname = split_mailbox(user.username)
if user.group != 'SimpleUsers' and domname is None:
if user.role != 'SimpleUsers' and domname is None:
return
sadmins = User.objects.filter(is_superuser=True)
try:
+51 -37
View File
@@ -38,7 +38,7 @@ class AccountFormGeneral(forms.ModelForm):
)
role = forms.ChoiceField(
label=ugettext_lazy("Role"),
choices=[('', ugettext_lazy("Choose"))],
choices=[("", ugettext_lazy("Choose"))],
help_text=ugettext_lazy("What level of permission this user will have")
)
password1 = PasswordField(
@@ -59,52 +59,63 @@ class AccountFormGeneral(forms.ModelForm):
"username", "first_name", "last_name", "role", "is_active",
"master_user"
)
labels = {
"is_active": ugettext_lazy("Enabled")
}
def __init__(self, user, *args, **kwargs):
super(AccountFormGeneral, self).__init__(*args, **kwargs)
self.fields = OrderedDict(
(key, self.fields[key]) for key in
['role', 'username', 'first_name', 'last_name', 'password1',
'password2', 'master_user', 'is_active']
["role", "username", "first_name", "last_name", "password1",
"password2", "master_user", "is_active"]
)
self.fields["is_active"].label = _("Enabled")
self.user = user
if user.group == "DomainAdmins":
condition = (
user.role == "DomainAdmins" or
user.role == "Resellers" and self.instance == user
)
if condition:
self.fields["role"] = forms.CharField(
label="",
widget=forms.HiddenInput(attrs={"class": "form-control"}),
required=False
)
else:
self.fields["role"].choices = [('', ugettext_lazy("Choose"))]
self.fields["role"].choices += \
get_account_roles(user, kwargs['instance']) \
if 'instance' in kwargs else get_account_roles(user)
self.fields["role"].choices += (
get_account_roles(user, self.instance)
if self.instance.pk else get_account_roles(user)
)
if not user.is_superuser:
del self.fields["master_user"]
if "instance" in kwargs:
account = kwargs["instance"]
domain_disabled = (
hasattr(account, "mailbox") and
not account.mailbox.domain.enabled
)
if not self.instance.pk:
return
domain_disabled = (
hasattr(self.instance, "mailbox") and
not self.instance.mailbox.domain.enabled
)
if domain_disabled:
self.fields["is_active"].widget.attrs["disabled"] = "disabled"
if args:
empty_password = (
args[0].get("password1", "") == "" and
args[0].get("password2", "") == "")
if empty_password:
self.fields["password1"].required = False
self.fields["password2"].required = False
if domain_disabled:
self.fields["is_active"].widget.attrs['disabled'] = "disabled"
if args:
if args[0].get("password1", "") == "" \
and args[0].get("password2", "") == "":
self.fields["password1"].required = False
self.fields["password2"].required = False
if domain_disabled:
del self.fields["is_active"]
self.fields["role"].initial = account.group
if not account.is_local \
and parameters.get_admin(
"LDAP_AUTH_METHOD", app="core") == "directbind":
del self.fields["password1"]
del self.fields["password2"]
del self.fields["is_active"]
self.fields["role"].initial = self.instance.role
condition = (
not self.instance.is_local and
parameters.get_admin(
"LDAP_AUTH_METHOD", app="core") == "directbind")
if condition:
del self.fields["password1"]
del self.fields["password2"]
def domain_is_disabled(self):
"""Little shortcut to get the domain's state.
@@ -118,10 +129,12 @@ class AccountFormGeneral(forms.ModelForm):
return not self.instance.mailbox.domain.enabled
def clean_role(self):
if self.user.group == "DomainAdmins":
if self.user.role == "DomainAdmins":
if self.instance == self.user:
return "DomainAdmins"
return "SimpleUsers"
elif self.user.role == "Resellers" and self.instance == self.user:
return "Resellers"
return self.cleaned_data["role"]
def clean_username(self):
@@ -459,10 +472,12 @@ class AccountForm(TabForms):
})
def check_perms(self, account):
if account.is_superuser:
return False
return self.user.has_perm("admin.add_domain") \
and account.has_perm("core.add_user")
"""Check if perms form must displayed or not."""
return (
self.user.is_superuser and
not account.is_superuser and
account.has_perm("core.add_user")
)
def _before_is_valid(self, form):
if form["id"] == "general":
@@ -480,9 +495,8 @@ class AccountForm(TabForms):
return True
def is_valid(self):
"""Two steps validation.
"""
self.instances["general"].oldgroup = self.instances["general"].group
"""Two steps validation."""
self.instances["general"].oldgroup = self.instances["general"].role
if super(AccountForm, self).is_valid(mandatory_only=True):
self.account = self.forms[0]["instance"].save()
return super(AccountForm, self).is_valid(optional_only=True)
+3 -7
View File
@@ -8,7 +8,7 @@ from django.utils.translation import ugettext as _, ugettext_lazy
from modoboa.core.models import User
from modoboa.core import signals as core_signals
from modoboa.lib import events, parameters
from modoboa.lib.exceptions import ModoboaException, Conflict
from modoboa.lib.exceptions import Conflict
from modoboa.lib.fields import DomainNameField
from modoboa.lib.form_utils import (
YesNoField, WizardForm, WizardStep, DynamicForm, TabForms
@@ -279,17 +279,13 @@ class DomainForm(TabForms):
super(DomainForm, self).__init__(request, *args, **kwargs)
def extra_context(self, context):
domadmins = [u for u in self.domain.admins
if self.request.user.can_access(u) and not u.is_superuser]
if not self.request.user.is_superuser:
domadmins = [u for u in domadmins if u.group == "DomainAdmins"]
"""Add information to template context."""
context.update({
"title": self.domain.name,
"action": reverse(
"admin:domain_change", args=[self.domain.pk]),
"formid": "domform",
"domain": self.domain,
"domadmins": domadmins
"domain": self.domain
})
def is_valid(self):
+1 -1
View File
@@ -283,7 +283,7 @@ class Mailbox(AdminObject):
newaddress = None
if values["email"] != self.full_address:
newaddress = values["email"]
elif (self.user.group == "SimpleUsers" and
elif (self.user.role == "SimpleUsers" and
self.user.username != self.full_address):
newaddress = self.user.username
if newaddress is not None:
+1 -1
View File
@@ -102,7 +102,7 @@ class AccountSerializer(serializers.ModelSerializer):
def get_role(self, account):
"""Return role."""
return account.group
return account.role
def get_domains(self, account):
"""Return domains administered by this account."""
@@ -16,7 +16,7 @@
<hr>
<div class="row">
{% if user.group == "SuperAdmins" %}
{% if user.role == "SuperAdmins" %}
<div class="col-xs-12 col-sm-6">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">{% trans "Global counters" %}</h3></div>
+36 -12
View File
@@ -5,8 +5,9 @@ from unittest import skipIf
from django.core.urlresolvers import reverse
from django.test import override_settings
from modoboa.core.models import User
from modoboa.core import factories as core_factories
from modoboa.core import tests as core_tests
from modoboa.core.models import User
from modoboa.lib import parameters
from modoboa.lib.tests import ModoTestCase
from modoboa.lib.tests import NO_LDAP
@@ -233,7 +234,9 @@ class PermissionsTestCase(ModoTestCase):
app="limits"
)
factories.populate_database()
cls.user = User.objects.get(username='user@test.com')
cls.reseller = core_factories.UserFactory(username="reseller")
cls.reseller.role = "Resellers"
cls.user = User.objects.get(username="user@test.com")
cls.values = dict(
username=cls.user.username, role="DomainAdmins",
is_active=cls.user.is_active, email="user@test.com",
@@ -248,14 +251,15 @@ class PermissionsTestCase(ModoTestCase):
reverse("admin:account_change", args=[self.user.id]),
self.values
)
self.assertEqual(self.user.group, "DomainAdmins")
self.assertEqual(self.user.role, "DomainAdmins")
self.values["role"] = "SimpleUsers"
self.ajax_post(
reverse("admin:account_change", args=[self.user.id]),
self.values
)
self.assertNotEqual(self.user.group, 'DomainAdmins')
self.assertNotEqual(
self.user.groups.first().name, "DomainAdmins")
def test_superusers(self):
self.values["role"] = "SuperAdmins"
@@ -281,16 +285,16 @@ class PermissionsTestCase(ModoTestCase):
self.client.login(username="admin@test.com", password="toto")
)
admin = User.objects.get(username="admin@test.com")
values = dict(
username="admin@test.com", first_name="Admin",
password1="", password2="",
quota=10, is_active=True, email="admin@test.com"
)
values = {
"username": "admin@test.com", "first_name": "Admin",
"password1": "", "password2": "",
"quota": 10, "is_active": True, "email": "admin@test.com"
}
self.ajax_post(
reverse("admin:account_change", args=[admin.id]),
values
)
self.assertEqual(admin.group, "DomainAdmins")
self.assertEqual(admin.role, "DomainAdmins")
self.assertTrue(admin.can_access(
models.Domain.objects.get(name="test.com")))
@@ -299,8 +303,28 @@ class PermissionsTestCase(ModoTestCase):
reverse("admin:account_change", args=[admin.id]),
values
)
admin = User.objects.get(username="admin@test.com")
self.assertEqual(admin.group, "DomainAdmins")
admin.refresh_from_db()
self.assertEqual(admin.role, "DomainAdmins")
self.client.logout()
self.client.login(username=self.reseller.username, password="toto")
self.assertTrue(self.reseller.can_access(self.reseller))
values = {
"username": self.reseller.username, "first_name": "Reseller",
"password1": "", "password2": "",
"is_active": True
}
self.ajax_post(
reverse("admin:account_change", args=[self.reseller.pk]),
values
)
self.assertEqual(self.reseller.role, "Resellers")
values["role"] = "SuperAdmins"
self.ajax_post(
reverse("admin:account_change", args=[self.reseller.pk]),
values
)
self.assertEqual(self.reseller.role, "Resellers")
def test_domadmin_access(self):
self.client.logout()
+3 -3
View File
@@ -22,7 +22,7 @@ class AliasTestCase(ModoTestCase):
def test_alias(self):
user = User.objects.get(username="user@test.com")
values = dict(
username="user@test.com", role=user.group,
username="user@test.com", role=user.role,
is_active=user.is_active, email="user@test.com",
aliases="toto@test.com", aliases_1="titi@test.com"
)
@@ -64,7 +64,7 @@ class AliasTestCase(ModoTestCase):
"""Try to create an upper case alias."""
user = User.objects.get(username="user@test.com")
values = dict(
username="user@test.com", role=user.group,
username="user@test.com", role=user.role,
is_active=user.is_active, email="user@test.com",
aliases="Toto@test.com"
)
@@ -94,7 +94,7 @@ class AliasTestCase(ModoTestCase):
"""Try to create a alias with tag in recipient address"""
user = User.objects.get(username="user@test.com")
values = dict(
username="user@test.com", role=user.group,
username="user@test.com", role=user.role,
is_active=user.is_active, email="user@test.com"
)
self.ajax_post(
+2 -2
View File
@@ -75,7 +75,7 @@ dlist; dlist@test.com; True; user1@test.com; user@extdomain.com
self.assertEqual(u1.first_name, "User")
self.assertEqual(u1.last_name, "One")
self.assertTrue(u1.is_active)
self.assertEqual(u1.group, "SimpleUsers")
self.assertEqual(u1.role, "SimpleUsers")
self.assertTrue(mb1.use_domain_quota)
self.assertEqual(mb1.quota, 0)
self.assertTrue(admin.is_owner(mb1))
@@ -87,7 +87,7 @@ dlist; dlist@test.com; True; user1@test.com; user@extdomain.com
da = User.objects.get(username="truc@test.com")
damb = da.mailbox
self.assertEqual(da.first_name, u"René")
self.assertEqual(da.group, "DomainAdmins")
self.assertEqual(da.role, "DomainAdmins")
self.assertEqual(damb.quota, 5)
self.assertFalse(damb.use_domain_quota)
self.assertEqual(damb.full_address, "truc@test.com")
+1 -1
View File
@@ -152,7 +152,7 @@ def deldomain(request, dom_id):
def domain_statistics(request):
"""A simple page to present domain statistics."""
context = {}
if request.user.group == "SuperAdmins":
if request.user.role == "SuperAdmins":
context.update({
"domains_counter": Domain.objects.count(),
"domain_aliases_counter": DomainAlias.objects.count(),
+2 -2
View File
@@ -28,7 +28,7 @@ class GroupFactory(factory.DjangoModelFactory):
class Meta:
model = Group
name = 'DefaultGroup'
name = "DefaultGroup"
class UserFactory(PermissionFactory):
@@ -40,7 +40,7 @@ class UserFactory(PermissionFactory):
django_get_or_create = ("username", )
email = factory.LazyAttribute(lambda a: a.username)
password = '{PLAIN}toto'
password = "{PLAIN}toto"
@factory.post_generation
def groups(self, create, extracted, **kwargs):
+7 -20
View File
@@ -220,7 +220,7 @@ class User(PermissionsMixin):
@property
def tags(self):
return [{"name": "account", "label": _("account"), "type": "idt"},
{"name": self.group, "label": self.group,
{"name": self.role, "label": self.role,
"type": "grp", "color": "info"}]
@property
@@ -258,22 +258,9 @@ class User(PermissionsMixin):
from email.header import Header
if self.first_name != "" or self.last_name != "":
return "%s <%s>" % \
(Header(self.fullname, 'utf8').encode(), self.email)
(Header(self.fullname, "utf8").encode(), self.email)
return self.email
def belongs_to_group(self, name):
"""Simple shortcut to check if this user is a member of a
specific group.
:param name: the group's name
:return: a boolean
"""
try:
self.groups.get(name=name)
except Group.DoesNotExist:
return False
return True
def is_owner(self, obj):
"""Tell is the user is the unique owner of this object
@@ -339,7 +326,7 @@ class User(PermissionsMixin):
:param string role: the role to set
"""
if role is None or self.group == role:
if role is None or self.role == role:
return
events.raiseEvent("RoleChanged", self, role)
self.groups.clear()
@@ -353,7 +340,7 @@ class User(PermissionsMixin):
self.groups.add(Group.objects.get(name=role))
except Group.DoesNotExist:
self.groups.add(Group.objects.get(name="SimpleUsers"))
if self.group != "SimpleUsers" and not self.can_access(self):
if role != "SimpleUsers" and not self.can_access(self):
from modoboa.lib.permissions import grant_access_to_object
grant_access_to_object(self, self)
self.save()
@@ -392,7 +379,7 @@ class User(PermissionsMixin):
The expected order is the following::
"account", loginname, password, first name, last name, enabled, group
"account", loginname, password, first name, last name, enabled, role
Additional fields can be added using the *AccountImported* event.
@@ -463,7 +450,7 @@ class User(PermissionsMixin):
self.first_name.encode("utf-8"),
self.last_name.encode("utf-8"),
self.is_active,
self.group,
self.role,
self.email.encode("utf-8")
]
row += events.raiseQueryEvent("AccountExported", self)
@@ -472,7 +459,7 @@ class User(PermissionsMixin):
reversion.register(User)
def populate_callback(user, group='SimpleUsers'):
def populate_callback(user, group="SimpleUsers"):
"""Populate callback
If the LDAP authentication backend is in use, this callback will
+2 -2
View File
@@ -34,7 +34,7 @@
<thead>
<tr>
<th>{% trans "Name" %}</th>
<th>{% trans "Group name" %}</th>
<th>{% trans "Role" %}</th>
<th>{% trans "Last login" %}</th>
</tr>
</thead>
@@ -42,7 +42,7 @@
{% connected_users as users %}{% for u in users %}
<tr>
<td>{{ u.username }}</td>
<td>{{ u.group }}</td>
<td>{{ u.role }}</td>
<td>{{ u.last_login }}</td>
</tr>
{% endfor %}
+1 -1
View File
@@ -159,7 +159,7 @@ class LDAPAuthenticationTestCase(LDAPTestCaseMixin, ModoTestCase):
def check_created_user(self, username, group="SimpleUsers", with_mb=True):
"""Check that created user is valid."""
user = models.User.objects.get(username=username)
self.assertEqual(user.group, group)
self.assertEqual(user.role, group)
if with_mb:
self.assertEqual(user.email, username)
self.assertEqual(user.mailbox.domain.name, "example.com")
+4 -4
View File
@@ -51,7 +51,7 @@ def user_can_set_role(user, role, account=None):
)
if condition:
return [True]
if account is not None and account.group == role:
if account is not None and account.role == role:
return [True]
return [False]
@@ -60,18 +60,18 @@ def user_can_set_role(user, role, account=None):
def on_account_modified(old, new):
"""Update limits when roles are updated"""
owner = get_object_owner(old)
if owner.group not in ["SuperAdmins", "Resellers"]:
if owner.role not in ["SuperAdmins", "Resellers"]:
# Domain admins can't change the role so nothing to check.
return
if new.group not in ["DomainAdmins", "Resellers"]:
if new.role not in ["DomainAdmins", "Resellers"]:
move_pool_resource(owner, new)
@events.observe("AccountDeleted")
def on_account_deleted(account, byuser, **kwargs):
owner = get_object_owner(account)
if owner.group not in ["SuperAdmins", "Resellers"]:
if owner.role not in ["SuperAdmins", "Resellers"]:
return
move_pool_resource(owner, account)
+1 -1
View File
@@ -21,7 +21,7 @@ class ResourcePoolForm(forms.Form):
if "required_role" in tpl:
condition = (
self.account is not None and
self.account.group != tpl["required_role"]
self.account.role != tpl["required_role"]
)
if condition:
continue
+11 -8
View File
@@ -26,7 +26,7 @@ def display_pool_usage(user, target, currentpage):
name for name, tpl in utils.get_user_limit_templates()
if name not in exceptions and
("required_role" not in tpl or
tpl["required_role"] == user.group)
tpl["required_role"] == user.role)
]
limits = user.userobjectlimit_set.filter(name__in=names, max_value__gt=0)
@@ -42,10 +42,14 @@ def display_pool_usage(user, target, currentpage):
def extra_account_form(user, account=None):
if parameters.get_admin("ENABLE_ADMIN_LIMITS") == "no":
return []
if user.group not in ["SuperAdmins", "Resellers"]:
if user.role not in ["SuperAdmins", "Resellers"]:
return []
if account is not None and \
account.group not in ["Resellers", "DomainAdmins"]:
condition = (
(account is not None and
account.role not in ["Resellers", "DomainAdmins"]) or
user == account
)
if condition:
return []
return [{
@@ -81,8 +85,7 @@ def fill_domain_instances(user, domain, instances):
def check_form_access(account, form):
if form["id"] != "resources":
return [True]
if not account.belongs_to_group("Resellers") and \
not account.belongs_to_group("DomainAdmins"):
if account.role not in ["Resellers", "DomainAdmins"]:
return [False]
return [True]
@@ -91,11 +94,11 @@ def check_form_access(account, form):
def fill_account_instances(user, account, instances):
condition = (
parameters.get_admin("ENABLE_ADMIN_LIMITS") == "no" or
(not user.is_superuser and user.group != "Resellers")
(not user.is_superuser and user.role != "Resellers")
)
if condition:
return
if account.group not in ["Resellers", "DomainAdmins"]:
if account.role not in ["Resellers", "DomainAdmins"]:
return
instances["resources"] = account
+2 -2
View File
@@ -83,7 +83,7 @@ class DomainLimitsTestCase(lib_tests.ModoTestCase):
self.assertFalse(limit.is_exceeded())
user = User.objects.get(username="admin@test2.com")
values = {
"username": user.username, "role": user.group,
"username": user.username, "role": user.role,
"is_active": user.is_active, "email": user.email,
"quota_act": True, "domains": "test2.com",
"domains_1": "test.com"
@@ -130,7 +130,7 @@ class DomainLimitsTestCase(lib_tests.ModoTestCase):
limit.save()
self.assertFalse(limit.is_exceeded())
values = {
"username": user.username, "role": user.group,
"username": user.username, "role": user.role,
"is_active": user.is_active, "email": user.email,
"quota_act": True,
"aliases": "alias@test.com", "aliases_1": "alias1@test.com"
+10 -10
View File
@@ -162,7 +162,7 @@ class DomainAdminTestCase(ResourceTestCase):
def test_aliases_limit_through_account_form(self):
user = User.objects.get(username='user@test.com')
values = dict(
username=user.username, role=user.group,
username=user.username, role=user.role,
is_active=user.is_active, email=user.email, quota_act=True,
aliases="alias1@test.com", aliases_1="alias2@test.com"
)
@@ -245,12 +245,12 @@ class ResellerTestCase(ResourceTestCase):
self.user.userobjectlimit_set.filter(
name="mailboxes").update(max_value=3)
self._create_account('user1@domain.tld')
user = User.objects.get(username='user1@domain.tld')
self._create_account("user1@domain.tld")
user = User.objects.get(username="user1@domain.tld")
values = {
'username': user.username, 'role': 'DomainAdmins',
'quota_act': True, 'is_active': user.is_active,
'email': user.email
"username": user.username, "role": "DomainAdmins",
"quota_act": True, "is_active": user.is_active,
"email": user.email
}
resp = self.ajax_post(
reverse("admin:account_change", args=[user.id]),
@@ -320,7 +320,7 @@ class ResellerTestCase(ResourceTestCase):
# Give 1 mailbox and 2 aliases to the admin -> should work
values = {
'username': user.username, 'role': user.group, 'quota_act': True,
'username': user.username, 'role': user.role, 'quota_act': True,
'is_active': user.is_active, 'email': user.email,
'mailboxes_limit': 1, 'mailbox_aliases_limit': 2
}
@@ -347,7 +347,7 @@ class ResellerTestCase(ResourceTestCase):
self._create_account('admin1@domain.tld', role='DomainAdmins')
user = User.objects.get(username='admin1@domain.tld')
values = {
'username': user.username, 'role': user.group, 'quota_act': True,
'username': user.username, 'role': user.role, 'quota_act': True,
'is_active': user.is_active, 'email': user.email,
'mailboxes_limit': 1, 'mailbox_aliases_limit': 2
}
@@ -379,7 +379,7 @@ class ResellerTestCase(ResourceTestCase):
# Give 1 mailbox and 2 aliases to the admin -> should work
values = {
'username': user.username, 'role': user.group, 'quota_act': True,
'username': user.username, 'role': user.role, 'quota_act': True,
'is_active': user.is_active, 'email': user.email,
'mailboxes_limit': 1, 'mailbox_aliases_limit': 2
}
@@ -411,7 +411,7 @@ class ResellerTestCase(ResourceTestCase):
# Give 2 mailboxes and 3 aliases to the admin -> should fail.
values = {
'username': user.username, 'role': user.group, 'quota_act': True,
'username': user.username, 'role': user.role, 'quota_act': True,
'is_active': user.is_active, 'email': user.email,
'mailboxes_limit': 2, 'mailbox_aliases_limit': 3
}