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']
|
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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.")
|
||||||
)
|
)
|
||||||
|
|
|
@ -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.")
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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')"),
|
||||||
|
)
|
||||||
|
|
|
@ -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
|
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
|
||||||
|
|
|
@ -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',
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 >
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue