Prepend ORCHESTRA_ to orchestra settigs

This commit is contained in:
Marc Aymerich 2015-04-05 22:34:47 +00:00
parent d165d7f03d
commit 4a6d29ebd7
27 changed files with 128 additions and 130 deletions

29
TODO.md
View File

@ -75,26 +75,19 @@
* Databases.User add reverse M2M databases widget (like mailbox.addresses) * Databases.User add reverse M2M databases widget (like mailbox.addresses)
* reconsider binding webapps to systemusers (pangea multiple users wordpress-ftp, moodle-pangea, etc) * Grant permissions to systemusers
* 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
* 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. * 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 * 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 * domain validation parse named-checzone output to assign errors to fields
* Directory Protection on webapp and use webapp path as base path (validate) * Directory Protection on webapp and use webapp path as base path (validate)
* validate systemuser.home on server-side * 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 * 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 ln -s /proc/self/fd /dev/fd
* escape passwords and not allow ' on them !
POST INSTALL POST INSTALL
------------ ------------
@ -155,8 +146,6 @@ Php binaries should have this format: /usr/bin/php5.2-cgi
* tags = GenericRelation(TaggedItem, related_query_name='bookmarks') * tags = GenericRelation(TaggedItem, related_query_name='bookmarks')
# make home for all systemusers (/home/username) and fix monitors
* user provided crons * user provided crons
* ```<?php * ```<?php
@ -167,15 +156,13 @@ require_once(/etc/moodles/.$moodle_host.config.php);``` moodle/drupl
# WPMU blog traffic # WPMU blog traffic
* normurlpath '' return '/'
* more robust backend error handling, continue executing but exit code > 0 if failure: failing_cmd || exit_code=1 and don't forget to call super.commit()!! * more robust backend error handling, continue executing but exit code > 0 if failure: failing_cmd || exit_code=1 and don't forget to call super.commit()!!
* website directives uniquenes validation on serializers * website directives uniquenes validation on serializers
+ is_Active custom filter with support for instance.account.is_Active annotate with F() needed (django 1.8) + 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 service help things: discount/refound/compensation effect and metric table
* Document metric interpretation help_text * Document metric interpretation help_text
@ -218,6 +205,7 @@ require_once(/etc/moodles/.$moodle_host.config.php);``` moodle/drupl
* write down insights * write down insights
# use english on services defs and so on, an translate them on render time # 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 ? * 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 # TDOO Base price: domini propi (all domains) + extra for other domains
# TODO prepend ORCHESTRA_ to orchestra/settings.py
Translation Translation
----------- -----------
@ -260,16 +246,12 @@ celery max-tasks-per-child
* postupgradeorchestra send signals in order to hook custom stuff * 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 * autoscale celery workers http://docs.celeryproject.org/en/latest/userguide/workers.html#autoscaling
* webapp has_website list filter * webapp has_website list filter
glic3rinu's django-fluent-dashboard glic3rinu's django-fluent-dashboard
* gevent is not ported to python3 :'( * gevent is not ported to python3 :'(
* uwsgi python3
# FIXME account deletion generates an integrity error # FIXME account deletion generates an integrity error
https://code.djangoproject.com/ticket/24576 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 # 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. * 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¶ + Query Expressions, Conditional Expressions, and Database Functions¶
* forms: You can now pass a callable that returns an iterable of choices when instantiating a ChoiceField. * forms: You can now pass a callable that returns an iterable of choices when instantiating a ChoiceField.
* migrate to DRF3.x * migrate to DRF3.x
* move all tests on django-orchestra/tests

View File

@ -56,7 +56,7 @@ class LogApiMixin(object):
class LinkHeaderRouter(DefaultRouter): class LinkHeaderRouter(DefaultRouter):
def get_api_root_view(self): def get_api_root_view(self):
""" returns the root view, with all the linked collections """ """ 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 APIRoot.router = self
return APIRoot.as_view() return APIRoot.as_view()

View File

@ -7,7 +7,10 @@ from ..core import services, accounts
class APIRoot(views.APIView): class APIRoot(views.APIView):
names = ['SITE_NAME', 'SITE_VERBOSE_NAME'] names = (
'ORCHESTRA_SITE_NAME',
'ORCHESTRA_SITE_VERBOSE_NAME'
)
def get(self, request, format=None): def get(self, request, format=None):
root_url = reverse('api-root', request=request, format=format) root_url = reverse('api-root', request=request, format=format)

View File

@ -137,7 +137,7 @@ function install_requirements () {
PIP="django==1.8 \ PIP="django==1.8 \
django-celery-email==1.0.4 \ 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 \ https://bitbucket.org/izi/django-admin-tools/get/a0abfffd76a0.zip \
IPy==0.81 \ IPy==0.81 \
django-extensions==1.5.2 \ django-extensions==1.5.2 \

View File

@ -68,5 +68,5 @@ LOCALE_PATHS = (
# DEFAULT_FROM_EMAIL = 'orchestra@yourhost.eu' # DEFAULT_FROM_EMAIL = 'orchestra@yourhost.eu'
SITE_NAME = '{{ project_name }}' ORCHESTRA_SITE_NAME = '{{ project_name }}'

View File

@ -1,7 +1,7 @@
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _ 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', ( ACCOUNTS_TYPES = getattr(settings, 'ACCOUNTS_TYPES', (
@ -53,9 +53,9 @@ ACCOUNTS_CREATE_RELATED = getattr(settings, 'ACCOUNTS_CREATE_RELATED', (
('domains.Domain', ('domains.Domain',
'name', '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 &lt;username&gt;.{} or not.".format(BASE_DOMAIN)), _("Designates whether to creates a related subdomain &lt;username&gt;.{} or not.".format(ORCHESTRA_BASE_DOMAIN)),
), ),
)) ))

View File

@ -1,7 +1,7 @@
from django.conf import settings from django.conf import settings
from django_countries import data 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', 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', 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', BILLS_SELLER_WEBSITE = getattr(settings, 'BILLS_SELLER_WEBSITE',
'www.{}'.format(BASE_DOMAIN) 'www.{}'.format(ORCHESTRA_BASE_DOMAIN)
) )

View File

@ -1,15 +1,15 @@
from django.conf import settings 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', 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', 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', ( DOMAINS_DEFAULT_MX = getattr(settings, 'DOMAINS_DEFAULT_MX', (
'10 mail.{}.'.format(BASE_DOMAIN), '10 mail.{}.'.format(ORCHESTRA_BASE_DOMAIN),
'10 mail2.{}.'.format(BASE_DOMAIN), '10 mail2.{}.'.format(ORCHESTRA_BASE_DOMAIN),
)) ))
DOMAINS_DEFAULT_NS = getattr(settings, 'DOMAINS_DEFAULT_NS', ( DOMAINS_DEFAULT_NS = getattr(settings, 'DOMAINS_DEFAULT_NS', (
'ns1.{}.'.format(BASE_DOMAIN), 'ns1.{}.'.format(ORCHESTRA_BASE_DOMAIN),
'ns2.{}.'.format(BASE_DOMAIN), 'ns2.{}.'.format(ORCHESTRA_BASE_DOMAIN),
)) ))

View File

@ -1,6 +1,6 @@
from django.conf import settings 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', 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_DEFAULT_DOMAIN = getattr(settings, 'LIST_DEFAULT_DOMAIN',
'lists.{}'.format(BASE_DOMAIN) 'lists.{}'.format(ORCHESTRA_BASE_DOMAIN)
) )
LISTS_LIST_URL = getattr(settings, 'LISTS_LIST_URL', 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)
) )

View File

@ -4,7 +4,7 @@ import textwrap
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _ 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', 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', 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', MAILBOXES_LOCAL_ADDRESS_DOMAIN = getattr(settings, 'MAILBOXES_LOCAL_ADDRESS_DOMAIN',
BASE_DOMAIN ORCHESTRA_BASE_DOMAIN
) )

View File

@ -11,8 +11,7 @@ from . import methods
def replace(context, pattern, repl): def replace(context, pattern, repl):
if isinstance(context, str): """ applies replace to all context str values """
return context.replace(patter, repl)
for key, value in context.items(): for key, value in context.items():
if isinstance(value, str): if isinstance(value, str):
context[key] = value.replace(pattern, repl) context[key] = value.replace(pattern, repl)
@ -23,7 +22,7 @@ class ServiceMount(plugins.PluginMount):
def __init__(cls, name, bases, attrs): def __init__(cls, name, bases, attrs):
# Make sure backends specify a model attribute # Make sure backends specify a model attribute
if not (attrs.get('abstract', False) or name == 'ServiceBackend' or cls.model): 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) 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. the changes of all modified objects, reloading the daemon just once.
""" """
model = None model = None
related_models = () # ((model, accessor__attribute),) related_models = () # ((model, accessor__attribute),)
script_method = methods.SSH script_method = methods.SSH
script_executable = '/bin/bash' script_executable = '/bin/bash'
function_method = methods.Python function_method = methods.Python
type = 'task' # 'sync' type = 'task' # 'sync'
ignore_fields = [] ignore_fields = []
actions = [] actions = []
default_route_match = 'True' 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): def __str__(self):
return type(self).__name__ return type(self).__name__

View File

@ -1,5 +1,6 @@
from django.contrib import admin from django.contrib import admin
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db.models import Prefetch
from django.utils import timezone from django.utils import timezone
from django.utils.html import escape from django.utils.html import escape
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -17,7 +18,7 @@ from .models import Order, MetricStorage
class MetricStorageInline(admin.TabularInline): class MetricStorageInline(admin.TabularInline):
model = MetricStorage model = MetricStorage
readonly_fields = ('value', 'updated_on') readonly_fields = ('value', 'created_on', 'updated_on')
extra = 0 extra = 0
def has_add_permission(self, request, obj=None): def has_add_permission(self, request, obj=None):
@ -33,10 +34,11 @@ class MetricStorageInline(admin.TabularInline):
def get_queryset(self, request): def get_queryset(self, request):
qs = super(MetricStorageInline, self).get_queryset(request) qs = super(MetricStorageInline, self).get_queryset(request)
if self.parent_object and self.parent_object.pk: change_view = bool(self.parent_object and self.parent_object.pk)
qs = qs.filter(order=self.parent_object.pk).order_by('-id') if change_view:
qs = qs.order_by('-id')
try: try:
tenth_id = qs.values_list('id', flat=True)[10] tenth_id = qs.values_list('id', flat=True)[9]
except IndexError: except IndexError:
pass pass
else: else:
@ -59,7 +61,10 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
inlines = (MetricStorageInline,) inlines = (MetricStorageInline,)
add_inlines = () add_inlines = ()
search_fields = ('account__username', 'description') 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') list_select_related = ('account', 'service')
service_link = admin_link('service') service_link = admin_link('service')

View File

@ -178,7 +178,7 @@ class Order(models.Model):
metric = ', metric:{}'.format(metric) metric = ', metric:{}'.format(metric)
description = handler.get_order_description(instance) description = handler.get_order_description(instance)
logger.info("UPDATED order id:{id}, description:{description}{metric}".format( 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: if self.description != description:
self.description = description self.description = description

View File

@ -1,6 +1,6 @@
from django.conf import settings 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', ( 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', 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', 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', 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', 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', SAAS_GITLAB_DOMAIN = getattr(settings, 'SAAS_GITLAB_DOMAIN',
'gitlab.{}'.format(BASE_DOMAIN) 'gitlab.{}'.format(ORCHESTRA_BASE_DOMAIN)
) )

View File

@ -504,7 +504,7 @@ class ServiceHandler(plugins.Plugin, metaclass=plugins.PluginMount):
new_price += self.get_price(order, metric) * size new_price += self.get_price(order, metric) * size
new_metric += metric new_metric += metric
size = self.get_price_size(rini, bp) 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: if new_price > old_price:
metric = new_metric - charged metric = new_metric - charged
price = new_price - old_price price = new_price - old_price
@ -520,7 +520,7 @@ class ServiceHandler(plugins.Plugin, metaclass=plugins.PluginMount):
if self.get_pricing_period() == self.NEVER: if self.get_pricing_period() == self.NEVER:
# Changes (Mailbox disk-like) # Changes (Mailbox disk-like)
for cini, cend, metric in order.get_metric(ini, bp, changes=True): 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)) lines.append(self.generate_line(order, price, cini, cend, metric=metric))
elif self.get_pricing_period() == self.billing_period: elif self.get_pricing_period() == self.billing_period:
# pricing_slots (Traffic-like) # pricing_slots (Traffic-like)
@ -528,7 +528,7 @@ class ServiceHandler(plugins.Plugin, metaclass=plugins.PluginMount):
raise NotImplementedError raise NotImplementedError
for cini, cend in self.get_pricing_slots(ini, bp): for cini, cend in self.get_pricing_slots(ini, bp):
metric = order.get_metric(cini, cend) 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)) lines.append(self.generate_line(order, price, cini, cend, metric=metric))
else: else:
raise NotImplementedError raise NotImplementedError
@ -541,7 +541,7 @@ class ServiceHandler(plugins.Plugin, metaclass=plugins.PluginMount):
if self.get_pricing_period() == self.NEVER: if self.get_pricing_period() == self.NEVER:
# get metric (Job-like) # get metric (Job-like)
metric = order.get_metric(date) 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)) lines.append(self.generate_line(order, price, date, metric=metric))
else: else:
raise NotImplementedError raise NotImplementedError

View File

@ -31,6 +31,12 @@ class UNIXUserBackend(ServiceController):
chmod 750 %(home)s chmod 750 %(home)s
chown %(user)s:%(user)s %(home)s""") % context 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: for member in settings.SYSTEMUSERS_DEFAULT_GROUP_MEMBERS:
context['member'] = member context['member'] = member
self.append('usermod -a -G %(user)s %(member)s' % context) 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; } & { sleep 2 && killall -u %(user)s -s KILL; } &
killall -u %(user)s || true killall -u %(user)s || true
userdel %(user)s || exit_code=1 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): def grant_permission(self, user):
context = self.get_context(user) context = self.get_context(user)
# TODO setacl # 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): def get_groups(self, user):
if user.is_main: if user.is_main:
return user.account.systemusers.exclude(username=user.username).values_list('username', flat=True) 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, 'password': user.password if user.active else '*%s' % user.password,
'shell': user.shell, 'shell': user.shell,
'mainuser': user.username if user.is_main else user.account.username, '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, "'", '"') return replace(context, "'", '"')
@ -91,16 +94,12 @@ class UNIXUserDisk(ServiceMonitor):
def monitor(self, user): def monitor(self, user):
context = self.get_context(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 %(base_home)s)" % context)
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)
def get_context(self, user): def get_context(self, user):
context = { context = {
'object_id': user.pk, 'object_id': user.pk,
'home': user.home, 'base_home': user.get_base_home(),
} }
return replace(context, "'", '"') return replace(context, "'", '"')

