Commit 2e64ae8a by Belákovics Ádám

Add basic authorization

parent 93307f6f
...@@ -17,6 +17,7 @@ django-cors-headers = "*" ...@@ -17,6 +17,7 @@ django-cors-headers = "*"
openstacksdk = "*" openstacksdk = "*"
python-novaclient = "*" python-novaclient = "*"
keystoneauth1 = "*" keystoneauth1 = "*"
django-guardian = "*"
[requires] [requires]
python_version = "3.6" python_version = "3.6"
from django.contrib import admin
# Register your models here.
from django.apps import AppConfig
class AuthorizationConfig(AppConfig):
name = 'authorization'
from guardian.shortcuts import get_objects_for_user
import logging
logger = logging.getLogger(__name__)
class AuthorizationMixin():
authorization = {}
def get_objects_with_perms(self, user, method, instance):
auth_params = self.authorization[method]
if auth_params:
return get_objects_for_user(user, auth_params["perms"], instance)
else:
logger.error(f"Invalid method for authorization: {method}")
def has_perms_for_object(self, user, method, instance):
auth_params = self.authorization[method]
if auth_params:
for perm in auth_params["perms"]:
user.has_perm('')
else:
logger.error(f"Invalid method for authorization: {method}")
def has_perms_for_model(self, user, method, model):
auth_params = self.authorization[method]
if auth_params:
pass
else:
logger.error(f"Invalid method for authorization: {method}")
from django.db import models
# Create your models here.
from django.contrib.auth.models import User
from instance.models import Instance
from guardian.shortcuts import get_objects_for_user
test_user = User.objects.get(username="test")
get_objects_for_user(test_user, "use_instance", Instance)
from django.test import TestCase
# Create your tests here.
from django.shortcuts import render
# Create your views here.
...@@ -2,6 +2,13 @@ from django.contrib import admin ...@@ -2,6 +2,13 @@ from django.contrib import admin
from instance.models import Instance, Flavor, Lease from instance.models import Instance, Flavor, Lease
admin.site.register(Instance) from guardian.admin import GuardedModelAdmin
class InstanceAdmin(GuardedModelAdmin):
pass
admin.site.register(Instance, InstanceAdmin)
admin.site.register(Flavor) admin.site.register(Flavor)
admin.site.register(Lease) admin.site.register(Lease)
...@@ -5,8 +5,8 @@ from django.utils import timezone ...@@ -5,8 +5,8 @@ from django.utils import timezone
from datetime import timedelta from datetime import timedelta
from image.models import Disk from image.models import Disk
from interface_openstack.implementation.vm.instance import ( from interface_openstack.implementation.vm.instance import (
OSVirtualMachineManager OSVirtualMachineManager
) )
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -45,8 +45,8 @@ class Flavor(models.Model): ...@@ -45,8 +45,8 @@ class Flavor(models.Model):
name = models.CharField(blank=True, max_length=100) name = models.CharField(blank=True, max_length=100)
description = models.CharField(blank=True, max_length=200) description = models.CharField(blank=True, max_length=200)
remote_id = models.CharField( remote_id = models.CharField(
max_length=100, help_text="ID of the instance on the backend" max_length=100, help_text="ID of the instance on the backend"
) )
ram = models.IntegerField(blank=True, null=True) ram = models.IntegerField(blank=True, null=True)
vcpu = models.IntegerField(blank=True, null=True) vcpu = models.IntegerField(blank=True, null=True)
initial_disk = models.IntegerField(blank=True, null=True) initial_disk = models.IntegerField(blank=True, null=True)
...@@ -82,6 +82,18 @@ class Instance(models.Model): ...@@ -82,6 +82,18 @@ class Instance(models.Model):
""" """
from template.models import ImageTemplate from template.models import ImageTemplate
class Meta:
permissions = (
('create_instance', 'Can create a new VM.'),
('use_instance', 'Can access the VM connection info.'),
('operate_instance', 'Can use basic lifecycle methods of the VM.'),
('administer_instance', 'Can delete VM.'),
('access_console', 'Can access the graphical console of a VM.'),
('change_resources', 'Can change resources of a VM.'),
('manage_access', 'Can manage access rights for the VM.'),
('config_ports', 'Can configure port forwards.'),
)
name = models.CharField(max_length=100, name = models.CharField(max_length=100,
help_text="Human readable name of instance") help_text="Human readable name of instance")
...@@ -168,10 +180,10 @@ class Instance(models.Model): ...@@ -168,10 +180,10 @@ class Instance(models.Model):
lease = self.lease lease = self.lease
return ( return (
timezone.now() + timedelta( timezone.now() + timedelta(
seconds=lease.suspend_interval_in_sec), seconds=lease.suspend_interval_in_sec),
timezone.now() + timedelta( timezone.now() + timedelta(
seconds=lease.delete_interval_in_sec) seconds=lease.delete_interval_in_sec)
) )
def delete(self): def delete(self):
try: try:
...@@ -193,5 +205,5 @@ class Instance(models.Model): ...@@ -193,5 +205,5 @@ class Instance(models.Model):
@classmethod @classmethod
def generate_password(self): def generate_password(self):
return User.objects.make_random_password( return User.objects.make_random_password(
allowed_chars='abcdefghijklmnopqrstuvwx' allowed_chars='abcdefghijklmnopqrstuvwx'
'ABCDEFGHIJKLMNOPQRSTUVWX123456789') 'ABCDEFGHIJKLMNOPQRSTUVWX123456789')
...@@ -8,15 +8,33 @@ from rest_framework.decorators import action ...@@ -8,15 +8,33 @@ from rest_framework.decorators import action
from template.serializers import InstanceFromTemplateSerializer from template.serializers import InstanceFromTemplateSerializer
from interface_openstack.implementation.vm.instance import ( from interface_openstack.implementation.vm.instance import (
OSVirtualMachineManager OSVirtualMachineManager
) )
from instance.models import Instance, Flavor, Lease from instance.models import Instance, Flavor, Lease
from instance.serializers import InstanceSerializer, FlavorSerializer from instance.serializers import InstanceSerializer, FlavorSerializer
from template.models import ImageTemplate from template.models import ImageTemplate
from template.serializers import ImageTemplateModelSerializer from template.serializers import ImageTemplateModelSerializer
from authorization.mixins import AuthorizationMixin
class InstanceViewSet(ViewSet): authorization = {
"list": {"auth_type": "filter", "perms": ["use_instance"]},
"create": {"auth_type": "class", "perms": ["create_instance"]},
"retrieve": {"auth_type": "object", "perms": ["use_instance"]},
"destroy": {"auth_type": "object", "perms": ["administer_instance"]},
"template": {"auth_type": "object", "perms": ["use_instance"]},
"start": {"auth_type": "object", "perms": ["operate_instance"]},
"stop": {"auth_type": "object", "perms": ["operate_instance"]},
"suspend": {"auth_type": "object", "perms": ["operate_instance"]},
"wake_up": {"auth_type": "object", "perms": ["operate_instance"]},
"reset": {"auth_type": "object", "perms": ["operate_instance"]},
"reboot": {"auth_type": "object", "perms": ["operate_instance"]},
}
class InstanceViewSet(AuthorizationMixin, ViewSet):
authorization = authorization
def get_object(self, pk): def get_object(self, pk):
try: try:
...@@ -25,7 +43,7 @@ class InstanceViewSet(ViewSet): ...@@ -25,7 +43,7 @@ class InstanceViewSet(ViewSet):
raise Http404 raise Http404
def list(self, request): def list(self, request):
instances = Instance.objects.all() instances = self.get_objects_with_perms(request.user, "list", Instance)
return Response(InstanceSerializer(instances, many=True).data) return Response(InstanceSerializer(instances, many=True).data)
def create(self, request): def create(self, request):
...@@ -34,6 +52,8 @@ class InstanceViewSet(ViewSet): ...@@ -34,6 +52,8 @@ class InstanceViewSet(ViewSet):
# flavor = Flavor.objects.get(pk=data["flavor"]) # flavor = Flavor.objects.get(pk=data["flavor"])
# lease = Lease.objects.get(pk=data["lease"]) # lease = Lease.objects.get(pk=data["lease"])
newInstance = Instance.create_instance_from_template( newInstance = Instance.create_instance_from_template(
params={"name": data["name"], params={"name": data["name"],
"description": data["description"], "description": data["description"],
...@@ -61,13 +81,15 @@ class InstanceViewSet(ViewSet): ...@@ -61,13 +81,15 @@ class InstanceViewSet(ViewSet):
return Response(merged_dict) return Response(merged_dict)
def update(self, request, pk, format=None): # I think we need separate actions for this behaviour because of the access control - BAdam
instance = self.get_object(pk)
serializer = InstanceSerializer(instance, data=request.data) # def update(self, request, pk, format=None):
if serializer.is_valid(): # instance = self.get_object(pk)
serializer.save() # serializer = InstanceSerializer(instance, data=request.data)
return Response(serializer.data) # if serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # serializer.save()
# return Response(serializer.data)
# return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def destroy(self, request, pk, format=None): def destroy(self, request, pk, format=None):
instance = self.get_object(pk) instance = self.get_object(pk)
......
...@@ -41,6 +41,7 @@ INSTALLED_APPS = [ ...@@ -41,6 +41,7 @@ INSTALLED_APPS = [
"djoser", "djoser",
"rest_framework_swagger", "rest_framework_swagger",
"corsheaders", "corsheaders",
"guardian",
] ]
LOCAL_APPS = [ LOCAL_APPS = [
...@@ -48,6 +49,7 @@ LOCAL_APPS = [ ...@@ -48,6 +49,7 @@ LOCAL_APPS = [
"instance", "instance",
"storage", "storage",
"template", "template",
"authorization",
] ]
INSTALLED_APPS += LOCAL_APPS INSTALLED_APPS += LOCAL_APPS
...@@ -111,6 +113,7 @@ REST_FRAMEWORK = { ...@@ -111,6 +113,7 @@ REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ( 'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.TokenAuthentication',
), ),
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema' # needed for swagger
} }
# Internationalization # Internationalization
...@@ -229,3 +232,10 @@ LOGGING = { ...@@ -229,3 +232,10 @@ LOGGING = {
for i in LOCAL_APPS: for i in LOCAL_APPS:
LOGGING['loggers'][i] = {'handlers': ['console'], 'level': 'DEBUG'} LOGGING['loggers'][i] = {'handlers': ['console'], 'level': 'DEBUG'}
# Configure django-guardian for the authorization
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', # this is default
'guardian.backends.ObjectPermissionBackend',
)
...@@ -11,3 +11,5 @@ for i in LOCAL_APPS: ...@@ -11,3 +11,5 @@ for i in LOCAL_APPS:
ADMIN_ENABLED = True ADMIN_ENABLED = True
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
AUTH_PASSWORD_VALIDATORS = []
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 sign in to comment