diff --git a/TODO.md b/TODO.md index 9ff14ead..5bd5b8ca 100644 --- a/TODO.md +++ b/TODO.md @@ -75,26 +75,19 @@ * Databases.User add reverse M2M databases widget (like mailbox.addresses) -* reconsider binding webapps to systemusers (pangea multiple users wordpress-ftp, moodle-pangea, etc) -* Secondary user home in /home/secondaryuser and simlink to /home/main/webapps/app so it can have private storage? -* Grant permissions to systemusers, the problem of creating a related permission model is out of sync with the server-side. evaluate tradeoff +* Grant permissions to systemusers * Make one dedicated CGI user for each account only for CGI execution (fpm/fcgid). Different from the files owner, and without W permissions, so attackers can not inject backdors and malware. -* In most cases we can prevent the creation of files for the CGI users, preventing attackers to upload and executing PHPShells. -* Make main systemuser able to write/read everything on its home, including stuff created by the CGI user and secondary users -* Prevent users from accessing other users home while at the same time allow access Apache/fcgid/fpm and secondary users (x) * resource min max allocation with validation -* mailman needs both aliases when address_name is provided (default messages and bounces and all) - * domain validation parse named-checzone output to assign errors to fields * Directory Protection on webapp and use webapp path as base path (validate) * validate systemuser.home on server-side -* webapp backend option compatibility check? +* webapp backend option compatibility check? raise exception, missconfigured error * admin systemuser home/directory, add default home and empty directory with has_shell on admin @@ -116,8 +109,6 @@ ln -s /proc/self/fd /dev/fd -* escape passwords and not allow ' on them ! - POST INSTALL ------------ @@ -155,8 +146,6 @@ Php binaries should have this format: /usr/bin/php5.2-cgi * tags = GenericRelation(TaggedItem, related_query_name='bookmarks') -# make home for all systemusers (/home/username) and fix monitors - * user provided crons * ``` 0 if failure: failing_cmd || exit_code=1 and don't forget to call super.commit()!! * website directives uniquenes validation on serializers + is_Active custom filter with support for instance.account.is_Active annotate with F() needed (django 1.8) -* delete apache logs and php logs +# delete apache logs and php logs * document service help things: discount/refound/compensation effect and metric table * Document metric interpretation help_text @@ -218,6 +205,7 @@ require_once(‘/etc/moodles/’.$moodle_host.‘config.php’);``` moodle/drupl * write down insights # use english on services defs and so on, an translate them on render time + python3 manage.py dumpdata services.Service --indent 4 --natural * websites directives get_location() and use it on last change view validation stage to compare with contents.location and also on the backend ? @@ -229,8 +217,6 @@ require_once(‘/etc/moodles/’.$moodle_host.‘config.php’);``` moodle/drupl # TDOO Base price: domini propi (all domains) + extra for other domains -# TODO prepend ORCHESTRA_ to orchestra/settings.py - Translation ----------- @@ -260,16 +246,12 @@ celery max-tasks-per-child * postupgradeorchestra send signals in order to hook custom stuff -# FIXME make base home for systemusers that ara homed into main account systemuser, and prevent shell users to have nested homes (if nnot implemented already) - * autoscale celery workers http://docs.celeryproject.org/en/latest/userguide/workers.html#autoscaling * webapp has_website list filter glic3rinu's django-fluent-dashboard * gevent is not ported to python3 :'( -* uwsgi python3 - # FIXME account deletion generates an integrity error https://code.djangoproject.com/ticket/24576 @@ -289,11 +271,10 @@ https://code.djangoproject.com/ticket/24576 # FIXME model contact info and account info (email, name, etc) correctly/unredundant/dry - * Use the new django.contrib.admin.RelatedOnlyFieldListFilter in ModelAdmin.list_filter to limit the list_filter choices to foreign objects which are attached to those from the ModelAdmin. + Query Expressions, Conditional Expressions, and Database Functions¶ * forms: You can now pass a callable that returns an iterable of choices when instantiating a ChoiceField. - * migrate to DRF3.x +* move all tests on django-orchestra/tests diff --git a/orchestra/api/options.py b/orchestra/api/options.py index 08c59c03..070a42bb 100644 --- a/orchestra/api/options.py +++ b/orchestra/api/options.py @@ -56,7 +56,7 @@ class LogApiMixin(object): class LinkHeaderRouter(DefaultRouter): def get_api_root_view(self): """ returns the root view, with all the linked collections """ - APIRoot = import_class(settings.API_ROOT_VIEW) + APIRoot = import_class(settings.ORCHESTRA_API_ROOT_VIEW) APIRoot.router = self return APIRoot.as_view() diff --git a/orchestra/api/root.py b/orchestra/api/root.py index 5517fe9e..7fef2313 100644 --- a/orchestra/api/root.py +++ b/orchestra/api/root.py @@ -7,7 +7,10 @@ from ..core import services, accounts class APIRoot(views.APIView): - names = ['SITE_NAME', 'SITE_VERBOSE_NAME'] + names = ( + 'ORCHESTRA_SITE_NAME', + 'ORCHESTRA_SITE_VERBOSE_NAME' + ) def get(self, request, format=None): root_url = reverse('api-root', request=request, format=format) diff --git a/orchestra/bin/orchestra-admin b/orchestra/bin/orchestra-admin index 74be3ae2..d4daee0b 100755 --- a/orchestra/bin/orchestra-admin +++ b/orchestra/bin/orchestra-admin @@ -137,7 +137,7 @@ function install_requirements () { PIP="django==1.8 \ django-celery-email==1.0.4 \ - django-fluent-dashboard==0.4 \ + https://github.com/glic3rinu/django-fluent-dashboard/archive/master.zip \ https://bitbucket.org/izi/django-admin-tools/get/a0abfffd76a0.zip \ IPy==0.81 \ django-extensions==1.5.2 \ diff --git a/orchestra/conf/project_template/project_name/settings.py b/orchestra/conf/project_template/project_name/settings.py index 8a4a06ed..20dee7c2 100644 --- a/orchestra/conf/project_template/project_name/settings.py +++ b/orchestra/conf/project_template/project_name/settings.py @@ -68,5 +68,5 @@ LOCALE_PATHS = ( # DEFAULT_FROM_EMAIL = 'orchestra@yourhost.eu' -SITE_NAME = '{{ project_name }}' +ORCHESTRA_SITE_NAME = '{{ project_name }}' diff --git a/orchestra/contrib/accounts/settings.py b/orchestra/contrib/accounts/settings.py index b5d380f5..67781676 100644 --- a/orchestra/contrib/accounts/settings.py +++ b/orchestra/contrib/accounts/settings.py @@ -1,7 +1,7 @@ from django.conf import settings from django.utils.translation import ugettext_lazy as _ -from orchestra.settings import BASE_DOMAIN +from orchestra.settings import ORCHESTRA_BASE_DOMAIN ACCOUNTS_TYPES = getattr(settings, 'ACCOUNTS_TYPES', ( @@ -53,9 +53,9 @@ ACCOUNTS_CREATE_RELATED = getattr(settings, 'ACCOUNTS_CREATE_RELATED', ( ('domains.Domain', 'name', { - 'name': '"%s.{}" % account.username.replace("_", "-")'.format(BASE_DOMAIN), + 'name': '"%s.{}" % account.username.replace("_", "-")'.format(ORCHESTRA_BASE_DOMAIN), }, - _("Designates whether to creates a related subdomain <username>.{} or not.".format(BASE_DOMAIN)), + _("Designates whether to creates a related subdomain <username>.{} or not.".format(ORCHESTRA_BASE_DOMAIN)), ), )) diff --git a/orchestra/contrib/bills/settings.py b/orchestra/contrib/bills/settings.py index c6696089..76dbef13 100644 --- a/orchestra/contrib/bills/settings.py +++ b/orchestra/contrib/bills/settings.py @@ -1,7 +1,7 @@ from django.conf import settings from django_countries import data -from orchestra.settings import BASE_DOMAIN +from orchestra.settings import ORCHESTRA_BASE_DOMAIN BILLS_NUMBER_LENGTH = getattr(settings, 'BILLS_NUMBER_LENGTH', @@ -60,12 +60,12 @@ BILLS_SELLER_PHONE = getattr(settings, 'BILLS_SELLER_PHONE', BILLS_SELLER_EMAIL = getattr(settings, 'BILLS_SELLER_EMAIL', - 'sales@{}'.format(BASE_DOMAIN) + 'sales@{}'.format(ORCHESTRA_BASE_DOMAIN) ) BILLS_SELLER_WEBSITE = getattr(settings, 'BILLS_SELLER_WEBSITE', - 'www.{}'.format(BASE_DOMAIN) + 'www.{}'.format(ORCHESTRA_BASE_DOMAIN) ) diff --git a/orchestra/contrib/domains/settings.py b/orchestra/contrib/domains/settings.py index 094ff445..6ce7037d 100644 --- a/orchestra/contrib/domains/settings.py +++ b/orchestra/contrib/domains/settings.py @@ -1,15 +1,15 @@ from django.conf import settings -from orchestra.settings import BASE_DOMAIN +from orchestra.settings import ORCHESTRA_BASE_DOMAIN DOMAINS_DEFAULT_NAME_SERVER = getattr(settings, 'DOMAINS_DEFAULT_NAME_SERVER', - 'ns.{}'.format(BASE_DOMAIN) + 'ns.{}'.format(ORCHESTRA_BASE_DOMAIN) ) DOMAINS_DEFAULT_HOSTMASTER = getattr(settings, 'DOMAINS_DEFAULT_HOSTMASTER', - 'hostmaster@{}'.format(BASE_DOMAIN) + 'hostmaster@{}'.format(ORCHESTRA_BASE_DOMAIN) ) @@ -70,14 +70,14 @@ DOMAINS_DEFAULT_A = getattr(settings, 'DOMAINS_DEFAULT_A', DOMAINS_DEFAULT_MX = getattr(settings, 'DOMAINS_DEFAULT_MX', ( - '10 mail.{}.'.format(BASE_DOMAIN), - '10 mail2.{}.'.format(BASE_DOMAIN), + '10 mail.{}.'.format(ORCHESTRA_BASE_DOMAIN), + '10 mail2.{}.'.format(ORCHESTRA_BASE_DOMAIN), )) DOMAINS_DEFAULT_NS = getattr(settings, 'DOMAINS_DEFAULT_NS', ( - 'ns1.{}.'.format(BASE_DOMAIN), - 'ns2.{}.'.format(BASE_DOMAIN), + 'ns1.{}.'.format(ORCHESTRA_BASE_DOMAIN), + 'ns2.{}.'.format(ORCHESTRA_BASE_DOMAIN), )) diff --git a/orchestra/contrib/lists/settings.py b/orchestra/contrib/lists/settings.py index e3a89e9f..472f808b 100644 --- a/orchestra/contrib/lists/settings.py +++ b/orchestra/contrib/lists/settings.py @@ -1,6 +1,6 @@ from django.conf import settings -from orchestra.settings import BASE_DOMAIN +from orchestra.settings import ORCHESTRA_BASE_DOMAIN LISTS_DOMAIN_MODEL = getattr(settings, 'LISTS_DOMAIN_MODEL', @@ -9,12 +9,12 @@ LISTS_DOMAIN_MODEL = getattr(settings, 'LISTS_DOMAIN_MODEL', LISTS_DEFAULT_DOMAIN = getattr(settings, 'LIST_DEFAULT_DOMAIN', - 'lists.{}'.format(BASE_DOMAIN) + 'lists.{}'.format(ORCHESTRA_BASE_DOMAIN) ) LISTS_LIST_URL = getattr(settings, 'LISTS_LIST_URL', - 'https://lists.{}/mailman/listinfo/%(name)s'.format(BASE_DOMAIN) + 'https://lists.{}/mailman/listinfo/%(name)s'.format(ORCHESTRA_BASE_DOMAIN) ) diff --git a/orchestra/contrib/mailboxes/settings.py b/orchestra/contrib/mailboxes/settings.py index 2717a1a3..bd40ad11 100644 --- a/orchestra/contrib/mailboxes/settings.py +++ b/orchestra/contrib/mailboxes/settings.py @@ -4,7 +4,7 @@ import textwrap from django.conf import settings from django.utils.translation import ugettext_lazy as _ -from orchestra.settings import BASE_DOMAIN +from orchestra.settings import ORCHESTRA_BASE_DOMAIN MAILBOXES_DOMAIN_MODEL = getattr(settings, 'MAILBOXES_DOMAIN_MODEL', @@ -48,7 +48,7 @@ MAILBOXES_VIRTUAL_ALIAS_DOMAINS_PATH = getattr(settings, 'MAILBOXES_VIRTUAL_ALIA MAILBOXES_VIRTUAL_MAILBOX_DEFAULT_DOMAIN = getattr(settings, 'MAILBOXES_VIRTUAL_MAILBOX_DEFAULT_DOMAIN', - BASE_DOMAIN + ORCHESTRA_BASE_DOMAIN ) @@ -87,7 +87,7 @@ MAILBOXES_MAILDIRSIZE_PATH = getattr(settings, 'MAILBOXES_MAILDIRSIZE_PATH', MAILBOXES_LOCAL_ADDRESS_DOMAIN = getattr(settings, 'MAILBOXES_LOCAL_ADDRESS_DOMAIN', - BASE_DOMAIN + ORCHESTRA_BASE_DOMAIN ) diff --git a/orchestra/contrib/orchestration/backends.py b/orchestra/contrib/orchestration/backends.py index 8aba5959..1867f5c5 100644 --- a/orchestra/contrib/orchestration/backends.py +++ b/orchestra/contrib/orchestration/backends.py @@ -11,8 +11,7 @@ from . import methods def replace(context, pattern, repl): - if isinstance(context, str): - return context.replace(patter, repl) + """ applies replace to all context str values """ for key, value in context.items(): if isinstance(value, str): context[key] = value.replace(pattern, repl) @@ -23,7 +22,7 @@ class ServiceMount(plugins.PluginMount): def __init__(cls, name, bases, attrs): # Make sure backends specify a model attribute if not (attrs.get('abstract', False) or name == 'ServiceBackend' or cls.model): - raise AttributeError("'%s' does not have a defined model attribute." % cls) + raise AttributeError("'%s' does not have a defined model attribute." % cls) super(ServiceMount, cls).__init__(name, bases, attrs) @@ -36,15 +35,16 @@ class ServiceBackend(plugins.Plugin, metaclass=ServiceMount): the changes of all modified objects, reloading the daemon just once. """ model = None - related_models = () # ((model, accessor__attribute),) + related_models = () # ((model, accessor__attribute),) script_method = methods.SSH script_executable = '/bin/bash' function_method = methods.Python - type = 'task' # 'sync' + type = 'task' # 'sync' ignore_fields = [] actions = [] default_route_match = 'True' - block = False # Force the backend manager to block in multiple backend executions and execute them synchronously + # Force the backend manager to block in multiple backend executions executing them synchronously + block = False def __str__(self): return type(self).__name__ diff --git a/orchestra/contrib/orders/admin.py b/orchestra/contrib/orders/admin.py index c20e41de..07667e97 100644 --- a/orchestra/contrib/orders/admin.py +++ b/orchestra/contrib/orders/admin.py @@ -1,5 +1,6 @@ from django.contrib import admin from django.core.urlresolvers import reverse +from django.db.models import Prefetch from django.utils import timezone from django.utils.html import escape from django.utils.safestring import mark_safe @@ -17,7 +18,7 @@ from .models import Order, MetricStorage class MetricStorageInline(admin.TabularInline): model = MetricStorage - readonly_fields = ('value', 'updated_on') + readonly_fields = ('value', 'created_on', 'updated_on') extra = 0 def has_add_permission(self, request, obj=None): @@ -33,10 +34,11 @@ class MetricStorageInline(admin.TabularInline): def get_queryset(self, request): qs = super(MetricStorageInline, self).get_queryset(request) - if self.parent_object and self.parent_object.pk: - qs = qs.filter(order=self.parent_object.pk).order_by('-id') + change_view = bool(self.parent_object and self.parent_object.pk) + if change_view: + qs = qs.order_by('-id') try: - tenth_id = qs.values_list('id', flat=True)[10] + tenth_id = qs.values_list('id', flat=True)[9] except IndexError: pass else: @@ -59,7 +61,10 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin): inlines = (MetricStorageInline,) add_inlines = () search_fields = ('account__username', 'description') - list_prefetch_related = ('metrics', 'content_object') + list_prefetch_related = ( + 'content_object', + Prefetch('metrics', queryset=MetricStorage.objects.order_by('-id')), + ) list_select_related = ('account', 'service') service_link = admin_link('service') diff --git a/orchestra/contrib/orders/models.py b/orchestra/contrib/orders/models.py index b7ac4af5..993607bf 100644 --- a/orchestra/contrib/orders/models.py +++ b/orchestra/contrib/orders/models.py @@ -178,7 +178,7 @@ class Order(models.Model): metric = ', metric:{}'.format(metric) description = handler.get_order_description(instance) logger.info("UPDATED order id:{id}, description:{description}{metric}".format( - id=self.id, description=description, metric=metric).encode('ascii', 'ignore') + id=self.id, description=description, metric=metric).encode('ascii', 'replace') ) if self.description != description: self.description = description diff --git a/orchestra/contrib/saas/settings.py b/orchestra/contrib/saas/settings.py index 0862aa36..132001a2 100644 --- a/orchestra/contrib/saas/settings.py +++ b/orchestra/contrib/saas/settings.py @@ -1,6 +1,6 @@ from django.conf import settings -from orchestra.settings import BASE_DOMAIN +from orchestra.settings import ORCHESTRA_BASE_DOMAIN SAAS_ENABLED_SERVICES = getattr(settings, 'SAAS_ENABLED_SERVICES', ( @@ -21,7 +21,7 @@ SAAS_WORDPRESS_ADMIN_PASSWORD = getattr(settings, 'SAAS_WORDPRESSMU_ADMIN_PASSWO SAAS_WORDPRESS_BASE_URL = getattr(settings, 'SAAS_WORDPRESS_BASE_URL', - 'http://blogs.{}/'.format(BASE_DOMAIN) + 'http://blogs.{}/'.format(ORCHESTRA_BASE_DOMAIN) ) @@ -46,12 +46,12 @@ SAAS_PHPLIST_DB_NAME = getattr(settings, 'SAAS_PHPLIST_DB_NAME', SAAS_PHPLIST_BASE_DOMAIN = getattr(settings, 'SAAS_PHPLIST_BASE_DOMAIN', - 'lists.{}'.format(BASE_DOMAIN) + 'lists.{}'.format(ORCHESTRA_BASE_DOMAIN) ) SAAS_SEAFILE_DOMAIN = getattr(settings, 'SAAS_SEAFILE_DOMAIN', - 'seafile.{}'.format(BASE_DOMAIN) + 'seafile.{}'.format(ORCHESTRA_BASE_DOMAIN) ) @@ -61,7 +61,7 @@ SAAS_SEAFILE_DEFAULT_QUOTA = getattr(settings, 'SAAS_SEAFILE_DEFAULT_QUOTA', SAAS_BSCW_DOMAIN = getattr(settings, 'SAAS_BSCW_DOMAIN', - 'bscw.{}'.format(BASE_DOMAIN) + 'bscw.{}'.format(ORCHESTRA_BASE_DOMAIN) ) @@ -80,6 +80,6 @@ SAAS_GITLAB_ROOT_PASSWORD = getattr(settings, 'SAAS_GITLAB_ROOT_PASSWORD', SAAS_GITLAB_DOMAIN = getattr(settings, 'SAAS_GITLAB_DOMAIN', - 'gitlab.{}'.format(BASE_DOMAIN) + 'gitlab.{}'.format(ORCHESTRA_BASE_DOMAIN) ) diff --git a/orchestra/contrib/services/handlers.py b/orchestra/contrib/services/handlers.py index 5ea70bd1..35137f26 100644 --- a/orchestra/contrib/services/handlers.py +++ b/orchestra/contrib/services/handlers.py @@ -504,7 +504,7 @@ class ServiceHandler(plugins.Plugin, metaclass=plugins.PluginMount): new_price += self.get_price(order, metric) * size new_metric += metric size = self.get_price_size(rini, bp) - old_price = self.get_price(order, charged) * size + old_price = self.get_price(account, charged) * size if new_price > old_price: metric = new_metric - charged price = new_price - old_price @@ -520,7 +520,7 @@ class ServiceHandler(plugins.Plugin, metaclass=plugins.PluginMount): if self.get_pricing_period() == self.NEVER: # Changes (Mailbox disk-like) for cini, cend, metric in order.get_metric(ini, bp, changes=True): - price = self.get_price(order, metric) + price = self.get_price(account, metric) lines.append(self.generate_line(order, price, cini, cend, metric=metric)) elif self.get_pricing_period() == self.billing_period: # pricing_slots (Traffic-like) @@ -528,7 +528,7 @@ class ServiceHandler(plugins.Plugin, metaclass=plugins.PluginMount): raise NotImplementedError for cini, cend in self.get_pricing_slots(ini, bp): metric = order.get_metric(cini, cend) - price = self.get_price(order, metric) + price = self.get_price(account, metric) lines.append(self.generate_line(order, price, cini, cend, metric=metric)) else: raise NotImplementedError @@ -541,7 +541,7 @@ class ServiceHandler(plugins.Plugin, metaclass=plugins.PluginMount): if self.get_pricing_period() == self.NEVER: # get metric (Job-like) metric = order.get_metric(date) - price = self.get_price(order, metric) + price = self.get_price(account, metric) lines.append(self.generate_line(order, price, date, metric=metric)) else: raise NotImplementedError diff --git a/orchestra/contrib/systemusers/backends.py b/orchestra/contrib/systemusers/backends.py index 98f3bcbe..ef1db43d 100644 --- a/orchestra/contrib/systemusers/backends.py +++ b/orchestra/contrib/systemusers/backends.py @@ -31,6 +31,12 @@ class UNIXUserBackend(ServiceController): chmod 750 %(home)s chown %(user)s:%(user)s %(home)s""") % context ) + if context['home'] != context['base_home']: + self.append(textwrap.dedent(""" + mkdir -p %(base_home)s + chmod 750 %(base_home)s + chown %(user)s:%(user)s %(base_home)s""") % context + ) for member in settings.SYSTEMUSERS_DEFAULT_GROUP_MEMBERS: context['member'] = member self.append('usermod -a -G %(user)s %(member)s' % context) @@ -45,19 +51,15 @@ class UNIXUserBackend(ServiceController): { sleep 2 && killall -u %(user)s -s KILL; } & killall -u %(user)s || true userdel %(user)s || exit_code=1 - groupdel %(group)s || exit_code=1""") % context + groupdel %(group)s || exit_code=1 + mv %(base_home)s %(base_home)s.deleted || exit_code=1 + """) % context ) - self.delete_home(context, user) def grant_permission(self, user): context = self.get_context(user) # TODO setacl - def delete_home(self, context, user): - if user.home.rstrip('/') == user.get_base_home().rstrip('/'): - # TODO delete instead of this shit - self.append("mv %(home)s %(home)s.deleted || exit_code=1" % context) - def get_groups(self, user): if user.is_main: return user.account.systemusers.exclude(username=user.username).values_list('username', flat=True) @@ -71,7 +73,8 @@ class UNIXUserBackend(ServiceController): 'password': user.password if user.active else '*%s' % user.password, 'shell': user.shell, 'mainuser': user.username if user.is_main else user.account.username, - 'home': user.get_home() + 'home': user.get_home(), + 'base_home': self.get_base_home(), } return replace(context, "'", '"') @@ -91,16 +94,12 @@ class UNIXUserDisk(ServiceMonitor): def monitor(self, user): context = self.get_context(user) - if user.is_main or os.path.normpath(user.home) == user.get_base_home(): - self.append("echo %(object_id)s $(monitor %(home)s)" % context) - else: - # Home is already included in other user home - self.append("echo %(object_id)s 0" % context) + self.append("echo %(object_id)s $(monitor %(base_home)s)" % context) def get_context(self, user): context = { 'object_id': user.pk, - 'home': user.home, + 'base_home': user.get_base_home(), } return replace(context, "'", '"') diff --git a/orchestra/contrib/systemusers/forms.py b/orchestra/contrib/systemusers/forms.py index c86ae3c4..84131d64 100644 --- a/orchestra/contrib/systemusers/forms.py +++ b/orchestra/contrib/systemusers/forms.py @@ -6,6 +6,7 @@ from orchestra.forms import UserCreationForm, UserChangeForm from . import settings from .models import SystemUser +from .validators import validate_home class SystemUserFormMixin(object): @@ -63,7 +64,7 @@ class SystemUserFormMixin(object): if home and self.MOCK_USERNAME in home: username = self.cleaned_data.get('username', '') self.cleaned_data['home'] = home.replace(self.MOCK_USERNAME, username) - self.instance.validate_home(self.cleaned_data, self.account) + validate_home(self.instance, self.cleaned_data, self.account) class SystemUserCreationForm(SystemUserFormMixin, UserCreationForm): diff --git a/orchestra/contrib/systemusers/models.py b/orchestra/contrib/systemusers/models.py index 3fb40a39..d483b9a1 100644 --- a/orchestra/contrib/systemusers/models.py +++ b/orchestra/contrib/systemusers/models.py @@ -92,29 +92,10 @@ class SystemUser(models.Model): raise ValidationError({ 'directory': directory_error, }) - - def validate_home(self, data, account): - """ validates home based on account and data['shell'] """ - if not 'username' in data and not self.pk: - # other validation will have been raised for required username - return - user = type(self)( - username=data.get('username') or self.username, - shell=data.get('shell') or self.shell, - ) - if 'home' in data and data['home']: - home = os.path.normpath(data['home']) - user_home = user.get_base_home() - account_home = account.main_systemuser.get_home() - if user.has_shell: - if home != user_home: - raise ValidationError({ - 'home': _("Not a valid home directory.") - }) - elif home not in (user_home, account_home): - raise ValidationError({ - 'home': _("Not a valid home directory.") - }) + if self.has_shell and self.home != self.get_base_home(): + raise ValidationError({ + 'home': _("Shell users should use their own home."), + }) def set_password(self, raw_password): self.password = make_password(raw_password) diff --git a/orchestra/contrib/systemusers/serializers.py b/orchestra/contrib/systemusers/serializers.py index 72778ad6..f0ae242b 100644 --- a/orchestra/contrib/systemusers/serializers.py +++ b/orchestra/contrib/systemusers/serializers.py @@ -8,6 +8,7 @@ from orchestra.contrib.accounts.serializers import AccountSerializerMixin from orchestra.core.validators import validate_password from .models import SystemUser +from .validators import validate_home class GroupSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer): @@ -38,7 +39,7 @@ class SystemUserSerializer(AccountSerializerMixin, HyperlinkedModelSerializer): username=attrs.get('username') or self.object.username, shell=attrs.get('shell') or self.object.shell, ) - user.validate_home(attrs, self.account) + validate_home(user, attrs, self.account) return attrs def validate_password(self, attrs, source): diff --git a/orchestra/contrib/systemusers/validators.py b/orchestra/contrib/systemusers/validators.py new file mode 100644 index 00000000..54670cbe --- /dev/null +++ b/orchestra/contrib/systemusers/validators.py @@ -0,0 +1,27 @@ +import os + +from django.core.exceptions import ValidationError + + +def validate_home(user, data, account): + """ validates home based on account and data['shell'] """ + if not 'username' in data and not user.pk: + # other validation will have been raised for required username + return + user = type(user)( + username=data.get('username') or user.username, + shell=data.get('shell') or user.shell, + ) + if 'home' in data and data['home']: + home = os.path.normpath(data['home']) + user_home = user.get_base_home() + account_home = account.main_systemuser.get_home() + if user.has_shell: + if home != user_home: + raise ValidationError({ + 'home': _("Not a valid home directory.") + }) + elif home not in (user_home, account_home): + raise ValidationError({ + 'home': _("Not a valid home directory.") + }) diff --git a/orchestra/contrib/webapps/settings.py b/orchestra/contrib/webapps/settings.py index 97b7d792..a68c342a 100644 --- a/orchestra/contrib/webapps/settings.py +++ b/orchestra/contrib/webapps/settings.py @@ -1,6 +1,6 @@ from django.conf import settings -from orchestra.settings import BASE_DOMAIN +from orchestra.settings import ORCHESTRA_BASE_DOMAIN WEBAPPS_BASE_ROOT = getattr(settings, 'WEBAPPS_BASE_ROOT', @@ -169,5 +169,5 @@ WEBAPPS_ENABLED_OPTIONS = getattr(settings, 'WEBAPPS_ENABLED_OPTIONS', ( WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST = getattr(settings, 'WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST', - 'mysql.{}'.format(BASE_DOMAIN) + 'mysql.{}'.format(ORCHESTRA_BASE_DOMAIN) ) diff --git a/orchestra/core/context_processors.py b/orchestra/core/context_processors.py index 56a9c80b..6080166b 100644 --- a/orchestra/core/context_processors.py +++ b/orchestra/core/context_processors.py @@ -4,7 +4,7 @@ from orchestra import settings def site(request): """ Adds site-related context variables to the context """ return { - 'SITE_NAME': settings.SITE_NAME, - 'SITE_VERBOSE_NAME': settings.SITE_VERBOSE_NAME + 'SITE_NAME': settings.ORCHESTRA_SITE_NAME, + 'SITE_VERBOSE_NAME': settings.ORCHESTRA_SITE_VERBOSE_NAME } diff --git a/orchestra/management/commands/restartservices.py b/orchestra/management/commands/restartservices.py index 50c0c91c..9c17d59b 100644 --- a/orchestra/management/commands/restartservices.py +++ b/orchestra/management/commands/restartservices.py @@ -1,11 +1,11 @@ from django.core.management.base import BaseCommand from orchestra.management.commands.startservices import ManageServiceCommand -from orchestra.settings import RESTART_SERVICES +from orchestra.settings import ORCHESTRA_RESTART_SERVICES class Command(ManageServiceCommand): - services = RESTART_SERVICES + services = ORCHESTRA_RESTART_SERVICES action = 'restart' option_list = BaseCommand.option_list help = 'Restart all related services. Usefull for reload configuration and files.' diff --git a/orchestra/management/commands/startservices.py b/orchestra/management/commands/startservices.py index 5bb6f811..be97e5b2 100644 --- a/orchestra/management/commands/startservices.py +++ b/orchestra/management/commands/startservices.py @@ -2,7 +2,7 @@ from optparse import make_option from django.core.management.base import BaseCommand -from orchestra.settings import START_SERVICES +from orchestra.settings import ORCHESTRA_START_SERVICES from orchestra.utils.sys import run, check_root @@ -53,7 +53,7 @@ class ManageServiceCommand(BaseCommand): class Command(ManageServiceCommand): - services = START_SERVICES + services = ORCHESTRA_START_SERVICES action = 'start' option_list = BaseCommand.option_list help = 'Start all related services. Usefull for reload configuration and files.' diff --git a/orchestra/management/commands/stopservices.py b/orchestra/management/commands/stopservices.py index 14bf0edc..67c026ad 100644 --- a/orchestra/management/commands/stopservices.py +++ b/orchestra/management/commands/stopservices.py @@ -1,11 +1,11 @@ from django.core.management.base import BaseCommand from orchestra.management.commands.startservices import ManageServiceCommand -from orchestra.settings import STOP_SERVICES +from orchestra.settings import ORCHESTRA_STOP_SERVICES class Command(ManageServiceCommand): - services = STOP_SERVICES + services = ORCHESTRA_STOP_SERVICES action = 'stop' option_list = BaseCommand.option_list help = 'Stop all related services. Usefull for reload configuration and files.' diff --git a/orchestra/settings.py b/orchestra/settings.py index b1c67efc..ad0b32b5 100644 --- a/orchestra/settings.py +++ b/orchestra/settings.py @@ -4,27 +4,27 @@ from django.utils.translation import ugettext_lazy as _ # Domain name used when it will not be possible to infere the domain from a request # For example in periodic tasks -SITE_URL = getattr(settings, 'SITE_URL', +ORCHESTRA_SITE_URL = getattr(settings, 'ORCHESTRA_SITE_URL', 'http://localhost' ) -SITE_NAME = getattr(settings, 'SITE_NAME', +ORCHESTRA_SITE_NAME = getattr(settings, 'ORCHESTRA_SITE_NAME', 'orchestra' ) -SITE_VERBOSE_NAME = getattr(settings, 'SITE_VERBOSE_NAME', - _("%s Hosting Management" % SITE_NAME.capitalize()) +ORCHESTRA_SITE_VERBOSE_NAME = getattr(settings, 'ORCHESTRA_SITE_VERBOSE_NAME', + _("%s Hosting Management" % ORCHESTRA_SITE_NAME.capitalize()) ) -BASE_DOMAIN = getattr(settings, 'BASE_DOMAIN', +ORCHESTRA_BASE_DOMAIN = getattr(settings, 'ORCHESTRA_BASE_DOMAIN', 'orchestra.lan' ) # Service management commands -START_SERVICES = getattr(settings, 'START_SERVICES', [ +ORCHESTRA_START_SERVICES = getattr(settings, 'ORCHESTRA_START_SERVICES', [ 'postgresql', 'celeryevcam', 'celeryd', @@ -33,13 +33,13 @@ START_SERVICES = getattr(settings, 'START_SERVICES', [ ]) -RESTART_SERVICES = getattr(settings, 'RESTART_SERVICES', [ +ORCHESTRA_RESTART_SERVICES = getattr(settings, 'ORCHESTRA_RESTART_SERVICES', [ 'celeryd', 'celerybeat', 'uwsgi' ]) -STOP_SERVICES = getattr(settings, 'STOP_SERVICES', [ +ORCHESTRA_STOP_SERVICES = getattr(settings, 'ORCHESTRA_STOP_SERVICES', [ ('uwsgi', 'nginx'), 'celerybeat', 'celeryd', @@ -48,11 +48,11 @@ STOP_SERVICES = getattr(settings, 'STOP_SERVICES', [ ]) -API_ROOT_VIEW = getattr(settings, 'API_ROOT_VIEW', +ORCHESTRA_API_ROOT_VIEW = getattr(settings, 'ORCHESTRA_API_ROOT_VIEW', 'orchestra.api.root.APIRoot' ) ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL = getattr(settings, 'ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL', - 'support@{}'.format(BASE_DOMAIN) + 'support@{}'.format(ORCHESTRA_BASE_DOMAIN) ) diff --git a/orchestra/utils/options.py b/orchestra/utils/options.py index 48d58f9d..4e3609a5 100644 --- a/orchestra/utils/options.py +++ b/orchestra/utils/options.py @@ -22,9 +22,9 @@ def send_email_template(template, context, to, email_from=None, html=None, attac if not 'site' in context: from orchestra import settings - url = urlparse.urlparse(settings.SITE_URL) + url = urlparse.urlparse(settings.ORCHESTRA_SITE_URL) context['site'] = { - 'name': settings.SITE_NAME, + 'name': settings.ORCHESTRA_SITE_NAME, 'scheme': url.scheme, 'domain': url.netloc, }