View File

@ -6,6 +6,7 @@ from orchestra.forms import UserCreationForm, UserChangeForm
from . import settings from . import settings
from .models import SystemUser from .models import SystemUser
from .validators import validate_home
class SystemUserFormMixin(object): class SystemUserFormMixin(object):
@ -63,7 +64,7 @@ class SystemUserFormMixin(object):
if home and self.MOCK_USERNAME in home: if home and self.MOCK_USERNAME in home:
username = self.cleaned_data.get('username', '') username = self.cleaned_data.get('username', '')
self.cleaned_data['home'] = home.replace(self.MOCK_USERNAME, 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): class SystemUserCreationForm(SystemUserFormMixin, UserCreationForm):

View File

@ -92,29 +92,10 @@ class SystemUser(models.Model):
raise ValidationError({ raise ValidationError({
'directory': directory_error, 'directory': directory_error,
}) })
if self.has_shell and self.home != self.get_base_home():
def validate_home(self, data, account): raise ValidationError({
""" validates home based on account and data['shell'] """ 'home': _("Shell users should use their own home."),
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.")
})
def set_password(self, raw_password): def set_password(self, raw_password):
self.password = make_password(raw_password) self.password = make_password(raw_password)

View File

@ -8,6 +8,7 @@ from orchestra.contrib.accounts.serializers import AccountSerializerMixin
from orchestra.core.validators import validate_password from orchestra.core.validators import validate_password
from .models import SystemUser from .models import SystemUser
from .validators import validate_home
class GroupSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer): class GroupSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer):
@ -38,7 +39,7 @@ class SystemUserSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
username=attrs.get('username') or self.object.username, username=attrs.get('username') or self.object.username,
shell=attrs.get('shell') or self.object.shell, shell=attrs.get('shell') or self.object.shell,
) )
user.validate_home(attrs, self.account) validate_home(user, attrs, self.account)
return attrs return attrs
def validate_password(self, attrs, source): def validate_password(self, attrs, source):

