Fixes on systemusers ACL permissions

This commit is contained in:
Marc Aymerich 2015-05-11 14:05:39 +00:00
parent 2877f64d9d
commit 20ccadf3e1
15 changed files with 201 additions and 133 deletions

View file

@ -359,3 +359,10 @@ resorce monitoring more efficient, less mem an better queries for calc current d
ciphers=['arcfour128', 'aes256'] ciphers=['arcfour128', 'aes256']
http://paramiko-docs.readthedocs.org/en/latest/api/transport.html http://paramiko-docs.readthedocs.org/en/latest/api/transport.html
* Grant and revoke permissions
setfacl: /home/marcay//logs: Operation not permitted

View file

@ -6,7 +6,6 @@ import socket
import sys import sys
import select import select
import paramiko
from celery.datastructures import ExceptionInfo from celery.datastructures import ExceptionInfo
from django.conf import settings as djsettings from django.conf import settings as djsettings
@ -25,11 +24,12 @@ def Paramiko(backend, log, server, cmds, async=False):
""" """
Executes cmds to remote server using Pramaiko Executes cmds to remote server using Pramaiko
""" """
import paramiko
script = '\n'.join(cmds) script = '\n'.join(cmds)
script = script.replace('\r', '') script = script.replace('\r', '')
log.state = log.STARTED log.state = log.STARTED
log.script = script log.script = script
log.save(update_fields=('script', 'state')) log.save(update_fields=('script', 'state', 'updated_at'))
if not cmds: if not cmds:
return return
channel = None channel = None
@ -48,7 +48,7 @@ def Paramiko(backend, log, server, cmds, async=False):
logger.error('%s timed out on %s' % (backend, addr)) logger.error('%s timed out on %s' % (backend, addr))
log.state = log.TIMEOUT log.state = log.TIMEOUT
log.stderr = str(e) log.stderr = str(e)
log.save(update_fields=['state', 'stderr']) log.save(update_fields=('state', 'stderr', 'updated_at'))
return return
paramiko_connections[addr] = ssh paramiko_connections[addr] = ssh
transport = ssh.get_transport() transport = ssh.get_transport()
@ -73,7 +73,7 @@ def Paramiko(backend, log, server, cmds, async=False):
while part: while part:
log.stderr += part log.stderr += part
part = channel.recv_stderr(1024).decode('utf-8') part = channel.recv_stderr(1024).decode('utf-8')
log.save(update_fields=['stdout', 'stderr']) log.save(update_fields=('stdout', 'stderr', 'updated_at'))
if channel.exit_status_ready(): if channel.exit_status_ready():
if second: if second:
break break
@ -95,7 +95,7 @@ def Paramiko(backend, log, server, cmds, async=False):
finally: finally:
if log.state == log.STARTED: if log.state == log.STARTED:
log.state = log.ABORTED log.state = log.ABORTED
log.save(update_fields=['state']) log.save(update_fields=('state', 'updated_at'))
if channel is not None: if channel is not None:
channel.close() channel.close()
@ -108,25 +108,25 @@ def OpenSSH(backend, log, server, cmds, async=False):
script = script.replace('\r', '') script = script.replace('\r', '')
log.state = log.STARTED log.state = log.STARTED
log.script = script log.script = script
log.save(update_fields=('script', 'state')) log.save(update_fields=('script', 'state', 'updated_at'))
if not cmds: if not cmds:
return return
channel = None channel = None
ssh = None ssh = None
try: try:
ssh = sshrun(server.get_address(), script, executable=backend.script_executable, ssh = sshrun(server.get_address(), script, executable=backend.script_executable,
persist=True, async=async) persist=True, async=async, silent=True)
logger.debug('%s running on %s' % (backend, server)) logger.debug('%s running on %s' % (backend, server))
if async: if async:
second = False second = False
for state in ssh: for state in ssh:
log.stdout += state.stdout.decode('utf8') log.stdout += state.stdout.decode('utf8')
log.stderr += state.stderr.decode('utf8') log.stderr += state.stderr.decode('utf8')
log.save() log.save(update_fields=('stdout', 'stderr', 'updated_at'))
log.exit_code = state.exit_code log.exit_code = state.exit_code
else: else:
log.stdout = ssh.stdout log.stdout = ssh.stdout.decode('utf8')
log.stderr = ssh.stderr log.stderr = ssh.stderr.decode('utf8')
log.exit_code = ssh.exit_code log.exit_code = ssh.exit_code
log.state = log.SUCCESS if log.exit_code == 0 else log.FAILURE log.state = log.SUCCESS if log.exit_code == 0 else log.FAILURE
logger.debug('%s execution state on %s is %s' % (backend, server, log.state)) logger.debug('%s execution state on %s is %s' % (backend, server, log.state))
@ -140,7 +140,7 @@ def OpenSSH(backend, log, server, cmds, async=False):
finally: finally:
if log.state == log.STARTED: if log.state == log.STARTED:
log.state = log.ABORTED log.state = log.ABORTED
log.save(update_fields=['state']) log.save(update_fields=('state', 'updated_at'))
def SSH(*args, **kwargs): def SSH(*args, **kwargs):
@ -150,12 +150,11 @@ def SSH(*args, **kwargs):
def Python(backend, log, server, cmds, async=False): def Python(backend, log, server, cmds, async=False):
# TODO collect stdout?
script = [ str(cmd.func.__name__) + str(cmd.args) for cmd in cmds ] script = [ str(cmd.func.__name__) + str(cmd.args) for cmd in cmds ]
script = json.dumps(script, indent=4).replace('"', '') script = json.dumps(script, indent=4).replace('"', '')
log.state = log.STARTED log.state = log.STARTED
log.script = '\n'.join([log.script, script]) log.script = '\n'.join((log.script, script))
log.save(update_fields=('script', 'state')) log.save(update_fields=('script', 'state', 'updated_at'))
try: try:
for cmd in cmds: for cmd in cmds:
with CaptureStdout() as stdout: with CaptureStdout() as stdout:
@ -163,7 +162,7 @@ def Python(backend, log, server, cmds, async=False):
for line in stdout: for line in stdout:
log.stdout += line + '\n' log.stdout += line + '\n'
if async: if async:
log.save(update_fields=['stdout']) log.save(update_fields=('stdout', 'updated_at'))
except: except:
log.exit_code = 1 log.exit_code = 1
log.state = log.FAILURE log.state = log.FAILURE

