Fixes on systemusers ACL permissions
This commit is contained in:
parent
2877f64d9d
commit
20ccadf3e1
7
TODO.md
7
TODO.md
|
@ -359,3 +359,10 @@ resorce monitoring more efficient, less mem an better queries for calc current d
|
|||
|
||||
ciphers=['arcfour128', 'aes256']
|
||||
http://paramiko-docs.readthedocs.org/en/latest/api/transport.html
|
||||
|
||||
|
||||
|
||||
* Grant and revoke permissions
|
||||
|
||||
setfacl: /home/marcay//logs: Operation not permitted
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import socket
|
|||
import sys
|
||||
import select
|
||||
|
||||
import paramiko
|
||||
from celery.datastructures import ExceptionInfo
|
||||
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
|
||||
"""
|
||||
import paramiko
|
||||
script = '\n'.join(cmds)
|
||||
script = script.replace('\r', '')
|
||||
log.state = log.STARTED
|
||||
log.script = script
|
||||
log.save(update_fields=('script', 'state'))
|
||||
log.save(update_fields=('script', 'state', 'updated_at'))
|
||||
if not cmds:
|
||||
return
|
||||
channel = None
|
||||
|
@ -48,7 +48,7 @@ def Paramiko(backend, log, server, cmds, async=False):
|
|||
logger.error('%s timed out on %s' % (backend, addr))
|
||||
log.state = log.TIMEOUT
|
||||
log.stderr = str(e)
|
||||
log.save(update_fields=['state', 'stderr'])
|
||||
log.save(update_fields=('state', 'stderr', 'updated_at'))
|
||||
return
|
||||
paramiko_connections[addr] = ssh
|
||||
transport = ssh.get_transport()
|
||||
|
@ -73,7 +73,7 @@ def Paramiko(backend, log, server, cmds, async=False):
|
|||
while part:
|
||||
log.stderr += part
|
||||
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 second:
|
||||
break
|
||||
|
@ -95,7 +95,7 @@ def Paramiko(backend, log, server, cmds, async=False):
|
|||
finally:
|
||||
if log.state == log.STARTED:
|
||||
log.state = log.ABORTED
|
||||
log.save(update_fields=['state'])
|
||||
log.save(update_fields=('state', 'updated_at'))
|
||||
if channel is not None:
|
||||
channel.close()
|
||||
|
||||
|
@ -108,25 +108,25 @@ def OpenSSH(backend, log, server, cmds, async=False):
|
|||
script = script.replace('\r', '')
|
||||
log.state = log.STARTED
|
||||
log.script = script
|
||||
log.save(update_fields=('script', 'state'))
|
||||
log.save(update_fields=('script', 'state', 'updated_at'))
|
||||
if not cmds:
|
||||
return
|
||||
channel = None
|
||||
ssh = None
|
||||
try:
|
||||
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))
|
||||
if async:
|
||||
second = False
|
||||
for state in ssh:
|
||||
log.stdout += state.stdout.decode('utf8')
|
||||
log.stderr += state.stderr.decode('utf8')
|
||||
log.save()
|
||||
log.save(update_fields=('stdout', 'stderr', 'updated_at'))
|
||||
log.exit_code = state.exit_code
|
||||
else:
|
||||
log.stdout = ssh.stdout
|
||||
log.stderr = ssh.stderr
|
||||
log.stdout = ssh.stdout.decode('utf8')
|
||||
log.stderr = ssh.stderr.decode('utf8')
|
||||
log.exit_code = ssh.exit_code
|
||||
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))
|
||||
|
@ -140,7 +140,7 @@ def OpenSSH(backend, log, server, cmds, async=False):
|
|||
finally:
|
||||
if log.state == log.STARTED:
|
||||
log.state = log.ABORTED
|
||||
log.save(update_fields=['state'])
|
||||
log.save(update_fields=('state', 'updated_at'))
|
||||
|
||||
|
||||
def SSH(*args, **kwargs):
|
||||
|
@ -150,12 +150,11 @@ def SSH(*args, **kwargs):
|
|||
|
||||
|
||||
def Python(backend, log, server, cmds, async=False):
|
||||
# TODO collect stdout?
|
||||
script = [ str(cmd.func.__name__) + str(cmd.args) for cmd in cmds ]
|
||||
script = json.dumps(script, indent=4).replace('"', '')
|
||||
log.state = log.STARTED
|
||||
log.script = '\n'.join([log.script, script])
|
||||
log.save(update_fields=('script', 'state'))
|
||||
log.script = '\n'.join((log.script, script))
|
||||
log.save(update_fields=('script', 'state', 'updated_at'))
|
||||
try:
|
||||
for cmd in cmds:
|
||||
with CaptureStdout() as stdout:
|
||||
|
@ -163,7 +162,7 @@ def Python(backend, log, server, cmds, async=False):
|
|||
for line in stdout:
|
||||
log.stdout += line + '\n'
|
||||
if async:
|
||||
log.save(update_fields=['stdout'])
|
||||
log.save(update_fields=('stdout', 'updated_at'))
|
||||
except:
|
||||
log.exit_code = 1
|
||||
log.state = log.FAILURE
|
||||
|
|
|
@ -43,9 +43,9 @@ ORCHESTRATION_BACKEND_CLEANUP_DAYS = Setting('ORCHESTRATION_BACKEND_CLEANUP_DAYS
|
|||
|
||||
ORCHESTRATION_SSH_METHOD_BACKEND = Setting('ORCHESTRATION_SSH_METHOD_BACKEND',
|
||||
'orchestra.contrib.orchestration.methods.OpenSSH',
|
||||
help_text=_("Two methods provided:<br>"
|
||||
"<tt>orchestra.contrib.orchestration.methods.OpenSSH</tt> with ControlPersist.<br>"
|
||||
"<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>"
|
||||
help_text=_("Two methods are provided:<br>"
|
||||
"1) <tt>orchestra.contrib.orchestration.methods.OpenSSH</tt> with ControlPersist.<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. "
|
||||
"Paramiko, in contrast, has a per worker connection pool.")
|
||||
)
|
||||
|
|
|
@ -155,6 +155,29 @@ match_price.help_text = _("Only <b>the rate</b> with a) inmediate inferior metri
|
|||
|
||||
|
||||
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.help_text = _("Produces the best possible price given all active rating lines.")
|
||||
|
|
|
@ -7,54 +7,67 @@ from django.template.response import TemplateResponse
|
|||
from django.utils.translation import ungettext, ugettext_lazy as _
|
||||
|
||||
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
|
||||
for user in queryset:
|
||||
account_id = account_id or user.account_id
|
||||
if user.account_id != account_id:
|
||||
messages.error("Users from the same account should be selected.")
|
||||
user = queryset[0]
|
||||
form = PermissionForm(user)
|
||||
if request.method == 'POST':
|
||||
form = GrantPermissionForm(user, request.POST)
|
||||
form = PermissionForm(user, request.POST)
|
||||
if form.is_valid():
|
||||
cleaned_data = form.cleaned_data
|
||||
to = os.path.join(cleaned_data['base_path'], cleaned_data['path_extension'])
|
||||
ro = cleaned_data['read_only']
|
||||
operations = []
|
||||
for user in queryset:
|
||||
user.grant_to = to
|
||||
user.grant_ro = ro
|
||||
# DOn't collect, execute right away for path validation
|
||||
OperationsMiddleware.collect('grant_permission', instance=user)
|
||||
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']
|
||||
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 = {
|
||||
'type': _("read-only") if ro else _("read-write"),
|
||||
'to': to,
|
||||
'action': verbose_action,
|
||||
'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)
|
||||
# TODO feedback message
|
||||
logs = Operation.execute(operations)
|
||||
helpers.message_user(request, logs)
|
||||
return
|
||||
opts = modeladmin.model._meta
|
||||
app_label = opts.app_label
|
||||
context = {
|
||||
'title': _("Grant permission"),
|
||||
'action_name': _("Grant permission"),
|
||||
'action_value': 'grant_permission',
|
||||
'title': _("Set permission"),
|
||||
'action_name': _("Set permission"),
|
||||
'action_value': 'set_permission',
|
||||
'queryset': queryset,
|
||||
'opts': opts,
|
||||
'obj': user,
|
||||
'app_label': app_label,
|
||||
'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)
|
||||
grant_permission.url_name = 'grant-permission'
|
||||
grant_permission.verbose_name = _("Grant permission")
|
||||
set_permission.url_name = 'set-permission'
|
||||
set_permission.verbose_name = _("Set permission")
|
||||
|
||||
|
||||
def delete_selected(modeladmin, request, queryset):
|
||||
|
|
|
@ -6,7 +6,7 @@ from orchestra.admin.actions import disable
|
|||
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
|
||||
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 .forms import SystemUserCreationForm, SystemUserChangeForm
|
||||
from .models import SystemUser
|
||||
|
@ -41,7 +41,7 @@ class SystemUserAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, Extende
|
|||
add_form = SystemUserCreationForm
|
||||
form = SystemUserChangeForm
|
||||
ordering = ('-id',)
|
||||
actions = (delete_selected, grant_permission, disable)
|
||||
actions = (delete_selected, set_permission, disable)
|
||||
change_view_actions = actions
|
||||
|
||||
def display_main(self, user):
|
||||
|
|
|
@ -15,7 +15,7 @@ class UNIXUserBackend(ServiceController):
|
|||
"""
|
||||
verbose_name = _("UNIX user")
|
||||
model = 'systemusers.SystemUser'
|
||||
actions = ('save', 'delete', 'grant_permission')
|
||||
actions = ('save', 'delete', 'set_permission', 'validate_path')
|
||||
doc_settings = (settings,
|
||||
('SYSTEMUSERS_DEFAULT_GROUP_MEMBERS', 'SYSTEMUSERS_MOVE_ON_DELETE_PATH')
|
||||
)
|
||||
|
@ -65,17 +65,69 @@ class UNIXUserBackend(ServiceController):
|
|||
else:
|
||||
self.append("rm -fr %(base_home)s" % context)
|
||||
|
||||
def grant_permission(self, user):
|
||||
# TODO
|
||||
def set_permission(self, user):
|
||||
context = self.get_context(user)
|
||||
context.update({
|
||||
'to': user.grant_to,
|
||||
'ro': user.grant_ro,
|
||||
'perm_action': user.set_perm_action,
|
||||
'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:
|
||||
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):
|
||||
if user.is_main:
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import textwrap
|
||||
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import ngettext, ugettext_lazy as _
|
||||
|
||||
from orchestra.contrib.orchestration import Operation
|
||||
from orchestra.forms import UserCreationForm, UserChangeForm
|
||||
|
||||
from . import settings
|
||||
|
@ -79,21 +81,44 @@ class SystemUserChangeForm(SystemUserFormMixin, UserChangeForm):
|
|||
pass
|
||||
|
||||
|
||||
class GrantPermissionForm(forms.Form):
|
||||
base_path = forms.ChoiceField(label=_("Grant access to"), choices=(),
|
||||
help_text=_("User will be granted access to this directory."))
|
||||
path_extension = forms.CharField(label=_("Path extension"), required=False, initial='',
|
||||
class PermissionForm(forms.Form):
|
||||
set_action = forms.ChoiceField(label=_("Action"), initial='grant',
|
||||
choices=(
|
||||
('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."))
|
||||
read_only = forms.BooleanField(label=_("Read only"), initial=False, required=False,
|
||||
help_text=_("Designates whether the permissions granted will be read-only or read/write."))
|
||||
permissions = forms.ChoiceField(label=_("Permissions"), initial='read-write',
|
||||
choices=(
|
||||
('read-write', _("Read and write")),
|
||||
('read-only', _("Read only")),
|
||||
('write-only', _("Write only"))
|
||||
))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
instance = args[0]
|
||||
self.instance = args[0]
|
||||
super_args = []
|
||||
if len(args) > 1:
|
||||
super_args.append(args[1])
|
||||
super(GrantPermissionForm, self).__init__(*super_args, **kwargs)
|
||||
related_users = type(instance).objects.filter(account=instance.account_id)
|
||||
self.fields['base_path'].choices = (
|
||||
super(PermissionForm, self).__init__(*super_args, **kwargs)
|
||||
related_users = type(self.instance).objects.filter(account=self.instance.account_id)
|
||||
self.fields['base_home'].choices = (
|
||||
(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
|
||||
|
|
|
@ -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),
|
||||
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')"),
|
||||
)
|
||||
|
|
|
@ -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>
|
||||
› <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a>
|
||||
› <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
|
||||
{% if obj %}
|
||||
› <a href="{% url opts|admin_urlname:'change' obj.pk %}">{{ obj }}</a>
|
||||
› {{ action_name }}
|
||||
{% elif add %}
|
||||
› <a href="../">{% trans "Add" %} {{ opts.verbose_name }}</a>
|
||||
› {{ action_name }}
|
||||
{% else %}
|
||||
› {{ 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" %} {% 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 %}
|
||||
|
|
@ -31,7 +31,7 @@ class WebAppServiceMixin(object):
|
|||
if [[ $CREATED == 1 && ! $(ls -A %(app_path)s) ]]; then
|
||||
{
|
||||
# Wait for other backends to do their thing or cp under construction
|
||||
sleep 1
|
||||
sleep 10
|
||||
if [[ ! $(ls -A %(app_path)s) ]]; then
|
||||
cp -r %(under_construction_path)s %(app_path)s
|
||||
chown -R %(user)s:%(group)s %(app_path)s
|
||||
|
|
|
@ -179,7 +179,6 @@ WEBAPPS_UNDER_CONSTRUCTION_PATH = Setting('WEBAPPS_UNDER_CONSTRUCTION_PATH', '',
|
|||
# WEBAPPS_TYPES[webapp_type] = value
|
||||
|
||||
|
||||
|
||||
WEBAPPS_PHP_DISABLED_FUNCTIONS = Setting('WEBAPPS_PHP_DISABLED_FUNCTION', (
|
||||
'exec',
|
||||
'passthru',
|
||||
|
@ -200,7 +199,14 @@ WEBAPPS_PHP_DISABLED_FUNCTIONS = Setting('WEBAPPS_PHP_DISABLED_FUNCTION', (
|
|||
'openlog',
|
||||
'escapeshellcmd',
|
||||
'escapeshellarg',
|
||||
'dl'
|
||||
'dl',
|
||||
'fsockopen',
|
||||
'pfsockopen',
|
||||
'stream_socket_client',
|
||||
# Used for spamming
|
||||
'getmxrr',
|
||||
# Used in some php shells
|
||||
'str_rot13',
|
||||
))
|
||||
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
<form action="" method="post">{% csrf_token %}
|
||||
{% if form %}
|
||||
<fieldset class="module aligned">
|
||||
{{ form.non_field_errors }}
|
||||
{% for field in form %}
|
||||
<div class="form-row ">
|
||||
<div >
|
||||
|
|
|
@ -148,8 +148,9 @@ def sshrun(addr, command, *args, executable='bash', persist=False, **kwargs):
|
|||
'ControlPersist=yes',
|
||||
'ControlPath=~/.ssh/orchestra-%r-%h-%p',
|
||||
))
|
||||
cmd = 'ssh -o {options} -C root@{addr} {executable}'.format(options=' -o '.join(options),
|
||||
addr=addr, executable=executable)
|
||||
options = ' -o '.join(options)
|
||||
cmd = 'ssh -o {options} -C root@{addr} {executable}'.format(options=options, addr=addr,
|
||||
executable=executable)
|
||||
return run(cmd, *args, stdin=command.encode('utf8'), **kwargs)
|
||||
|
||||
|
||||
|
@ -181,7 +182,7 @@ class OperationLocked(Exception):
|
|||
class LockFile(object):
|
||||
""" File-based lock mechanism used for preventing concurrency problems """
|
||||
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.expire = expire
|
||||
self.unlocked = unlocked
|
||||
|
|
|
@ -11,7 +11,6 @@ kombu==3.0.23
|
|||
billiard==3.3.0.18
|
||||
Markdown==2.4
|
||||
djangorestframework==3.1.1
|
||||
paramiko==1.15.2
|
||||
ecdsa==0.11
|
||||
Pygments==1.6
|
||||
django-filter==0.7
|
||||
|
|
Loading…
Reference in a new issue