View File

@ -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.")
})

View File

@ -1,6 +1,6 @@
from django.conf import settings 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', 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', WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST = getattr(settings, 'WEBAPPS_DEFAULT_MYSQL_DATABASE_HOST',
'mysql.{}'.format(BASE_DOMAIN) 'mysql.{}'.format(ORCHESTRA_BASE_DOMAIN)
) )

View File

@ -4,7 +4,7 @@ from orchestra import settings
def site(request): def site(request):
""" Adds site-related context variables to the context """ """ Adds site-related context variables to the context """
return { return {
'SITE_NAME': settings.SITE_NAME, 'SITE_NAME': settings.ORCHESTRA_SITE_NAME,
'SITE_VERBOSE_NAME': settings.SITE_VERBOSE_NAME 'SITE_VERBOSE_NAME': settings.ORCHESTRA_SITE_VERBOSE_NAME
} }

View File

@ -1,11 +1,11 @@
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from orchestra.management.commands.startservices import ManageServiceCommand from orchestra.management.commands.startservices import ManageServiceCommand
from orchestra.settings import RESTART_SERVICES from orchestra.settings import ORCHESTRA_RESTART_SERVICES
class Command(ManageServiceCommand): class Command(ManageServiceCommand):
services = RESTART_SERVICES services = ORCHESTRA_RESTART_SERVICES
action = 'restart' action = 'restart'
option_list = BaseCommand.option_list option_list = BaseCommand.option_list
help = 'Restart all related services. Usefull for reload configuration and files.' help = 'Restart all related services. Usefull for reload configuration and files.'