View file

@ -43,9 +43,9 @@ ORCHESTRATION_BACKEND_CLEANUP_DAYS = Setting('ORCHESTRATION_BACKEND_CLEANUP_DAYS
ORCHESTRATION_SSH_METHOD_BACKEND = Setting('ORCHESTRATION_SSH_METHOD_BACKEND', ORCHESTRATION_SSH_METHOD_BACKEND = Setting('ORCHESTRATION_SSH_METHOD_BACKEND',
'orchestra.contrib.orchestration.methods.OpenSSH', 'orchestra.contrib.orchestration.methods.OpenSSH',
help_text=_("Two methods provided:<br>" help_text=_("Two methods are provided:<br>"
"<tt>orchestra.contrib.orchestration.methods.OpenSSH</tt> with ControlPersist.<br>" "1) <tt>orchestra.contrib.orchestration.methods.OpenSSH</tt> with ControlPersist.<br>"
"<tt>orchestra.contrib.orchestration.methods.Paramiko</tt> with connection pool.<br>" "2) <tt>orchestra.contrib.orchestration.methods.Paramiko</tt> with connection pool.<br>"
"Both perform similarly, but OpenSSH has the advantage that the connections are shared between workers,<br>" "Both perform similarly, but OpenSSH has the advantage that the connections are shared between workers. "
"Paramiko, in contrast, has a per worker connection pool.") "Paramiko, in contrast, has a per worker connection pool.")
) )

View file

@ -155,6 +155,29 @@ match_price.help_text = _("Only <b>the rate</b> with a) inmediate inferior metri
def best_price(rates, metric): def best_price(rates, metric):
pass candidates = []
selected = False
prev = None
rates = _prepend_missing(rates.distinct())
for rate in rates:
if prev:
if prev.plan != rate.plan:
if not selected and prev.quantity <= metric:
candidates.append(prev)
selected = False
if not selected and rate.quantity > metric:
if prev.quantity <= metric:
candidates.append(prev)
selected = True
prev = rate
if not selected and prev.quantity <= metric:
candidates.append(prev)
candidates.sort(key=lambda r: r.price)
if candidates:
return [AttrDict(**{
'quantity': metric,
'price': candidates[0].price,
})]
return None
best_price.verbose_name = _("Best price") best_price.verbose_name = _("Best price")
best_price.help_text = _("Produces the best possible price given all active rating lines.") best_price.help_text = _("Produces the best possible price given all active rating lines.")

View file

@ -7,54 +7,67 @@ from django.template.response import TemplateResponse
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ungettext, ugettext_lazy as _
from orchestra.admin.decorators import action_with_confirmation from orchestra.admin.decorators import action_with_confirmation
from orchestra.contrib.orchestration.middlewares import OperationsMiddleware from orchestra.contrib.orchestration import Operation, helpers
from .forms import GrantPermissionForm from .forms import PermissionForm
def grant_permission(modeladmin, request, queryset): def get_verbose_choice(choices, value):
for choice, verbose in choices:
if choice == value:
return verbose
def set_permission(modeladmin, request, queryset):
account_id = None account_id = None
for user in queryset: for user in queryset:
account_id = account_id or user.account_id account_id = account_id or user.account_id
if user.account_id != account_id: if user.account_id != account_id:
messages.error("Users from the same account should be selected.") messages.error("Users from the same account should be selected.")
user = queryset[0] user = queryset[0]
form = PermissionForm(user)
if request.method == 'POST': if request.method == 'POST':
form = GrantPermissionForm(user, request.POST) form = PermissionForm(user, request.POST)
if form.is_valid(): if form.is_valid():
cleaned_data = form.cleaned_data cleaned_data = form.cleaned_data
to = os.path.join(cleaned_data['base_path'], cleaned_data['path_extension']) operations = []
ro = cleaned_data['read_only']
for user in queryset: for user in queryset:
user.grant_to = to user.set_perm_action = cleaned_data['set_action']
user.grant_ro = ro user.set_perm_base_home = cleaned_data['base_home']
# DOn't collect, execute right away for path validation user.set_perm_home_extension = cleaned_data['home_extension']
OperationsMiddleware.collect('grant_permission', instance=user) user.set_perm_perms = cleaned_data['permissions']
operations.extend(Operation.create_for_action(user, 'set_permission'))
verbose_action = get_verbose_choice(form.fields['set_action'].choices,
user.set_perm_action)
verbose_permissions = get_verbose_choice(form.fields['permissions'].choices,
user.set_perm_perms)
context = { context = {
'type': _("read-only") if ro else _("read-write"), 'action': verbose_action,
'to': to, 'perms': verbose_permissions,
'to': os.path.join(user.set_perm_base_home, user.set_perm_home_extension),
} }
msg = _("Granted %(type)s permissions on %(to)s") % context msg = _("%(action)s %(perms)s permission to %(to)s") % context
modeladmin.log_change(request, user, msg) modeladmin.log_change(request, user, msg)
# TODO feedback message logs = Operation.execute(operations)
helpers.message_user(request, logs)
return return
opts = modeladmin.model._meta opts = modeladmin.model._meta
app_label = opts.app_label app_label = opts.app_label
context = { context = {
'title': _("Grant permission"), 'title': _("Set permission"),
'action_name': _("Grant permission"), 'action_name': _("Set permission"),
'action_value': 'grant_permission', 'action_value': 'set_permission',
'queryset': queryset, 'queryset': queryset,
'opts': opts, 'opts': opts,
'obj': user, 'obj': user,
'app_label': app_label, 'app_label': app_label,
'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME, 'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
'form': GrantPermissionForm(user), 'form': form,
} }
return TemplateResponse(request, 'admin/systemusers/systemuser/grant_permission.html', return TemplateResponse(request, 'admin/systemusers/systemuser/set_permission.html',
context, current_app=modeladmin.admin_site.name) context, current_app=modeladmin.admin_site.name)
grant_permission.url_name = 'grant-permission' set_permission.url_name = 'set-permission'
grant_permission.verbose_name = _("Grant permission") set_permission.verbose_name = _("Set permission")
def delete_selected(modeladmin, request, queryset): def delete_selected(modeladmin, request, queryset):

View file

@ -6,7 +6,7 @@ from orchestra.admin.actions import disable
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
from orchestra.contrib.accounts.filters import IsActiveListFilter from orchestra.contrib.accounts.filters import IsActiveListFilter
from .actions import grant_permission, delete_selected from .actions import set_permission, delete_selected
from .filters import IsMainListFilter from .filters import IsMainListFilter
from .forms import SystemUserCreationForm, SystemUserChangeForm from .forms import SystemUserCreationForm, SystemUserChangeForm
from .models import SystemUser from .models import SystemUser
@ -41,7 +41,7 @@ class SystemUserAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, Extende
add_form = SystemUserCreationForm add_form = SystemUserCreationForm
form = SystemUserChangeForm form = SystemUserChangeForm
ordering = ('-id',) ordering = ('-id',)
actions = (delete_selected, grant_permission, disable) actions = (delete_selected, set_permission, disable)
change_view_actions = actions change_view_actions = actions
def display_main(self, user): def display_main(self, user):

View file

@ -15,7 +15,7 @@ class UNIXUserBackend(ServiceController):
""" """
verbose_name = _("UNIX user") verbose_name = _("UNIX user")
model = 'systemusers.SystemUser' model = 'systemusers.SystemUser'
actions = ('save', 'delete', 'grant_permission') actions = ('save', 'delete', 'set_permission', 'validate_path')
doc_settings = (settings, doc_settings = (settings,
('SYSTEMUSERS_DEFAULT_GROUP_MEMBERS', 'SYSTEMUSERS_MOVE_ON_DELETE_PATH') ('SYSTEMUSERS_DEFAULT_GROUP_MEMBERS', 'SYSTEMUSERS_MOVE_ON_DELETE_PATH')
) )
@ -65,17 +65,69 @@ class UNIXUserBackend(ServiceController):
else: else:
self.append("rm -fr %(base_home)s" % context) self.append("rm -fr %(base_home)s" % context)
def grant_permission(self, user): def set_permission(self, user):
# TODO
context = self.get_context(user) context = self.get_context(user)
context.update({ context.update({
'to': user.grant_to, 'perm_action': user.set_perm_action,
'ro': user.grant_ro, 'perm_home': user.set_perm_base_home,
'perm_to': os.path.join(user.set_perm_base_home, user.set_perm_home_extension),
'exclude': '',
}) })
if user.grant_ro:
self.append('echo "acl add read permissions for %(user)s to %(to)s"' % context) exclude_acl = []
for exclude in settings.SYSTEMUSERS_EXLUDE_ACL_PATHS:
context['exclude'] = exclude
exclude_acl.append('-not -path "%(perm_home)s/%(exclude)s"' % context)
if exclude_acl:
context['exclude'] = ' \\\n -a '.join(exclude_acl)
if user.set_perm_perms == 'read-write':
context['perm_perms'] = 'rwx' if user.set_perm_action == 'grant' else '---'
elif user.set_perm_perms == 'read-only':
context['perm_perms'] = 'r-x' if user.set_perm_action == 'grant' else '-wx'
elif user.set_perm_perms == 'write-only':
context['perm_perms'] = '-wx' if user.set_perm_action == 'grant' else 'r-x'
if user.set_perm_action == 'grant':
self.append(textwrap.dedent("""\
# Home access
setfacl -m u:%(user)s:--x '%(perm_home)s'
# Grant perms to existing and future files
find '%(perm_to)s' %(exclude)s \\
-exec setfacl -m u:%(user)s:%(perm_perms)s {} \\;
find '%(perm_to)s' -type d %(exclude)s \\
-exec setfacl -m d:u:%(user)s:%(perm_perms)s {} \\;
# Account group as the owner of new files
chmod g+s '%(perm_to)s'
""") % context
)
if not user.is_main:
self.append(textwrap.dedent("""\
# Grant access to main user
find '%(perm_to)s' -type d %(exclude)s \\
-exec setfacl -m d:u:%(mainuser)s:rwx {} \\;
""") % context
)
elif user.set_perm_action == 'revoke':
self.append(textwrap.dedent("""\
# Revoke permissions
find '%(perm_to)s' %(exclude)s \\
-exec setfacl -m u:%(user)s:%(perm_perms)s {} \\;
""") % context
)
else: else:
self.append('echo "acl add read-write permissions for %(user)s to %(to)s"' % context) raise NotImplementedError()
def validate_path(self, user):
context = {
'perm_to': os.path.join(user.set_perm_base_home, user.set_perm_home_extension)
}
self.append(textwrap.dedent("""\
if [[ ! -e '%(perm_to)s' ]]; then
echo "%(perm_to)s path does not exists." >&2
exit 1
fi
""") % context
)
def get_groups(self, user): def get_groups(self, user):
if user.is_main: if user.is_main:

View file

@ -1,8 +1,10 @@
import textwrap import textwrap
from django import forms from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import ngettext, ugettext_lazy as _ from django.utils.translation import ngettext, ugettext_lazy as _
from orchestra.contrib.orchestration import Operation
from orchestra.forms import UserCreationForm, UserChangeForm from orchestra.forms import UserCreationForm, UserChangeForm
from . import settings from . import settings
@ -79,21 +81,44 @@ class SystemUserChangeForm(SystemUserFormMixin, UserChangeForm):
pass pass
class GrantPermissionForm(forms.Form): class PermissionForm(forms.Form):
base_path = forms.ChoiceField(label=_("Grant access to"), choices=(), set_action = forms.ChoiceField(label=_("Action"), initial='grant',
help_text=_("User will be granted access to this directory.")) choices=(
path_extension = forms.CharField(label=_("Path extension"), required=False, initial='', ('grant', _("Grant")),
('revoke', _("Revoke"))
))
base_home = forms.ChoiceField(label=_("Set permissions to"), choices=(),
help_text=_("User will be granted/revoked access to this directory."))
home_extension = forms.CharField(label=_("Home extension"), required=False, initial='',
widget=forms.TextInput(attrs={'size':'70'}), help_text=_("Relative to chosen home.")) widget=forms.TextInput(attrs={'size':'70'}), help_text=_("Relative to chosen home."))
read_only = forms.BooleanField(label=_("Read only"), initial=False, required=False, permissions = forms.ChoiceField(label=_("Permissions"), initial='read-write',
help_text=_("Designates whether the permissions granted will be read-only or read/write.")) choices=(
('read-write', _("Read and write")),
('read-only', _("Read only")),
('write-only', _("Write only"))
))
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
instance = args[0] self.instance = args[0]
super_args = [] super_args = []
if len(args) > 1: if len(args) > 1:
super_args.append(args[1]) super_args.append(args[1])
super(GrantPermissionForm, self).__init__(*super_args, **kwargs) super(PermissionForm, self).__init__(*super_args, **kwargs)
related_users = type(instance).objects.filter(account=instance.account_id) related_users = type(self.instance).objects.filter(account=self.instance.account_id)
self.fields['base_path'].choices = ( self.fields['base_home'].choices = (
(user.get_base_home(), user.get_base_home()) for user in related_users (user.get_base_home(), user.get_base_home()) for user in related_users
) )
def clean(self):
cleaned_data = super(PermissionForm, self).clean()
user = self.instance
user.set_perm_action = cleaned_data['set_action']
user.set_perm_base_home = cleaned_data['base_home']
user.set_perm_home_extension = cleaned_data['home_extension']
user.set_perm_perms = cleaned_data['permissions']
log = Operation.execute_action(user, 'validate_path')[0]
if 'path does not exists' in log.stderr:
raise ValidationError({
'home_extension': log.stderr,
})
return cleaned_data

View file

@ -58,3 +58,10 @@ SYSTEMUSERS_MOVE_ON_DELETE_PATH = Setting('SYSTEMUSERS_MOVE_ON_DELETE_PATH',
help_text="Available fromat names: <tt>%s</tt>" % ', '.join(_backend_names), help_text="Available fromat names: <tt>%s</tt>" % ', '.join(_backend_names),
validators=[Setting.string_format_validator(_backend_names)], validators=[Setting.string_format_validator(_backend_names)],
) )
SYSTEMUSERS_EXLUDE_ACL_PATHS = Setting('SYSTEMUSERS_EXLUDE_ACL_PATHS',
(),
help_text=("Relative to user's home.<br>"
"e.g. ('logs', 'logs/apache*', 'webapps')"),
)

View file

@ -1,65 +0,0 @@
{% extends "admin/base_site.html" %}
{% load i18n l10n %}
{% load url from future %}
{% load admin_urls static utils %}
{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />
<link rel="stylesheet" type="text/css" href="{% static "orchestra/css/hide-inline-id.css" %}" />
{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
{% if obj %}
&rsaquo; <a href="{% url opts|admin_urlname:'change' obj.pk %}">{{ obj }}</a>
&rsaquo; {{ action_name }}
{% elif add %}
&rsaquo; <a href="../">{% trans "Add" %} {{ opts.verbose_name }}</a>
&rsaquo; {{ action_name }}
{% else %}
&rsaquo; {{ action_name }} multiple objects
{% endif %}
</div>
{% endblock %}
{% block content %}
<div>
<div style="margin:20px;">
Grant permissions to these system users: {% for user in queryset %}{{ user.username }}{% if not forloop.last %},{% endif %}{% endfor %}.
<ul>{{ display_objects | unordered_list }}</ul>
<form action="" method="post">{% csrf_token %}
<fieldset class="module aligned wide">
<div class="form-row ">
<div class="field-box field-home">
{{ form.path_extension.errors }}
<label for="{{ form.base_path.id_for_label }}">{{ form.base_path.label }}:</label>
{{ form.base_path }}{% for x in ""|ljust:"50" %}&nbsp;{% endfor %}
<p class="help">{{ form.base_path.help_text|safe }}</p>
</div>
<div class="field-box field-home">
{{ form.path_extension.errors }}
<label for="{{ form.path_extension.id_for_label }}"></label>
{{ form.path_extension }}
<p class="help">{{ form.path_extension.help_text|safe }}</p>
</div>
</div>
<div class="form-row ">
{{ form.read_only }} <label for="{{ form.read_only.id_for_label }}" class="vCheckboxLabel">{{ form.read_only.label }}</label>
<p class="help">{{ form.read_only.help_text|safe }}</p>
</div>
</fieldset>
<div>
{% for obj in queryset %}
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
{% endfor %}
<input type="hidden" name="action" value="{{ action_value }}" />
<input type="hidden" name="post" value="{{ post_value|default:'generic_confirmation' }}" />
<input type="submit" value="{{ submit_value|default:_("Save") }}" />
</div>
</form>
{% endblock %}

View file

@ -31,7 +31,7 @@ class WebAppServiceMixin(object):
if [[ $CREATED == 1 && ! $(ls -A %(app_path)s) ]]; then if [[ $CREATED == 1 && ! $(ls -A %(app_path)s) ]]; then
{ {
# Wait for other backends to do their thing or cp under construction # Wait for other backends to do their thing or cp under construction
sleep 1 sleep 10
if [[ ! $(ls -A %(app_path)s) ]]; then if [[ ! $(ls -A %(app_path)s) ]]; then
cp -r %(under_construction_path)s %(app_path)s cp -r %(under_construction_path)s %(app_path)s
chown -R %(user)s:%(group)s %(app_path)s chown -R %(user)s:%(group)s %(app_path)s

View file

@ -179,7 +179,6 @@ WEBAPPS_UNDER_CONSTRUCTION_PATH = Setting('WEBAPPS_UNDER_CONSTRUCTION_PATH', '',
# WEBAPPS_TYPES[webapp_type] = value # WEBAPPS_TYPES[webapp_type] = value
WEBAPPS_PHP_DISABLED_FUNCTIONS = Setting('WEBAPPS_PHP_DISABLED_FUNCTION', ( WEBAPPS_PHP_DISABLED_FUNCTIONS = Setting('WEBAPPS_PHP_DISABLED_FUNCTION', (
'exec', 'exec',
'passthru', 'passthru',
@ -200,7 +199,14 @@ WEBAPPS_PHP_DISABLED_FUNCTIONS = Setting('WEBAPPS_PHP_DISABLED_FUNCTION', (
'openlog', 'openlog',
'escapeshellcmd', 'escapeshellcmd',
'escapeshellarg', 'escapeshellarg',
'dl' 'dl',
'fsockopen',
'pfsockopen',
'stream_socket_client',
# Used for spamming
'getmxrr',
# Used in some php shells
'str_rot13',
)) ))

View file

@ -34,6 +34,7 @@
<form action="" method="post">{% csrf_token %} <form action="" method="post">{% csrf_token %}
{% if form %} {% if form %}
<fieldset class="module aligned"> <fieldset class="module aligned">
{{ form.non_field_errors }}
{% for field in form %} {% for field in form %}
<div class="form-row "> <div class="form-row ">
<div > <div >

View file

@ -148,8 +148,9 @@ def sshrun(addr, command, *args, executable='bash', persist=False, **kwargs):
'ControlPersist=yes', 'ControlPersist=yes',
'ControlPath=~/.ssh/orchestra-%r-%h-%p', 'ControlPath=~/.ssh/orchestra-%r-%h-%p',
)) ))
cmd = 'ssh -o {options} -C root@{addr} {executable}'.format(options=' -o '.join(options), options = ' -o '.join(options)
addr=addr, executable=executable) cmd = 'ssh -o {options} -C root@{addr} {executable}'.format(options=options, addr=addr,
executable=executable)
return run(cmd, *args, stdin=command.encode('utf8'), **kwargs) return run(cmd, *args, stdin=command.encode('utf8'), **kwargs)
@ -181,7 +182,7 @@ class OperationLocked(Exception):
class LockFile(object): class LockFile(object):
""" File-based lock mechanism used for preventing concurrency problems """ """ File-based lock mechanism used for preventing concurrency problems """
def __init__(self, lockfile, expire=5*60, unlocked=False): def __init__(self, lockfile, expire=5*60, unlocked=False):
""" /dev/shm/ can be a good place for storing locks ;) """ # /dev/shm/ can be a good place for storing locks
self.lockfile = lockfile self.lockfile = lockfile
self.expire = expire self.expire = expire
self.unlocked = unlocked self.unlocked = unlocked

View file

@ -11,7 +11,6 @@ kombu==3.0.23
billiard==3.3.0.18 billiard==3.3.0.18
Markdown==2.4 Markdown==2.4
djangorestframework==3.1.1 djangorestframework==3.1.1
paramiko==1.15.2
ecdsa==0.11 ecdsa==0.11
Pygments==1.6 Pygments==1.6
django-filter==0.7 django-filter==0.7