From dde98c235c4438e2eef37263e446b14bc369efb5 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 29 Jan 2015 21:06:15 +0100 Subject: [PATCH] WIP: change the way extensions initial data is loaded. --- modoboa/core/__init__.py | 10 ++++ modoboa/core/commands/deploy.py | 13 ++--- .../core/commands/templates/settings.py.tpl | 4 -- modoboa/core/extensions.py | 4 ++ .../management/commands/load_initial_data.py | 51 +++++++++++++++++++ ..._extension.py => 0002_delete_extension.py} | 2 +- .../core/migrations/0002_load_initial_data.py | 26 ---------- .../migrations/0002_load_initial_data.py | 43 ---------------- modoboa/extensions/admin/modo_extension.py | 24 +++++++++ .../extensions/limits/general_callbacks.py | 20 ++++++++ .../migrations/0002_load_initial_data.py | 23 +-------- modoboa/extensions/limits/modo_extension.py | 8 +++ .../general_callbacks.py | 27 +++++++++- .../migrations/0002_load_initial_data.py | 23 --------- .../postfix_relay_domains/modo_extension.py | 12 ----- .../extensions/radicale/general_callbacks.py | 20 ++++++++ .../migrations/0002_load_initial_data.py | 37 -------------- modoboa/lib/migration_tools.py | 18 ------- modoboa/lib/permissions.py | 16 ++++++ 19 files changed, 186 insertions(+), 195 deletions(-) create mode 100644 modoboa/core/management/commands/load_initial_data.py rename modoboa/core/migrations/{0003_delete_extension.py => 0002_delete_extension.py} (86%) delete mode 100644 modoboa/core/migrations/0002_load_initial_data.py delete mode 100644 modoboa/extensions/admin/migrations/0002_load_initial_data.py delete mode 100644 modoboa/extensions/radicale/migrations/0002_load_initial_data.py delete mode 100644 modoboa/lib/migration_tools.py diff --git a/modoboa/core/__init__.py b/modoboa/core/__init__.py index 8a03f0e5d..e31c299f1 100644 --- a/modoboa/core/__init__.py +++ b/modoboa/core/__init__.py @@ -18,6 +18,7 @@ BASE_EVENTS = [ "ExtraAccountActions", "RoleChanged", "GetExtraRoles", + "GetExtraRolePermissions", "PasswordChange", "UserCanSetRole", @@ -41,6 +42,15 @@ BASE_EVENTS = [ "SaveExtraFormFields", ] +PERMISSIONS = { + "DomainAdmins": [ + ["core", "user", "add_user"], + ["core", "user", "change_user"], + ["core", "user", "delete_user"], + ], + "SimpleUsers": [] +} + def load_core_settings(): """Load core settings. diff --git a/modoboa/core/commands/deploy.py b/modoboa/core/commands/deploy.py index 4e77afc5d..4bcbd7e9a 100644 --- a/modoboa/core/commands/deploy.py +++ b/modoboa/core/commands/deploy.py @@ -6,6 +6,7 @@ import sys import subprocess +import django from django.core import management from django.template import Context, Template @@ -138,6 +139,7 @@ class DeployCommand(Command): return info def handle(self, parsed_args): + django.setup() management.call_command( 'startproject', parsed_args.name, verbosity=False ) @@ -215,17 +217,10 @@ class DeployCommand(Command): os.unlink("%s/settings.pyc" % path) if parsed_args.dbaction == "install": self._exec_django_command( - "syncdb", parsed_args.name, '--noinput' - ) - exec_cmd('sed -ri "s|^#(\s+\'south)|\\1|" %s/settings.py' % path) - self._exec_django_command( - "syncdb", parsed_args.name, + "migrate", parsed_args.name, '--noinput' ) self._exec_django_command( - 'migrate', parsed_args.name, '--fake' - ) - self._exec_django_command( - "loaddata", parsed_args.name, 'initial_users.json' + "load_initial_data", parsed_args.name ) elif parsed_args.dbaction == "upgrade": exec_cmd('sed -ri "s|^#(\s+\'south)|\\1|" %s/settings.py' % path) diff --git a/modoboa/core/commands/templates/settings.py.tpl b/modoboa/core/commands/templates/settings.py.tpl index aaf648741..2ba6dc86b 100644 --- a/modoboa/core/commands/templates/settings.py.tpl +++ b/modoboa/core/commands/templates/settings.py.tpl @@ -46,7 +46,6 @@ INSTALLED_APPS = ( 'django.contrib.messages', 'django.contrib.sites', 'django.contrib.staticfiles', -# 'south', 'reversion', ) @@ -150,9 +149,6 @@ STATICFILES_DIRS = ( MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') -# South settings -SOUTH_TESTS_MIGRATE = False - # Modoboa settings #MODOBOA_CUSTOM_LOGO = os.path.join(MEDIA_URL, "custom_logo.png") diff --git a/modoboa/core/extensions.py b/modoboa/core/extensions.py index a67e3c04d..82e5fd30e 100644 --- a/modoboa/core/extensions.py +++ b/modoboa/core/extensions.py @@ -29,6 +29,10 @@ class ModoExtension(object): always_active=self.always_active ) + def load_initial_data(self): + """Declare extension data in this method.""" + pass + def load(self): pass diff --git a/modoboa/core/management/commands/load_initial_data.py b/modoboa/core/management/commands/load_initial_data.py new file mode 100644 index 000000000..a42a99ef3 --- /dev/null +++ b/modoboa/core/management/commands/load_initial_data.py @@ -0,0 +1,51 @@ +"""A management command to load Modoboa initial data: + +* Create a default super admin if none exists +* Create groups and permissions + +""" + +from django.contrib.auth.models import Group +from django.core.management.base import BaseCommand + +from modoboa.core import PERMISSIONS +from modoboa.core.extensions import exts_pool +from modoboa.core.models import User, ObjectAccess +from modoboa.lib import events +from modoboa.lib.permissions import add_permissions_to_group +from . import CloseConnectionMixin + + +class Command(BaseCommand, CloseConnectionMixin): + + """Command defintion.""" + + help = "Load Modoboa initial data" + + def handle(self, *args, **options): + """Command entry point.""" + + if not User.objects.filter(pk=1, username="admin").exists(): + admin = User(username="admin", is_superuser=True) + admin.set_password("password") + admin.save() + ObjectAccess.objects.create( + user=admin, content_object=admin, is_owner=True) + + groups = PERMISSIONS.keys() + [ + role[0] for role in events.raiseQueryEvent("GetExtraRoles") + ] + for groupname in groups: + group, created = Group.objects.get_or_create(name=groupname) + permissions = ( + PERMISSIONS.get(groupname, []) + + events.raiseQueryEvent("GetExtraRolePermissions", groupname) + ) + if not permissions: + continue + add_permissions_to_group(group, permissions) + + exts_pool.load_all() + for extname in exts_pool.extensions.keys(): + extension = exts_pool.get_extension(extname) + extension.load_initial_data() diff --git a/modoboa/core/migrations/0003_delete_extension.py b/modoboa/core/migrations/0002_delete_extension.py similarity index 86% rename from modoboa/core/migrations/0003_delete_extension.py rename to modoboa/core/migrations/0002_delete_extension.py index 3b4dc989e..073ef44bb 100644 --- a/modoboa/core/migrations/0003_delete_extension.py +++ b/modoboa/core/migrations/0002_delete_extension.py @@ -7,7 +7,7 @@ from django.db import models, migrations class Migration(migrations.Migration): dependencies = [ - ('core', '0002_load_initial_data'), + ('core', '0001_initial'), ] operations = [ diff --git a/modoboa/core/migrations/0002_load_initial_data.py b/modoboa/core/migrations/0002_load_initial_data.py deleted file mode 100644 index 67be945f2..000000000 --- a/modoboa/core/migrations/0002_load_initial_data.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -def load_initial_data(apps, schema_editor): - """Load initial data.""" - User = apps.get_model("core", "User") - user = User(username="admin", is_superuser=True) - user.set_password("password") - user.save() - - ObjectAccess = apps.get_model("core", "ObjectAccess") - ObjectAccess.objects.create(user=user, content_object=user, is_owner=True) - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0001_initial'), - ] - - operations = [ - migrations.RunPython(load_initial_data) - ] diff --git a/modoboa/extensions/admin/migrations/0002_load_initial_data.py b/modoboa/extensions/admin/migrations/0002_load_initial_data.py deleted file mode 100644 index 1f08a43c9..000000000 --- a/modoboa/extensions/admin/migrations/0002_load_initial_data.py +++ /dev/null @@ -1,43 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - -from modoboa.lib.migration_tools import add_permissions_to_group - -PERMISSIONS = [ - ["core", "user", "add_user"], - ["core", "user", "change_user"], - ["core", "user", "delete_user"], - ["admin", "domain", "view_domain"], - ["admin", "mailbox", "add_mailbox"], - ["admin", "mailbox", "change_mailbox"], - ["admin", "mailbox", "delete_mailbox"], - ["admin", "alias", "add_alias"], - ["admin", "alias", "change_alias"], - ["admin", "alias", "delete_alias"], - ["admin", "mailbox", "view_mailboxes"], - ["admin", "alias", "view_aliases"], - ["admin", "domainalias", "view_domainaliases"], - ["admin", "domainalias", "add_domainalias"], - ["admin", "domainalias", "change_domainalias"], - ["admin", "domainalias", "delete_domainalias"] -] - -def load_initial_data(apps, schema_editor): - """Load initial data.""" - Group = apps.get_model("auth", "Group") - dadmins = Group.objects.create(name="DomainAdmins") - add_permissions_to_group(apps, dadmins, PERMISSIONS) - Group.objects.create(name="SimpleUsers") - - -class Migration(migrations.Migration): - - dependencies = [ - ('admin', '0001_initial'), - ] - - operations = [ - migrations.RunPython(load_initial_data) - ] diff --git a/modoboa/extensions/admin/modo_extension.py b/modoboa/extensions/admin/modo_extension.py index fdbd41c80..0b5687508 100644 --- a/modoboa/extensions/admin/modo_extension.py +++ b/modoboa/extensions/admin/modo_extension.py @@ -45,6 +45,24 @@ ADMIN_EVENTS = [ "ImportObject" ] +PERMISSIONS = { + "DomainAdmins": [ + ["admin", "domain", "view_domain"], + ["admin", "mailbox", "add_mailbox"], + ["admin", "mailbox", "change_mailbox"], + ["admin", "mailbox", "delete_mailbox"], + ["admin", "alias", "add_alias"], + ["admin", "alias", "change_alias"], + ["admin", "alias", "delete_alias"], + ["admin", "mailbox", "view_mailboxes"], + ["admin", "alias", "view_aliases"], + ["admin", "domainalias", "view_domainaliases"], + ["admin", "domainalias", "add_domainalias"], + ["admin", "domainalias", "change_domainalias"], + ["admin", "domainalias", "delete_domainalias"] + ] +} + class AdminConsole(ModoExtension): name = "admin" @@ -64,6 +82,12 @@ class AdminConsole(ModoExtension): exts_pool.register_extension(AdminConsole) +@events.observe("GetExtraRolePermissions") +def extra_permissions(rolename): + """Return extra permissions for :kw:`rolename`.""" + return PERMISSIONS.get(rolename, []) + + @events.observe("ExtraUprefsRoutes") def extra_routes(): from django.conf.urls import url diff --git a/modoboa/extensions/limits/general_callbacks.py b/modoboa/extensions/limits/general_callbacks.py index 66561368d..3f207eebc 100644 --- a/modoboa/extensions/limits/general_callbacks.py +++ b/modoboa/extensions/limits/general_callbacks.py @@ -2,11 +2,25 @@ from django.utils.translation import ugettext as _ +from modoboa.core import PERMISSIONS as CORE_PERMS +from modoboa.extensions.admin.modo_extension import PERMISSIONS as ADMIN_PERMS from modoboa.extensions.limits.forms import ResourcePoolForm from modoboa.extensions.limits.models import LimitTemplates from modoboa.lib import events +PERMISSIONS = { + "Resellers": ( + CORE_PERMS.get("DomainAdmins") + + ADMIN_PERMS.get("DomainAdmins") + + [["admin", "domain", "view_domains"], + ["admin", "domain", "add_domain"], + ["admin", "domain", "change_domain"], + ["admin", "domain", "delete_domain"]] + ) +} + + @events.observe("GetExtraRoles") def get_extra_roles(user): if user.is_superuser: @@ -14,6 +28,12 @@ def get_extra_roles(user): return [] +@events.observe("GetExtraRolePermissions") +def extra_permissions(rolename): + """Return extra permissions for Resellers.""" + return PERMISSIONS.get(rolename, []) + + @events.observe("ExtraAdminContent") def display_pool_usage(user, target, currentpage): from django.template.loader import render_to_string diff --git a/modoboa/extensions/limits/migrations/0002_load_initial_data.py b/modoboa/extensions/limits/migrations/0002_load_initial_data.py index ef293c25b..eb51dcd1d 100644 --- a/modoboa/extensions/limits/migrations/0002_load_initial_data.py +++ b/modoboa/extensions/limits/migrations/0002_load_initial_data.py @@ -3,37 +3,18 @@ from __future__ import unicode_literals from django.db import models, migrations -from modoboa.core.extensions import exts_pool -from modoboa.lib.migration_tools import add_permissions_to_group -from modoboa.extensions.limits.midels import LimitTemplates +from modoboa.extensions.limits.models import LimitTemplates def load_initial_data(apps, schema_editor): """Load initial data.""" - Group = apps.get_model("auth", "Group") User = apps.get_model("core", "User") LimitsPool = apps.get_model("limits", "LimitsPool") Limit = apps.get_model("limits", "Limit") - dagrp = Group.objects.get(name="DomainAdmins") - grp = Group(name="Resellers") - grp.save() - grp.permissions.add(*dagrp.permissions.all()) - add_permissions_to_group( - apps, grp, [("admin", "domain", "view_domains"), - ("admin", "domain", "add_domain"), - ("admin", "domain", "change_domain"), - ("admin", "domain", "delete_domain")] - ) - if exts_pool.is_extension_installed( - "modoboa.extensions.postfix_relay_domains"): - from modoboa.extensions.postfix_relay_domains.modo_extension \ - import RESELLERS_PERMISSIONS - add_permissions_to_group(apps, grp, RESELLERS_PERMISSIONS) - for user in User.objects.filter(groups__name="DomainAdmins"): pool, created = LimitsPool.objects.get_or_create(user=user) - for tpl in LimitTemplates().templates(): + for tpl in LimitTemplates().templates: Limit.objects.create(name=tpl[0], pool=pool, maxvalue=0) diff --git a/modoboa/extensions/limits/modo_extension.py b/modoboa/extensions/limits/modo_extension.py index f98433e30..2f1253ee2 100644 --- a/modoboa/extensions/limits/modo_extension.py +++ b/modoboa/extensions/limits/modo_extension.py @@ -8,6 +8,7 @@ from django.utils.translation import ugettext_lazy from modoboa.core.extensions import ModoExtension, exts_pool from modoboa.lib import events, parameters +from .models import LimitsPool, Limit EVENTS = [ @@ -32,4 +33,11 @@ class Limits(ModoExtension): events.declare(EVENTS) from modoboa.extensions.limits import general_callbacks + def load_initial_data(self): + """Complete existing pools with new limits.""" + new_limits = ["relay_domains_limit", "relay_domain_aliases_limit"] + for pool in LimitsPool.objects.all(): + for lname in new_limits: + Limit.objects.get_or_create(name=lname, pool=pool, maxvalue=0) + exts_pool.register_extension(Limits) diff --git a/modoboa/extensions/postfix_relay_domains/general_callbacks.py b/modoboa/extensions/postfix_relay_domains/general_callbacks.py index 404a4013b..34422c156 100644 --- a/modoboa/extensions/postfix_relay_domains/general_callbacks.py +++ b/modoboa/extensions/postfix_relay_domains/general_callbacks.py @@ -6,8 +6,33 @@ from django.db.models import Q from django.template import Template, Context from django.utils.translation import ugettext_lazy -from .models import RelayDomain, RelayDomainAlias +from modoboa.core.extensions import exts_pool from modoboa.lib import events +from .models import RelayDomain, RelayDomainAlias + +PERMISSIONS = { + "Resellers": [ + ("postfix_relay_domains", "relaydomain", "add_relaydomain"), + ("postfix_relay_domains", "relaydomain", "change_relaydomain"), + ("postfix_relay_domains", "relaydomain", "delete_relaydomain"), + ("postfix_relay_domains", "relaydomainalias", "add_relaydomainalias"), + ("postfix_relay_domains", "relaydomainalias", + "change_relaydomainalias"), + ("postfix_relay_domains", "relaydomainalias", + "delete_relaydomainalias"), + ("postfix_relay_domains", "service", "add_service"), + ("postfix_relay_domains", "service", "change_service"), + ("postfix_relay_domains", "service", "delete_service") + ] +} + + +@events.observe("GetExtraRolePermissions") +def extra_permissions(rolename): + """Return extra permissions for Resellers.""" + if not exts_pool.is_extension_installed("modoboa.extensions.limit"): + return [] + return PERMISSIONS.get(rolename, []) @events.observe('GetStaticContent') diff --git a/modoboa/extensions/postfix_relay_domains/migrations/0002_load_initial_data.py b/modoboa/extensions/postfix_relay_domains/migrations/0002_load_initial_data.py index 4be3e1d02..cec56af50 100644 --- a/modoboa/extensions/postfix_relay_domains/migrations/0002_load_initial_data.py +++ b/modoboa/extensions/postfix_relay_domains/migrations/0002_load_initial_data.py @@ -3,11 +3,6 @@ from __future__ import unicode_literals from django.db import models, migrations -from modoboa.core.extensions import exts_pool -from modoboa.lib.migration_tools import add_permissions_to_group - -from ..modo_extension import RESELLERS_PERMISSIONS - def load_initial_data(apps, schema_editor): """Load initial data.""" @@ -15,24 +10,6 @@ def load_initial_data(apps, schema_editor): for service_name in ['relay', 'smtp']: Service.objects.get_or_create(name=service_name) - if not exts_pool.is_extension_installed("modoboa.extensions.limits"): - return - - Group = apps.get_model("auth", "Group") - LimitsPool = apps.get_model("limits", "LimitsPool") - Limit = apps.get_model("limits", "Limit") - - # Add new limits to existing pools. Set maxvalue to 0 because we - # don't want to load the parameters module here... - new_limits = ["relay_domains_limit", "relay_domain_aliases_limit"] - for pool in LimitsPool.objects.all(): - for lname in new_limits: - Limit.objects.create(name=lname, pool=pool, maxvalue=0) - - # Add new permissions to the Resellers group. - grp = Group.objects.get(name='Resellers') - add_permissions_to_group(apps, grp, RESELLERS_PERMISSIONS) - class Migration(migrations.Migration): diff --git a/modoboa/extensions/postfix_relay_domains/modo_extension.py b/modoboa/extensions/postfix_relay_domains/modo_extension.py index ed212771f..2fe412608 100644 --- a/modoboa/extensions/postfix_relay_domains/modo_extension.py +++ b/modoboa/extensions/postfix_relay_domains/modo_extension.py @@ -18,18 +18,6 @@ EXTENSION_EVENTS = [ "FillRelayDomainInstances" ] -RESELLERS_PERMISSIONS = [ - ("postfix_relay_domains", "relaydomain", "add_relaydomain"), - ("postfix_relay_domains", "relaydomain", "change_relaydomain"), - ("postfix_relay_domains", "relaydomain", "delete_relaydomain"), - ("postfix_relay_domains", "relaydomainalias", "add_relaydomainalias"), - ("postfix_relay_domains", "relaydomainalias", "change_relaydomainalias"), - ("postfix_relay_domains", "relaydomainalias", "delete_relaydomainalias"), - ("postfix_relay_domains", "service", "add_service"), - ("postfix_relay_domains", "service", "change_service"), - ("postfix_relay_domains", "service", "delete_service") -] - class PostfixRelayDomains(ModoExtension): diff --git a/modoboa/extensions/radicale/general_callbacks.py b/modoboa/extensions/radicale/general_callbacks.py index 4688f748a..9cfb78938 100644 --- a/modoboa/extensions/radicale/general_callbacks.py +++ b/modoboa/extensions/radicale/general_callbacks.py @@ -6,6 +6,26 @@ from django.core.urlresolvers import reverse from django.utils.translation import ugettext as _ from modoboa.lib import events +PERMISSIONS = [ + ("radicale", "usercalendar", "add_usercalendar"), + ("radicale", "usercalendar", "change_usercalendar"), + ("radicale", "usercalendar", "delete_usercalendar"), + ("radicale", "sharedcalendar", "add_sharedcalendar"), + ("radicale", "sharedcalendar", "change_sharedcalendar"), + ("radicale", "sharedcalendar", "delete_sharedcalendar") +] + +ROLES_PERMISSIONS = { + "DomainAdmins": PERMISSIONS, + "Resellers": PERMISSIONS +} + + +@events.observe("GetExtraRolePermissions") +def extra_permissions(rolename): + """Extra permissions.""" + return ROLES_PERMISSIONS.get(rolename, []) + @events.observe("UserMenuDisplay") def top_menu(target, user): diff --git a/modoboa/extensions/radicale/migrations/0002_load_initial_data.py b/modoboa/extensions/radicale/migrations/0002_load_initial_data.py deleted file mode 100644 index b21ff3896..000000000 --- a/modoboa/extensions/radicale/migrations/0002_load_initial_data.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - -from modoboa.core.extensions import exts_pool -from modoboa.lib.migration_tools import add_permissions_to_group - - -def load_initial_data(apps, schema_editor): - """Load initial data.""" - - permissions = [ - ("radicale", "usercalendar", "add_usercalendar"), - ("radicale", "usercalendar", "change_usercalendar"), - ("radicale", "usercalendar", "delete_usercalendar"), - ("radicale", "sharedcalendar", "add_sharedcalendar"), - ("radicale", "sharedcalendar", "change_sharedcalendar"), - ("radicale", "sharedcalendar", "delete_sharedcalendar") - ] - - add_permissions_to_group(apps, "DomainAdmins", permissions) - if not exts_pool.is_extension_installed( - "modoboa.extensions.limits"): - return - add_permissions_to_group(apps, "Resellers", permissions) - - -class Migration(migrations.Migration): - - dependencies = [ - ('radicale', '0001_initial'), - ] - - operations = [ - migrations.RunPython(load_initial_data) - ] diff --git a/modoboa/lib/migration_tools.py b/modoboa/lib/migration_tools.py deleted file mode 100644 index 307acad6f..000000000 --- a/modoboa/lib/migration_tools.py +++ /dev/null @@ -1,18 +0,0 @@ -"""Additional functions available for migration scripts.""" - - -def add_permissions_to_group(apps, group, permissions): - """Add the specified permissions to a django group.""" - Permission = apps.get_model("auth", "Permission") - ContentType = apps.get_model("contenttypes", "ContentType") - - if isinstance(group, basestring): - Group = apps.get_model("auth", "Group") - group = Group.objects.get(name=group) - - for appname, modelname, permname in permissions: - ct = ContentType.objects.get_by_natural_key( - appname, modelname) - group.permissions.add( - Permission.objects.get(content_type=ct, codename=permname) - ) diff --git a/modoboa/lib/permissions.py b/modoboa/lib/permissions.py index 5558bc3e6..661db46fb 100644 --- a/modoboa/lib/permissions.py +++ b/modoboa/lib/permissions.py @@ -1,5 +1,6 @@ # coding: utf-8 +from django.contrib.auth.models import Group, Permission from django.contrib.contenttypes.models import ContentType from django.db import IntegrityError from django.utils.translation import ugettext as _ @@ -147,3 +148,18 @@ def get_object_owner(obj): except ObjectAccess.DoesNotExist: return None return entry.user + + +def add_permissions_to_group(group, permissions): + """Add the specified permissions to a django group.""" + if isinstance(group, basestring): + group = Group.objects.get(name=group) + + for appname, modelname, permname in permissions: + ct = ContentType.objects.get_by_natural_key(appname, modelname) + if group.permissions.filter( + content_type=ct, codename=permname).exists(): + continue + group.permissions.add( + Permission.objects.get(content_type=ct, codename=permname) + )