View File

@ -2,7 +2,7 @@ from optparse import make_option
from django.core.management.base import BaseCommand 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 from orchestra.utils.sys import run, check_root
@ -53,7 +53,7 @@ class ManageServiceCommand(BaseCommand):
class Command(ManageServiceCommand): class Command(ManageServiceCommand):
services = START_SERVICES services = ORCHESTRA_START_SERVICES
action = 'start' action = 'start'
option_list = BaseCommand.option_list option_list = BaseCommand.option_list
help = 'Start all related services. Usefull for reload configuration and files.' help = 'Start all related services. Usefull for reload configuration and files.'

View File

@ -1,11 +1,11 @@
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from orchestra.management.commands.startservices import ManageServiceCommand from orchestra.management.commands.startservices import ManageServiceCommand
from orchestra.settings import STOP_SERVICES from orchestra.settings import ORCHESTRA_STOP_SERVICES
class Command(ManageServiceCommand): class Command(ManageServiceCommand):
services = STOP_SERVICES services = ORCHESTRA_STOP_SERVICES
action = 'stop' action = 'stop'
option_list = BaseCommand.option_list option_list = BaseCommand.option_list
help = 'Stop all related services. Usefull for reload configuration and files.' help = 'Stop all related services. Usefull for reload configuration and files.'

