Commit 6d8dc023 authored by Lukas Burgey's avatar Lukas Burgey
Browse files

Merge branch 'privileged-users' into dev

This adds permission checks to endpoints and the new upstream user type
parents 1910b426 3a02efcf
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import Group as AuthGroup
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin
from . import models
from .models import deployments
from .auth.v1.models import OIDCConfig
from .auth.v1.models.vo import VO, Group, Entitlement, EntitlementNameSpace
class TypeFilter(admin.SimpleListFilter):
title = 'Type'
parameter_name = 'user_type'
def lookups(self, request, model_admin):
return models.User.TYPE_CHOICES
def queryset(self, request, queryset):
if self.value():
return queryset.filter(user_type=self.value())
return queryset
class ClientAdmin(UserAdmin):
list_filter = (TypeFilter,)
admin.site.register(models.User, ClientAdmin)
admin.site.register(models.SSHPublicKey)
admin.site.unregister(AuthGroup)
@admin.register(Group)
class GroupAdmin(PolymorphicChildModelAdmin):
base_model = Group # Explicitly set here!
show_in_index = True # makes child model admin visible in main admin site
# define custom features here
@admin.register(Entitlement)
class EntitlementAdmin(PolymorphicChildModelAdmin):
base_model = Entitlement # Explicitly set here!
show_in_index = True # makes child model admin visible in main admin site
# define custom features here
@admin.register(VO)
class VOParentAdmin(PolymorphicParentModelAdmin):
""" The parent model admin """
base_model = VO # Optional, explicitly set here.
child_models = (Group, Entitlement)
admin.site.register(EntitlementNameSpace)
admin.site.register(OIDCConfig)
admin.site.register(models.Site)
admin.site.register(models.Service)
@admin.register(deployments.VODeployment)
class VODeploymentAdmin(PolymorphicChildModelAdmin):
show_in_index = True
@admin.register(deployments.ServiceDeployment)
class ServiceDeploymentAdmin(PolymorphicChildModelAdmin):
show_in_index = True
@admin.register(deployments.Deployment)
class DeploymentAdmin(PolymorphicChildModelAdmin):
base_model = Entitlement # Explicitly set here!
show_in_index = True # makes child model admin visible in main admin site
child_models = (deployments.VODeployment, deployments.ServiceDeployment)
admin.site.register([
deployments.DeploymentState,
deployments.CredentialState,
])
from django.contrib import admin
from django.contrib.auth.models import Group as AuthGroup
from .. import models
from ..auth.v1.models import OIDCConfig
from ..auth.v1.models.vo import VO, Group, Entitlement, EntitlementNameSpace
from ..models import User
from ..models.deployments import VODeployment, ServiceDeployment, Deployment, DeploymentState, CredentialState
from .users import CustomUserAdmin
from .vos import GroupAdmin, EntitlementAdmin, VOAdmin
from .deployments import VODeploymentAdmin, ServiceDeploymentAdmin, DeploymentAdmin
# users
admin.site.register(User, CustomUserAdmin)
admin.site.unregister(AuthGroup)
# vos
admin.site.register(Group, GroupAdmin)
admin.site.register(Entitlement, EntitlementAdmin)
admin.site.register(VO, VOAdmin)
admin.site.register(EntitlementNameSpace)
admin.site.register(OIDCConfig)
# deployments
admin.site.register(VODeployment, VODeploymentAdmin)
admin.site.register(ServiceDeployment, ServiceDeploymentAdmin)
admin.site.register(Deployment, DeploymentAdmin)
# other
admin.site.register([
DeploymentState,
CredentialState,
models.SSHPublicKey,
models.Site,
models.Service,
])
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin
from ..auth.v1.models.vo import Entitlement
from ..models import deployments
class VODeploymentAdmin(PolymorphicChildModelAdmin):
show_in_index = True
class ServiceDeploymentAdmin(PolymorphicChildModelAdmin):
show_in_index = True
class DeploymentAdmin(PolymorphicParentModelAdmin):
base_model = Entitlement # Explicitly set here!
show_in_index = True # makes child model admin visible in main admin site
child_models = (deployments.VODeployment, deployments.ServiceDeployment)
from django.contrib.admin import SimpleListFilter
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm
from ..auth.v1.models import OIDCConfig
from ..models import User
class TypeFilter(SimpleListFilter):
title = 'Type'
parameter_name = 'user_type'
def lookups(self, request, model_admin):
return User.TYPE_CHOICES
def queryset(self, request, queryset):
if self.value():
return queryset.filter(user_type=self.value())
return queryset
class IdPFilter(SimpleListFilter):
title = 'IdP'
parameter_name = 'idp'
# returns sorted list of pairs
def lookups(self, request, model_admin):
queryset = OIDCConfig.objects.order_by('name')
return [(idp.id, idp.name) for idp in queryset]
def queryset(self, request, queryset):
if self.value():
return queryset.filter(idp=OIDCConfig.objects.get(id=self.value()))
return queryset
# from https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#custom-users-and-the-built-in-auth-forms
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = User
fields = UserCreationForm.Meta.fields + (
'user_type', 'idp',
)
class CustomUserAdmin(UserAdmin):
list_display = ('username', 'user_type', 'email', 'idp', 'is_staff', 'is_superuser')
list_filter = (TypeFilter, IdPFilter,)
add_form = CustomUserCreationForm
fieldsets = (
(None, {'fields': ('username', 'password', 'user_type',)}),
('Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser',)}),
('Dates', {'fields': ('last_login', 'date_joined')}),
('Upstream Client fields', {'fields': ('idp',)}),
)
add_fieldsets = (
(None, {'fields': ('user_type',)}),
) + UserAdmin.add_fieldsets + (
('Upstream Client fields', {'fields': ('idp',)}),
)
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin
from ..auth.v1.models.vo import VO, Group, Entitlement
class GroupAdmin(PolymorphicChildModelAdmin):
base_model = Group # Explicitly set here!
show_in_index = True # makes child model admin visible in main admin site
# define custom features here
class EntitlementAdmin(PolymorphicChildModelAdmin):
base_model = Entitlement # Explicitly set here!
show_in_index = True # makes child model admin visible in main admin site
# define custom features here
class VOAdmin(PolymorphicParentModelAdmin):
""" The parent model admin """
base_model = VO # Optional, explicitly set here.
child_models = (Group, Entitlement)
# Generated by Django 2.2.7 on 2020-01-30 14:28
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('backend', '0012_auto_20200124_1403'),
]
operations = [
migrations.AlterField(
model_name='user',
name='idp',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='users', to='backend.OIDCConfig'),
),
migrations.AlterField(
model_name='user',
name='user_type',
field=models.CharField(choices=[('apiclient', 'Downstream Client'), ('oidcuser', 'Webpage User'), ('admin', 'Admin'), ('upstream', 'Upstream Client')], default='apiclient', max_length=20),
),
]
......@@ -24,10 +24,16 @@ def user_info_default():
class User(AbstractUser):
USER_ALREADY_EXISTS = Exception('The user does already exist. This usually implies that the IdP changed the sub. Only possible fix: delete the old user')
TYPE_CHOICE_DOWNSTREAM = 'apiclient'
TYPE_CHOICE_USER = 'oidcuser'
TYPE_CHOICE_ADMIN = 'admin'
TYPE_CHOICE_UPSTREAM = 'upstream'
TYPE_CHOICES = (
('apiclient', 'API-Client'),
('oidcuser', 'OIDC User'),
('admin', 'Admin'),
(TYPE_CHOICE_DOWNSTREAM, 'Downstream Client'), # clients which connect to us via pubsub
(TYPE_CHOICE_USER, 'Webpage User'), # normal users which logged in using the webpage
(TYPE_CHOICE_ADMIN, 'Admin'), # admins of the django admin
(TYPE_CHOICE_UPSTREAM, 'Upstream Client'), # E.g. an idP that provides us with fresh userinfos or access tokens
)
user_type = models.CharField(
max_length=20,
......@@ -58,7 +64,7 @@ class User(AbstractUser):
on_delete=models.CASCADE,
blank=True,
null=True,
editable=False,
editable=True, # editable because when user_type==upstream the idp needs to be configurable in the admin
)
userinfo = JSONField(
default=user_info_default,
......
from rest_framework.permissions import IsAuthenticated
from .models import User
class TypeOnly(IsAuthenticated):
user_type = ''
def has_permission(self, request, view):
return super().has_permission(self, request, view) and request.user.user_type == self.user_type
class UpstreamOnly(IsAuthenticated):
user_type = User.TYPE_CHOICE_UPSTREAM
class DownstreamOnly(IsAuthenticated):
user_type = User.TYPE_CHOICE_DOWNSTREAM
class UserOnly(IsAuthenticated):
user_type = User.TYPE_CHOICE_USER
......@@ -14,16 +14,19 @@ from feudal.backend.auth.v1.models.vo import VO, Group, Entitlement
from feudal.backend.models import Site, Service, deployments
from feudal.backend.models.brokers import RabbitMQInstance
from feudal.backend.models.serializers import clients
from feudal.backend.permissions import DownstreamOnly
LOGGER = logging.getLogger(__name__)
# authentication class for the client api
AUTHENTICATION_CLASSES = (BasicAuthentication, )
AUTHENTICATION_CLASSES = (BasicAuthentication,)
PERMISSION_CLASSES = (DownstreamOnly,)
class DeploymentStateView(generics.UpdateAPIView):
authentication_classes = AUTHENTICATION_CLASSES
permission_classes = PERMISSION_CLASSES
serializer_class = clients.DeploymentStateSerializer
def get_object(self):
......@@ -59,7 +62,6 @@ class DeploymentStateView(generics.UpdateAPIView):
state.message,
)
# update the credential states of this deployment state
state.client_credential_states(self.request.data.get('credential_states', {}))
......@@ -75,6 +77,7 @@ class DeploymentStateView(generics.UpdateAPIView):
class DeploymentStateListView(generics.ListAPIView):
authentication_classes = AUTHENTICATION_CLASSES
permission_classes = PERMISSION_CLASSES
serializer_class = clients.DeploymentStateSerializer
def get_queryset(self):
......@@ -88,6 +91,7 @@ class DeploymentStateListView(generics.ListAPIView):
# the client has to fetch the configuration
class ConfigurationView(views.APIView):
authentication_classes = AUTHENTICATION_CLASSES
permission_classes = PERMISSION_CLASSES
sid_to_service = {}
......@@ -109,7 +113,6 @@ class ConfigurationView(views.APIView):
for dep in vo.vo_deployments.all():
dep.update()
# returns the service ID to service mapping contained in the request
def parse_sid_to_service(self, request):
self.sid_to_service = {}
......@@ -192,6 +195,7 @@ class ConfigurationView(views.APIView):
class DeregisterView(views.APIView):
authentication_classes = AUTHENTICATION_CLASSES
permission_classes = PERMISSION_CLASSES
def put(self, request):
......
......@@ -11,6 +11,7 @@ from rest_framework.permissions import AllowAny
from feudal.backend.auth.v1.models.serializers import VOSerializer
from feudal.backend.auth.v1.models.vo import VO
from feudal.backend.models import serializers, deployments, Service
from feudal.backend.permissions import UserOnly
LOGGER = logging.getLogger(__name__)
HELP_TEXT = """
......@@ -51,6 +52,8 @@ state/<id> GET Show your deployment state with id <id>
deployment state has state=questionnaire
"""
PERMISSION_CLASSES = (UserOnly,)
class HelpView(views.APIView):
permission_classes = (AllowAny,)
......@@ -60,6 +63,7 @@ class HelpView(views.APIView):
class UserDeletionView(generics.RetrieveDestroyAPIView):
permission_classes = PERMISSION_CLASSES
serializer_class = serializers.UserStateSerializer
def get_object(self):
......@@ -72,6 +76,7 @@ class UserDeletionView(generics.RetrieveDestroyAPIView):
class ServiceListView(generics.ListAPIView):
permission_classes = PERMISSION_CLASSES
serializer_class = serializers.ServiceSerializer
def get_queryset(self):
......@@ -79,6 +84,7 @@ class ServiceListView(generics.ListAPIView):
class ServiceView(generics.RetrieveAPIView):
permission_classes = PERMISSION_CLASSES
serializer_class = serializers.ServiceSerializer
def get_object(self):
......@@ -89,6 +95,7 @@ class ServiceView(generics.RetrieveAPIView):
class VOListView(generics.ListAPIView):
permission_classes = PERMISSION_CLASSES
serializer_class = VOSerializer
def get_queryset(self):
......@@ -96,6 +103,7 @@ class VOListView(generics.ListAPIView):
class VOView(generics.RetrieveAPIView):
permission_classes = PERMISSION_CLASSES
serializer_class = VOSerializer
def get_object(self):
......@@ -106,6 +114,7 @@ class VOView(generics.RetrieveAPIView):
class SSHPublicKeyListView(generics.ListCreateAPIView):
permission_classes = PERMISSION_CLASSES
serializer_class = serializers.SSHPublicKeySerializer
def get_queryset(self):
......@@ -117,6 +126,7 @@ class SSHPublicKeyListView(generics.ListCreateAPIView):
class SSHPublicKeyView(generics.RetrieveDestroyAPIView):
permission_classes = PERMISSION_CLASSES
serializer_class = serializers.SSHPublicKeySerializer
def get_object(self):
......@@ -130,6 +140,7 @@ class SSHPublicKeyView(generics.RetrieveDestroyAPIView):
class DeploymentListView(generics.ListAPIView):
permission_classes = PERMISSION_CLASSES
serializer_class = serializers.DeploymentSerializer
def get_queryset(self):
......@@ -142,6 +153,7 @@ class DeploymentListView(generics.ListAPIView):
class DeploymentView(generics.RetrieveUpdateAPIView):
permission_classes = PERMISSION_CLASSES
serializer_class = serializers.DeploymentSerializer
def get_serializer_context(self):
......@@ -199,6 +211,7 @@ class DeploymentView(generics.RetrieveUpdateAPIView):
class DeploymentStateListView(generics.ListCreateAPIView):
permission_classes = PERMISSION_CLASSES
serializer_class = serializers.DeploymentStateSerializer
def get_queryset(self):
......@@ -206,6 +219,7 @@ class DeploymentStateListView(generics.ListCreateAPIView):
class DeploymentStateView(generics.RetrieveUpdateAPIView):
permission_classes = PERMISSION_CLASSES
serializer_class = serializers.DeploymentStateSerializer
def get_object(self):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment