Skip to content

Commit

Permalink
Add requester permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
a-belhadj committed Sep 27, 2023
1 parent a799840 commit f949f52
Show file tree
Hide file tree
Showing 19 changed files with 361 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Squest/utils/squest_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,6 @@ def get_content_type(self):
def get_absolute_url(self):
content_type = self.get_content_type()
return reverse(f"{content_type.app_label}:{content_type.model}_details", args=[self.pk])

def is_owner(self, user):
return False
13 changes: 13 additions & 0 deletions Squest/utils/squest_rbac.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.mixins import PermissionRequiredMixin
from profiles.models import RequesterPermission
from profiles.models.squest_permission import Permission
from django.db.models import Q
from django.utils.safestring import mark_safe
Expand Down Expand Up @@ -79,5 +80,17 @@ def has_perm(self, user_obj, perm, obj=None):
codename=codename,
content_type__app_label=app_label)
).exists()
if permission_granted:
cache.set(key, permission_granted, 60)
return permission_granted
if obj:
try:
if obj.is_owner(user_obj):
requester_permission = RequesterPermission.load()
permission_granted = Permission.objects.filter(requesterpermission=requester_permission,
codename=codename,
content_type__app_label=app_label).exists()
except AttributeError:
logger.debug("is_owner method not found")
cache.set(key, permission_granted, 60)
return permission_granted
18 changes: 18 additions & 0 deletions profiles/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,28 @@ def insert_default_user_permissions(sender, **kwargs):
)
)

def insert_default_requester_permissions(sender, **kwargs):
from profiles.default_rbac.default_requester_permissions import default_requester_permissions
from profiles.models import RequesterPermission
from profiles.models.squest_permission import Permission
requester_permission, created = RequesterPermission.objects.get_or_create(name="RequesterPermission")
if created:
codenames = list(
map(lambda default_permissions: default_permissions.split('.')[1], default_requester_permissions))
app_labels = list(
map(lambda default_permissions: default_permissions.split('.')[0], default_requester_permissions))
requester_permission.default_permissions.add(
*Permission.objects.filter(
codename__in=codenames,
content_type__app_label__in=app_labels
)
)


class ProfilesConfig(AppConfig):
name = 'profiles'

def ready(self):
post_migrate.connect(create_roles, sender=self)
post_migrate.connect(insert_default_user_permissions, sender=self)
post_migrate.connect(insert_default_requester_permissions, sender=self)
11 changes: 11 additions & 0 deletions profiles/default_rbac/default_requester_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
default_requester_permissions = [
'service_catalog.view_instance',
'service_catalog.view_request',
'service_catalog.view_support',
'service_catalog.add_support',
'service_catalog.view_supportmessage',
'service_catalog.add_supportmessage',
'service_catalog.view_requestmessage',
'service_catalog.add_requestmessage',
'service_catalog.request_on_instance'
]
1 change: 1 addition & 0 deletions profiles/forms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
from .organization_forms import *
from .role_forms import *
from .globalpermission_forms import *
from .requesterpermission_forms import *
8 changes: 8 additions & 0 deletions profiles/forms/requesterpermission_forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from Squest.utils.squest_model_form import SquestModelForm
from profiles.models import RequesterPermission


class RequesterPermissionForm(SquestModelForm):
class Meta:
model = RequesterPermission
fields = ["default_permissions"]
25 changes: 25 additions & 0 deletions profiles/migrations/0020_requesterpermission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 3.2.13 on 2023-09-26 15:39

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('profiles', '0019_alter_quota_options'),
]

operations = [
migrations.CreateModel(
name='RequesterPermission',
fields=[
('abstractscope_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='profiles.abstractscope')),
('default_permissions', models.ManyToManyField(blank=True, help_text='Permissions assigned to the owner of Squest objects.', limit_choices_to={'content_type__app_label__in': ['service_catalog']}, to='profiles.Permission')),
],
options={
'default_permissions': ('add', 'change', 'delete', 'view', 'list'),
},
bases=('profiles.abstractscope',),
),
]
1 change: 1 addition & 0 deletions profiles/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
from profiles.models.globalpermission import GlobalPermission
from profiles.models.quota import Quota
from profiles.models.squest_permission import Permission
from profiles.models.requesterpermission import RequesterPermission
50 changes: 50 additions & 0 deletions profiles/models/requesterpermission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from django.contrib.auth.models import User
from django.core.cache import cache
from django.db.models import ManyToManyField
from django.urls import reverse

from profiles.models import AbstractScope
from profiles.models.squest_permission import Permission


class RequesterPermission(AbstractScope):
class Meta:
default_permissions = ('add', 'change', 'delete', 'view', 'list')

default_permissions = ManyToManyField(
Permission,
blank=True,
help_text="Permissions assigned to the owner of Squest objects.",
limit_choices_to={"content_type__app_label__in": ["service_catalog"]}
)

def __str__(self):
return self.name

def save(self, *args, **kwargs):
super(RequesterPermission, self).save(*args, **kwargs)
self.set_cache()

def set_cache(self):
cache.set(self.__class__.__name__, self)

def get_absolute_url(self):
return reverse("profiles:requesterpermission_details")

def delete(self, *args, **kwargs):
pass

@classmethod
def load(cls):
if cache.get(cls.__name__) is None:
obj, created = RequesterPermission.objects.get_or_create(name="RequesterPermission")
if not created:
obj.set_cache()
return obj
return cache.get(cls.__name__)

def get_potential_users(self):
return User.objects.all()

def get_scopes(self):
return AbstractScope.objects.filter(id=self.id)
9 changes: 9 additions & 0 deletions profiles/templatetags/squest_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,15 @@ def generate_sidebar(user):
'active': [
"globalpermission_default_permissions", "globalpermission_edit"
]
},
{
'name': 'Requester permissions',
'view_name': 'profiles:requesterpermission_details',
'icon': 'fas fa-check',
'permission_required': 'profiles.list_requesterpermission',
'active': [
"requesterpermission_details", "requesterpermission_edit"
]
}
],
},
Expand Down
3 changes: 3 additions & 0 deletions profiles/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
# User
path('user/', views.UserListView.as_view(), name='user_list'),
path('user/<int:pk>/', views.UserDetailsView.as_view(), name='user_details'),
# Global Permission
path('requester-permission/', views.RequesterPermissionDetailsView.as_view(), name="requesterpermission_details"),
path('requester-permission/edit/', views.RequesterPermissionEditView.as_view(),name="requesterpermission_edit"),