View File

@ -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 # Domain name used when it will not be possible to infere the domain from a request
# For example in periodic tasks # For example in periodic tasks
SITE_URL = getattr(settings, 'SITE_URL', ORCHESTRA_SITE_URL = getattr(settings, 'ORCHESTRA_SITE_URL',
'http://localhost' 'http://localhost'
) )
SITE_NAME = getattr(settings, 'SITE_NAME', ORCHESTRA_SITE_NAME = getattr(settings, 'ORCHESTRA_SITE_NAME',
'orchestra' 'orchestra'
) )
SITE_VERBOSE_NAME = getattr(settings, 'SITE_VERBOSE_NAME', ORCHESTRA_SITE_VERBOSE_NAME = getattr(settings, 'ORCHESTRA_SITE_VERBOSE_NAME',
_("%s Hosting Management" % SITE_NAME.capitalize()) _("%s Hosting Management" % ORCHESTRA_SITE_NAME.capitalize())
) )
BASE_DOMAIN = getattr(settings, 'BASE_DOMAIN', ORCHESTRA_BASE_DOMAIN = getattr(settings, 'ORCHESTRA_BASE_DOMAIN',
'orchestra.lan' 'orchestra.lan'
) )
# Service management commands # Service management commands
START_SERVICES = getattr(settings, 'START_SERVICES', [ ORCHESTRA_START_SERVICES = getattr(settings, 'ORCHESTRA_START_SERVICES', [
'postgresql', 'postgresql',
'celeryevcam', 'celeryevcam',
'celeryd', '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', 'celeryd',
'celerybeat', 'celerybeat',
'uwsgi' 'uwsgi'
]) ])
STOP_SERVICES = getattr(settings, 'STOP_SERVICES', [ ORCHESTRA_STOP_SERVICES = getattr(settings, 'ORCHESTRA_STOP_SERVICES', [
('uwsgi', 'nginx'), ('uwsgi', 'nginx'),
'celerybeat', 'celerybeat',
'celeryd', '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.api.root.APIRoot'
) )
ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL = getattr(settings, 'ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL', ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL = getattr(settings, 'ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL',
'support@{}'.format(BASE_DOMAIN) 'support@{}'.format(ORCHESTRA_BASE_DOMAIN)
) )

View File

@ -22,9 +22,9 @@ def send_email_template(template, context, to, email_from=None, html=None, attac
if not 'site' in context: if not 'site' in context:
from orchestra import settings from orchestra import settings
url = urlparse.urlparse(settings.SITE_URL) url = urlparse.urlparse(settings.ORCHESTRA_SITE_URL)
context['site'] = { context['site'] = {
'name': settings.SITE_NAME, 'name': settings.ORCHESTRA_SITE_NAME,
'scheme': url.scheme, 'scheme': url.scheme,
'domain': url.netloc, 'domain': url.netloc,
} }