mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-17 04:58:16 -06:00
Fix support for object-based permissions
This commit is contained in:
parent
2d206fbe8b
commit
274bbfa22f
@ -1038,63 +1038,29 @@ class ScriptListView(ContentTypePermissionRequiredMixin, View):
|
||||
})
|
||||
|
||||
|
||||
def get_script_module(module, request):
|
||||
return get_object_or_404(ScriptModule.objects.restrict(request.user), file_path__regex=f"^{module}\\.")
|
||||
|
||||
|
||||
class BaseScriptView(generic.ObjectView):
|
||||
class ScriptView(generic.ObjectView):
|
||||
queryset = Script.objects.all()
|
||||
script = None
|
||||
script_class = None
|
||||
jobs = None
|
||||
|
||||
def _init_vars(self, request):
|
||||
if self.script.python_class:
|
||||
self.script_class = self.script.python_class()
|
||||
else:
|
||||
self.script.delete(soft_delete=True)
|
||||
messages.error(request, _("Script class has been deleted from module: ") + str(self.script.module))
|
||||
if not self.script.id:
|
||||
return redirect('extras:script_list')
|
||||
else:
|
||||
return redirect('extras:script_jobs', pk=self.script.id)
|
||||
|
||||
self.jobs = self.script.jobs.all()
|
||||
return None
|
||||
|
||||
def init_vars_or_redirect(self, request, pk):
|
||||
self.script = get_object_or_404(Script.objects.all(), pk=pk)
|
||||
return self._init_vars(request)
|
||||
|
||||
|
||||
class ScriptView(BaseScriptView):
|
||||
|
||||
def get(self, request, pk):
|
||||
if ret := self.init_vars_or_redirect(request, pk):
|
||||
return ret
|
||||
|
||||
form = None
|
||||
if self.script_class:
|
||||
form = self.script_class.as_form(initial=normalize_querydict(request.GET))
|
||||
def get(self, request, **kwargs):
|
||||
script = self.get_object(**kwargs)
|
||||
script_class = script.python_class()
|
||||
form = script_class.as_form(initial=normalize_querydict(request.GET))
|
||||
|
||||
return render(request, 'extras/script.html', {
|
||||
'job_count': self.jobs.count(),
|
||||
'module': self.script.module,
|
||||
'script': self.script,
|
||||
'script_class': self.script_class,
|
||||
'script': script,
|
||||
'script_class': script_class,
|
||||
'form': form,
|
||||
'job_count': script.jobs.count(),
|
||||
})
|
||||
|
||||
def post(self, request, pk):
|
||||
if not request.user.has_perm('extras.run_script'):
|
||||
def post(self, request, **kwargs):
|
||||
script = self.get_object(**kwargs)
|
||||
script_class = script.python_class()
|
||||
|
||||
if not request.user.has_perm('extras.run_script', obj=script):
|
||||
return HttpResponseForbidden()
|
||||
|
||||
if ret := self.init_vars_or_redirect(request, pk):
|
||||
return ret
|
||||
|
||||
form = None
|
||||
if self.script_class:
|
||||
form = self.script_class.as_form(request.POST, request.FILES)
|
||||
form = script_class.as_form(request.POST, request.FILES)
|
||||
|
||||
# Allow execution only if RQ worker process is running
|
||||
if not get_workers_for_queue('default'):
|
||||
@ -1102,77 +1068,58 @@ class ScriptView(BaseScriptView):
|
||||
elif form.is_valid():
|
||||
job = Job.enqueue(
|
||||
run_script,
|
||||
instance=self.script,
|
||||
name=self.script_class.class_name,
|
||||
instance=script,
|
||||
name=script_class.class_name,
|
||||
user=request.user,
|
||||
schedule_at=form.cleaned_data.pop('_schedule_at'),
|
||||
interval=form.cleaned_data.pop('_interval'),
|
||||
data=form.cleaned_data,
|
||||
request=copy_safe_request(request),
|
||||
job_timeout=self.script.python_class.job_timeout,
|
||||
job_timeout=script.python_class.job_timeout,
|
||||
commit=form.cleaned_data.pop('_commit')
|
||||
)
|
||||
|
||||
return redirect('extras:script_result', job_pk=job.pk)
|
||||
|
||||
return render(request, 'extras/script.html', {
|
||||
'job_count': self.jobs.count(),
|
||||
'module': self.script.module,
|
||||
'script': self.script,
|
||||
'script_class': self.script_class,
|
||||
'script': script,
|
||||
'script_class': script.python_class(),
|
||||
'form': form,
|
||||
'job_count': script.jobs.count(),
|
||||
})
|
||||
|
||||
|
||||
class ScriptSourceView(BaseScriptView):
|
||||
class ScriptSourceView(generic.ObjectView):
|
||||
queryset = Script.objects.all()
|
||||
|
||||
def get(self, request, pk):
|
||||
if ret := self.init_vars_or_redirect(request, pk):
|
||||
return ret
|
||||
def get(self, request, **kwargs):
|
||||
script = self.get_object(**kwargs)
|
||||
|
||||
return render(request, 'extras/script/source.html', {
|
||||
'job_count': self.jobs.count(),
|
||||
'module': self.script.module,
|
||||
'script': self.script,
|
||||
'script_class': self.script_class,
|
||||
'script': script,
|
||||
'script_class': script.python_class(),
|
||||
'job_count': script.jobs.count(),
|
||||
'tab': 'source',
|
||||
})
|
||||
|
||||
|
||||
class ScriptJobsView(generic.ObjectView):
|
||||
queryset = Script.objects.all()
|
||||
script = None
|
||||
script_class = None
|
||||
jobs = None
|
||||
|
||||
def get_required_permission(self):
|
||||
return 'extras.view_script'
|
||||
|
||||
def get(self, request, pk):
|
||||
self.script = get_object_or_404(Script.objects.all(), pk=pk)
|
||||
|
||||
if self.script.python_class:
|
||||
self.script_class = self.script.python_class()
|
||||
else:
|
||||
self.script.delete(soft_delete=True)
|
||||
if not self.script.id:
|
||||
messages.error(request, _("Script class has been deleted from module: ") + str(self.script.module))
|
||||
return redirect('extras:script_list')
|
||||
|
||||
self.jobs = self.script.jobs.all()
|
||||
def get(self, request, **kwargs):
|
||||
script = self.get_object(**kwargs)
|
||||
|
||||
jobs_table = JobTable(
|
||||
data=self.jobs,
|
||||
data=script.jobs.all(),
|
||||
orderable=False,
|
||||
user=request.user
|
||||
)
|
||||
jobs_table.configure(request)
|
||||
|
||||
return render(request, 'extras/script/jobs.html', {
|
||||
'job_count': self.jobs.count(),
|
||||
'module': self.script.module,
|
||||
'script': self.script,
|
||||
'script': script,
|
||||
'table': jobs_table,
|
||||
'job_count': script.jobs.count(),
|
||||
'tab': 'jobs',
|
||||
})
|
||||
|
||||
@ -1196,19 +1143,17 @@ class LegacyScriptRedirectView(ContentTypePermissionRequiredMixin, View):
|
||||
return redirect(f'{url}{path}')
|
||||
|
||||
|
||||
class ScriptResultView(BaseScriptView):
|
||||
class ScriptResultView(generic.ObjectView):
|
||||
queryset = Job.objects.all()
|
||||
|
||||
def get_required_permission(self):
|
||||
return 'extras.view_script'
|
||||
|
||||
def get(self, request, job_pk):
|
||||
object_type = ContentType.objects.get_by_natural_key(app_label='extras', model='script')
|
||||
job = get_object_or_404(Job.objects.all(), pk=job_pk, object_type=object_type)
|
||||
|
||||
script = job.object
|
||||
def get(self, request, **kwargs):
|
||||
job = get_object_or_404(Job.objects.all(), pk=kwargs.get('job_pk'))
|
||||
|
||||
context = {
|
||||
'script': script,
|
||||
'script': job.object,
|
||||
'job': job,
|
||||
}
|
||||
if job.data and 'log' in job.data:
|
||||
|
@ -2,6 +2,7 @@
|
||||
{% load helpers %}
|
||||
{% load form_helpers %}
|
||||
{% load log_levels %}
|
||||
{% load perms %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
@ -32,9 +33,17 @@
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="float-end">
|
||||
<div class="text-end">
|
||||
<a href="{% url 'extras:script_list' %}" class="btn btn-outline-secondary">{% trans "Cancel" %}</a>
|
||||
<button type="submit" name="_run" class="btn btn-primary"{% if not perms.extras.run_script %} disabled="disabled"{% endif %}><i class="mdi mdi-play"></i> {% trans "Run Script" %}</button>
|
||||
{% if not request.user|can_run:script or not script.is_executable %}
|
||||
<button class="btn btn-primary" disabled>
|
||||
<i class="mdi mdi-play"></i> {% trans "Run Script" %}
|
||||
</button>
|
||||
{% else %}
|
||||
<button type="submit" name="_run" class="btn btn-primary">
|
||||
<i class="mdi mdi-play"></i> {% trans "Run Script" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<li class="breadcrumb-item"><a href="{% url 'extras:script_list' %}">{% trans "Scripts" %}</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'extras:script_list' %}#module{{ module.pk }}">{{ module|bettertitle }}</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'extras:script_list' %}#module{{ script.module.pk }}">{{ script.module|bettertitle }}</a></li>
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block subtitle %}
|
||||
|
@ -59,7 +59,9 @@
|
||||
<a href="{% url 'extras:script' script.pk %}" id="{{ script.module }}.{{ script.class_name }}">{{ script.name }}</a>
|
||||
{% else %}
|
||||
<a href="{% url 'extras:script_jobs' script.pk %}" id="{{ script.module }}.{{ script.class_name }}">{{ script.name }}</a>
|
||||
<i class="mdi mdi-alert"></i>
|
||||
<span class="text-danger">
|
||||
<i class="mdi mdi-alert" title="{% trans "Script is no longer present in the source file" %}"></i>
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ script.description|markdown|placeholder }}</td>
|
||||
@ -75,7 +77,7 @@
|
||||
<td>{{ ''|placeholder }}</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
{% if perms.extras.run_script %}
|
||||
{% if request.user|can_run:script and script.is_executable %}
|
||||
<div class="float-end d-print-none">
|
||||
<form action="{% url 'extras:script' script.pk %}" method="post">
|
||||
{% csrf_token %}
|
||||
|
@ -376,11 +376,6 @@ class ObjectPermissionForm(forms.ModelForm):
|
||||
for ct in object_types:
|
||||
model = ct.model_class()
|
||||
|
||||
if model._meta.model_name in ['script', 'report']:
|
||||
raise forms.ValidationError({
|
||||
'constraints': _('Constraints are not supported for this object type.')
|
||||
})
|
||||
|
||||
try:
|
||||
tokens = {
|
||||
CONSTRAINT_TOKEN_USER: 0, # Replace token with a null user ID
|
||||
|
@ -6,6 +6,7 @@ __all__ = (
|
||||
'can_add',
|
||||
'can_change',
|
||||
'can_delete',
|
||||
'can_run',
|
||||
'can_sync',
|
||||
'can_view',
|
||||
)
|
||||
@ -42,3 +43,8 @@ def can_delete(user, instance):
|
||||
@register.filter()
|
||||
def can_sync(user, instance):
|
||||
return _check_permission(user, instance, 'sync')
|
||||
|
||||
|
||||
@register.filter()
|
||||
def can_run(user, instance):
|
||||
return _check_permission(user, instance, 'run')
|
||||
|
Loading…
Reference in New Issue
Block a user