# Global Permission
path('global-permission/', views.GlobalPermissionRBACView.as_view(), name="globalpermission_rbac"),
Expand Down
1 change: 1 addition & 0 deletions profiles/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
from profiles.views.user import *
from profiles.views.role import *
from profiles.views.globalpermission import *
from profiles.views.requesterpermission import *
from profiles.views.quota import *
from profiles.views.permission import *
49 changes: 49 additions & 0 deletions profiles/views/requesterpermission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from Squest.utils.squest_views import *
from profiles.forms import RequesterPermissionForm
from profiles.models import RequesterPermission
from profiles.tables import PermissionTable


class RequesterPermissionEditView(SquestUpdateView):
model = RequesterPermission
form_class = RequesterPermissionForm

def get_object(self, queryset=None):
return RequesterPermission.load()

def get_success_url(self):
return reverse_lazy('profiles:requesterpermission_details')

def dispatch(self, request, *args, **kwargs):
self.kwargs['pk'] = self.get_object().id
kwargs['pk'] = self.kwargs.get('pk')
return super().dispatch(request, *args, **kwargs)

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['breadcrumbs'] = [
{'text': 'Requester permissions', 'url': reverse_lazy('profiles:requesterpermission_details')},
{'text': f'Edit', 'url': ""},
]
return context


class RequesterPermissionDetailsView(SquestDetailView):
model = RequesterPermission

def get_object(self, queryset=None):
return RequesterPermission.load()

def dispatch(self, request, *args, **kwargs):
self.kwargs['pk'] = self.get_object().id
kwargs['pk'] = self.kwargs.get('pk')
return super().dispatch(request, *args, **kwargs)

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['breadcrumbs'] = None
context['title'] = "Requester Permission"
permission_table = PermissionTable(self.object.default_permissions.all())
permission_table.exclude = ("actions",)
context['default_permissions'] = permission_table
return context
15 changes: 14 additions & 1 deletion service_catalog/models/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ class Meta:
def get_q_filter(cls, user, perm):
from profiles.models import Team
app_label, codename = perm.split(".")

from profiles.models import RequesterPermission
requesterpermission = RequesterPermission.load()
additional_q = Q()
if requesterpermission.default_permissions.filter(
codename=codename,
content_type__app_label=app_label
).exists():
additional_q = Q(requester=user)

return Q(
# Quota scope
## Quota scope - Org - User
Expand All @@ -77,11 +87,14 @@ def get_q_filter(cls, user, perm):
org__roles__permissions__codename=codename,
org__roles__permissions__content_type__app_label=app_label
)
)
) | additional_q

def get_scopes(self):
return self.quota_scope.get_scopes()

def is_owner(self, user):
return self.requester == user

def __str__(self):
return f"{self.name} (#{self.id})"

Expand Down
5 changes: 5 additions & 0 deletions service_catalog/models/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class RequestMessage(Message):
def get_scopes(self):
return self.request.get_scopes()

def is_owner(self, user):
return self.request.is_owner(user)

class SupportMessage(Message):
support = ForeignKey(Support,
Expand All @@ -47,3 +49,6 @@ class SupportMessage(Message):

def get_scopes(self):
return self.support.get_scopes()

def is_owner(self, user):
return self.support.is_owner(user)
3 changes: 3 additions & 0 deletions service_catalog/models/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ def get_q_filter(cls, user, perm):
instance__in=Instance.get_queryset_for_user(user, perm)
)

def is_owner(self, user):
return self.instance.is_owner(user) or self.user == user

def get_scopes(self):
return self.instance.get_scopes()

Expand Down
3 changes: 3 additions & 0 deletions service_catalog/models/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,8 @@ def get_q_filter(cls, user, perm):
instance__in=Instance.get_queryset_for_user(user, perm)
)

def is_owner(self, user):
return self.instance.is_owner(user) or self.opened_by == user

def get_scopes(self):
return self.instance.get_scopes()
35 changes: 35 additions & 0 deletions templates/profiles/requesterpermission_detail.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{% extends 'base.html' %}
{% block title %}
Default permissions
{% endblock %}
{% load render_table from django_tables2 %}
{% load static %}
{% block header_button %}
{% has_perm request.user "profiles.change_requesterpermission" object as can_change_requester_perm %}
{% if can_change_requester_perm %}
<a href="{% url 'profiles:requesterpermission_edit' %}"
class="btn btn-primary">
<i class="fas fa-pencil-alt"></i>
</a>
{% endif %}
{% endblock %}

{% block main %}
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<div class="callout callout-info">
List of permissions granted to the requester of an Instance and all related Requests and Supports
</div>
{% render_table default_permissions %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_script %}
<script>add_tab_management();</script>
{% endblock %}
Loading

0 comments on commit f949f52

Please sign in to comment.