Skip to content
Snippets Groups Projects
Commit 31db16df authored by Tomáš Valenta's avatar Tomáš Valenta
Browse files

implement first permission level - secret readers, group based admin

parent 99a8ec88
No related branches found
No related tags found
No related merge requests found
Pipeline #12005 passed
......@@ -173,6 +173,17 @@ class ContractAdmin(MarkdownxGuardedModelAdmin, FieldsetsInlineMixin, NestedMode
for index, fieldset in enumerate(fieldsets_with_inlines)
]
def get_queryset(self, request):
queryset = super().get_queryset(request)
if not request.user.has_perm("contracts.view_confidential"):
queryset = queryset.filter(is_public=True)
if not request.user.has_perm("contracts.approve"):
queryset = queryset.filter(is_approved=True)
return queryset
def save_model(self, request, obj, form, change) -> None:
if obj.created_by is None:
obj.created_by = request.user
......@@ -262,24 +273,49 @@ class SigneeAdmin(MarkdownxGuardedModelAdmin):
form = SigneeAdminForm
fields = (
readonly_fields = ("load_ares_data_button",)
list_filter = ("entity_type",)
list_display = ("name", "entity_type")
def get_fields(self, request, obj=None):
fields = [
"name",
"entity_type",
"ico_number",
"department",
"role",
]
if (
obj is None # Creating
or obj.entity_has_public_address
or request.user.has_perm("contracts.view_confidential", obj)
):
entity_type_index = fields.index("entity_type")
fields[entity_type_index:entity_type_index] = [
"address_street_with_number",
"address_district",
"address_zip",
"address_country",
"ico_number",
"load_ares_data_button",
]
fields.insert(
fields.index("department") - 1,
"date_of_birth",
"department",
"role",
)
readonly_fields = ("load_ares_data_button",)
if (
obj is None # Allowed to create
or request.user.has_perm("contracts.edit_signee", obj)
):
fields.insert(
fields.index("ico_number"),
"load_ares_data_button"
)
list_filter = ("entity_type",)
list_display = ("name", "entity_type")
return fields
def load_ares_data_button(self, obj):
return format_html(
......
# Generated by Django 4.1.4 on 2023-03-24 10:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contracts', '0012_alter_contractee_address_country_and_more'),
]
operations = [
migrations.AlterField(
model_name='contractee',
name='address_country',
field=models.CharField(default='CZ', max_length=256, verbose_name='Země'),
),
migrations.AlterField(
model_name='signee',
name='address_country',
field=models.CharField(default='CZ', max_length=256, verbose_name='Země'),
),
]
import typing
import logging
import jwt
from django.conf import settings
from django.contrib.auth.models import Group
from django.conf import settings
from pirates.auth import PiratesOIDCAuthenticationBackend
logging.basicConfig(level=logging.DEBUG)
class RegistryOIDCAuthenticationBackend(PiratesOIDCAuthenticationBackend):
def _assign_new_user_groups(self, user, access_token, user_groups=None) -> None:
def _assign_new_user_groups(
self,
user,
access_token: dict,
user_groups: typing.Union[None, list] = None
) -> None:
if user_groups is None:
user_groups = user.groups.all()
for group in access_token["groups"]:
if group.startswith("_"):
continue
group_name = f"sso_{group}"
group = Group.objects.filter(name=group_name)
......@@ -27,9 +36,12 @@ class RegistryOIDCAuthenticationBackend(PiratesOIDCAuthenticationBackend):
if group not in user_groups:
user.groups.add(group)
user.save()
def _remove_old_user_groups(self, user, access_token, user_groups=None) -> None:
def _remove_old_user_groups(
self,
user,
access_token: dict,
user_groups: typing.Union[None, list] = None
) -> None:
if user_groups is None:
user_groups = user.groups.all()
......@@ -50,10 +62,17 @@ class RegistryOIDCAuthenticationBackend(PiratesOIDCAuthenticationBackend):
user_groups = user.groups.all()
self._remove_old_user_groups(
user, decoded_access_token, user_groups=user_groups
user,
decoded_access_token,
user_groups=user_groups
)
self._assign_new_user_groups(
user, decoded_access_token, user_groups=user_groups
user,
decoded_access_token,
user_groups=user_groups
)
user.update_group_based_admin()
user.save(saved_by_auth=True)
return user
......@@ -3,8 +3,9 @@
# exit on error
set -e
# migrate database
# Migrate database
python manage.py makemigrations # Custom Group model
python manage.py migrate
# start webserver
# Start webserver
exec gunicorn -c gunicorn.conf.py registry.wsgi
# Generated by Django 4.1.4 on 2023-03-24 10:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='user',
name='is_staff_based_on_group',
field=models.BooleanField(default=True, verbose_name='Admin přístup dle členství ve skupině'),
),
]
# Generated by Django 4.1.4 on 2023-03-24 10:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0002_user_is_staff_based_on_group'),
]
operations = [
migrations.AlterField(
model_name='user',
name='is_staff_based_on_group',
field=models.BooleanField(default=True, help_text='Určuje, zda bude "Administrační přístup" uživatele definován dle členství ve skupinách, nebo podle speciálního nastavení zde.', verbose_name='Administrační přístup dle členství ve skupině'),
),
]
from django.db import models
from django.contrib.auth.models import Group
from pirates import models as pirates_models
class User(pirates_models.AbstractUser):
is_staff_based_on_group = models.BooleanField(
default=True,
verbose_name="Administrační přístup dle členství ve skupině",
help_text=(
"Určuje, zda bude \"Administrační přístup\" uživatele "
"definován dle členství ve skupinách, nebo podle "
"speciálního nastavení zde."
)
)
def set_unusable_password(self) -> None:
# Purely for compatibility with Guardian
pass
......@@ -14,19 +26,65 @@ class User(pirates_models.AbstractUser):
return f"{first_name}{self.last_name}"
# https://docs.djangoproject.com/en/4.1/ref/models/instances/#customizing-model-loading
@classmethod
def from_db(cls, db, field_names, values):
# Default implementation of from_db() (subject to change and could
# be replaced with super()).
if len(values) != len(cls._meta.concrete_fields):
values = list(values)
values.reverse()
values = [
values.pop() if f.attname in field_names else models.DEFERRED
for f in cls._meta.concrete_fields
]
instance = cls(*values)
instance._state.adding = False
instance._state.db = db
# customization to store the original field values on the instance
instance._loaded_values = dict(
zip(
field_names,
(
value
for value in values
if value is not models.DEFERRED
)
)
)
return instance
def save(self, *args, saved_by_auth: bool = False, **kwargs):
if (
not self._state.adding
and not saved_by_auth
and self._loaded_values["is_staff"] != self.is_staff
):
self.is_staff_based_on_group = False
return super().save(*args, **kwargs)
def update_group_based_admin(self) -> None:
if not self.is_staff_based_on_group:
return
self.is_staff_based_on_group = True
self.is_staff = self.groups.filter(is_staff=True).exists()
@property
def can_approve_contracts(self) -> bool:
# TODO: Do we need the superuser check?
return self.is_superuser or self.has_perm("contracts.approve")
return self.has_perm("contracts.approve")
@property
def can_create_contracts(self) -> bool:
# TODO: Do we need the superuser check?
return self.is_superuser or self.has_perm("contracts.add")
return self.has_perm("contracts.add")
@property
def can_view_confidential(self) -> bool:
return self.is_superuser or self.has_perm("contracts.view_confidential")
return self.has_perm("contracts.view_confidential")
@property
def contracts_to_approve_count(self) -> int:
......@@ -41,3 +99,12 @@ class User(pirates_models.AbstractUser):
app_label = "users"
verbose_name = "Uživatel"
verbose_name_plural = "Uživatelé"
if not hasattr(Group, "is_staff"):
is_staff = models.BooleanField(
default=False,
verbose_name="Administrační přístup",
help_text="Určuje, zda se skupina může přihlásit do správy tohoto webu.",
)
is_staff.contribute_to_class(Group, "is_staff")
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment