Commit a5a4daa4 authored by Lukas Burgey's avatar Lukas Burgey

Add a more clear api to the User class

Alongside other linting / minor changes.
parent b9a15d9e
......@@ -8,6 +8,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DEBUG = True
DEBUG_AUTH = False
DEBUG_CREDENTIALS = True
ALLOWED_HOSTS = [
'hdf-portal.data.kit.edu',
......
......@@ -5,7 +5,9 @@ from logging import getLogger
from django.contrib.auth.models import Group
from django.db import models
from django_mysql.models import JSONField
from django.conf import settings
# these imports are exports!
from .brokers import RabbitMQInstance
from .users import User, SSHPublicKey
from .groups import GroupDescription
......@@ -21,6 +23,10 @@ QUESTIONNAIRE = 'questionnaire'
FAILED = 'failed'
REJECTED = 'rejected'
TARGET_CHOICES = (
(DEPLOYED, 'Deployed'),
(NOT_DEPLOYED, 'Not Deployed'),
)
STATE_CHOICES = (
(DEPLOYMENT_PENDING, 'Deployment Pending'),
(REMOVAL_PENDING, 'Removal Pending'),
......@@ -346,7 +352,7 @@ class NewDeployment(models.Model):
)
def msg(self, msg):
return '[Deploy:{}] {}'.format(self, msg)
return '[{}] {}'.format(self, msg)
def _set_target(self, target):
self.state_target = target
......@@ -355,12 +361,12 @@ class NewDeployment(models.Model):
def __str__(self):
if self.service is not None:
return '{}:{}#{}'.format(
return 'SVC-Dep: ({}:{})#{}'.format(
self.service,
self.user,
self.id,
)
return '{}:{}#{}'.format(
return 'VO-Dep: ({}:{})#{}'.format(
self.group,
self.user,
self.id,
......@@ -421,6 +427,23 @@ class NewDeploymentStateItem(models.Model):
def group(self):
return self.parent.group
# starts tracking this the credential for this item
def add_credential(self, credential):
if settings.DEBUG_CREDENTIALS:
LOGGER.debug('add_credential: %s %s', self, credential)
CredentialState.get_credential_state(
credential,
self,
)
def remove_credential(self, credential):
if settings.DEBUG_CREDENTIALS:
LOGGER.debug('remove_credential: %s %s', self, credential)
# TODO implement
# what is needs to be done here?
pass
@classmethod
def get_state_item(cls, parent=None, site=None, service=None):
try:
......@@ -429,7 +452,7 @@ class NewDeploymentStateItem(models.Model):
site=site,
service=service,
)
LOGGER.debug('get_state_item: item already exists')
# LOGGER.debug('get_state_item: item already exists')
return item
except cls.DoesNotExist:
......@@ -442,11 +465,9 @@ class NewDeploymentStateItem(models.Model):
# start tracking the states of the users ssh keys
for key in parent.user.ssh_keys.all():
CredentialState.get_credential_state(
key,
item,
)
LOGGER.debug(item.msg('created'))
item.add_credential(key)
LOGGER.debug('get_state_item: created %s', item)
return item
# STATE TRANSITIONS
......@@ -547,8 +568,7 @@ class NewDeploymentStateItem(models.Model):
def set_onroute_credential_states(self, state):
for credential_state in self.get_onroute_credential_states().all():
credential_state.state = state
credential_state.save()
credential_state.set(state)
# resets all client sent values
def _reset(self):
......@@ -557,7 +577,7 @@ class NewDeploymentStateItem(models.Model):
self.message = ''
def msg(self, msg):
return '[DSItem:{}] {}'.format(self, msg)
return '[{}] {}'.format(self, msg)
def _set_state(self, state, publish=True):
self.set_onroute_credential_states(state)
......@@ -568,14 +588,8 @@ class NewDeploymentStateItem(models.Model):
self.parent.publish_to_user()
def __str__(self):
if self.group is not None:
return '{}:{}@{}#{}'.format(
self.group,
self.service,
self.site,
self.id,
)
return '{}:@{}#{}'.format(
return 'Item: ({}:{}:{})#{}'.format(
self.parent.id,
self.service,
self.site,
self.id,
......@@ -610,7 +624,8 @@ class CredentialState(models.Model):
target=target,
)
except cls.DoesNotExist:
LOGGER.debug('new credential state for %s / %s', credential, target)
if settings.DEBUG_CREDENTIALS:
LOGGER.debug('new credential state for %s / %s', credential, target)
new_state = cls(
credential=credential,
target=target,
......@@ -622,7 +637,8 @@ class CredentialState(models.Model):
def set(self, state):
self.state = state
self.save()
LOGGER.debug('%s state changed to: %s', self, state)
if settings.DEBUG_CREDENTIALS:
LOGGER.debug('%s state changed to: %s', self, state)
def __str__(self):
return '{}@{}'.format(
......
......@@ -22,7 +22,11 @@ class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
fields = ['email', 'groups', 'userinfo']
fields = [
'email',
'groups',
'userinfo',
]
class NewDeploymentSerializer(serializers.ModelSerializer):
......@@ -52,4 +56,6 @@ class NewDeploymentsSerializer(serializers.Serializer):
class RabbitMQInstanceSerializer(serializers.ModelSerializer):
class Meta:
model = models.RabbitMQInstance
fields = ['vhost']
fields = [
'vhost',
]
......@@ -13,6 +13,7 @@ from ..auth.v1.models import OIDCConfig
LOGGER = logging.getLogger(__name__)
def user_info_default():
return {}
......@@ -213,8 +214,9 @@ class User(AbstractUser):
# oidcuser: deploy the according credentials
if self.user_type == 'oidcuser':
for dep in self.deployments.all():
dep.activate()
#for dep in self.deployments.all():
# dep.activate()
pass
def deactivate(self):
if not self._is_active:
......@@ -224,13 +226,25 @@ class User(AbstractUser):
self.is_active = False
self._is_active = False
self.save()
LOGGER.info(self.msg('deactivated'))
LOGGER.debug(self.msg('deactivating'))
# oidcuser: remove all credentials
if self.user_type == 'oidcuser':
self.deployments_remove_all()
LOGGER.info(self.msg('deactivated'))
for dep in self.deployments.all():
dep.user_remove()
def deployments_remove_all(self):
LOGGER.debug('Removing all deployments of user %s', self)
sites = []
# find which sites need to be notified
for dep in self.deployments.all():
for state_item in dep.state_items.all():
if state_item.state != 'not_deployed':
sites.append(state_item.site)
def update_userinfo_groups(self, userinfo):
changed = False
......@@ -239,16 +253,13 @@ class User(AbstractUser):
# check if groups were removed
for group in self.groups.all():
if group.name not in groups:
LOGGER.info(self.msg('User left the group %s'), group)
self.groups.remove(group)
# remove group from user and deactivate deployments
for dep in self.deployments.filter(group=group):
LOGGER.info(dep.msg('Deactivating, as user left the group'), group)
dep.deactivate()
# TRIGGER
self.user_changed_group_removed(group)
if not changed:
changed = True
self.groups.remove(group)
changed = True
# check if groups were added
for group_name in groups:
......@@ -260,11 +271,10 @@ class User(AbstractUser):
if not self.groups.filter(name=group_name).exists():
LOGGER.info(self.msg('User is now in the group %s'), group)
self.groups.add(group)
for dep in self.deployments.filter(group=group):
dep.activate()
if not changed:
changed = True
self.user_changed_group_added(group)
changed = True
except Group.DoesNotExist:
LOGGER.info('New group from IdP: %s', group_name)
......@@ -272,23 +282,33 @@ class User(AbstractUser):
group.save()
self.groups.add(group)
for dep in self.deployments.filter(group=group):
LOGGER.info(dep.msg('Reactivating, as user is back in the group'), group)
dep.activate()
# TRIGGER
self.user_changed_group_added(group)
if not changed:
changed = True
changed = True
return changed
# returns True if any keys changed
def update_userinfo_ssh_key(self, userinfo):
unity_key_value = userinfo.get('ssh_key', '')
idp_key_name = 'ssh_key'
unity_key_name = 'unity_key'
unity_key_value = userinfo.get(idp_key_name, '')
try:
key = self._ssh_keys.get(name=unity_key_name)
if idp_key_name not in userinfo:
self.user_changed_key_changed(key)
key.delete_key()
return True
if key.key != unity_key_value:
LOGGER.debug('unity_key of user %s changed', self)
key.delete_key()
key = SSHPublicKey(
name=unity_key_name,
......@@ -296,13 +316,17 @@ class User(AbstractUser):
user=self,
)
key.save()
# changed
self.user_changed_key_changed(key)
return True
# not changed
return False
except SSHPublicKey.DoesNotExist:
if idp_key_name not in userinfo:
return False
key = SSHPublicKey(
name=unity_key_name,
key=unity_key_value,
......@@ -310,7 +334,8 @@ class User(AbstractUser):
)
key.save()
# changed
self.user_changed_key_added(key)
return True
def update_userinfo(self, userinfo):
......@@ -335,15 +360,73 @@ class User(AbstractUser):
changed = True
if changed:
LOGGER.debug('update_userinfo caused changes to the user')
self.user_changed()
# TODO implement
# Call if the user or its keys are changed
def user_changed(self):
LOGGER.debug('User changed. Propagating changes')
LOGGER.info('user_changed')
pass
def sanity_check(self):
for dep in self.deployments.all():
LOGGER.debug('User has a deployment %s with target %s', dep, dep.state_target)
for state_item in dep.state_items.all():
LOGGER.debug('User has a state item for %s', state_item.service)
for credential_state in state_item.credential_states.all():
if credential_state.credential is None:
LOGGER.debug('Credential state exists for null key -> we have to update the deployment so the according key is deleted')
else:
LOGGER.debug('User has a credential state for %s', credential_state.credential)
for ssh_key in self.ssh_keys.all():
try:
state_item.credential_states.get(
key=ssh_key,
)
except:
LOGGER.debug('ssh key %s has no credential_state! -> we possibly need to deploy this key', ssh_key)
# TODO implement all user_changed_ triggers
# The user_changed methods are triggerd in user_changed
# They serve to assure the correct state at the backend and clients
#
def user_changed_key_added(self, key):
LOGGER.debug('user_changed_key_added: %s %s', self, key)
for dep in self.deployments.all():
LOGGER.debug('user_changed_key_added: need to add key to deployment %s', dep)
def user_changed_key_changed(self, key):
LOGGER.debug('user_changed_key_changed: %s %s', self, key)
for credential_state in key.credential_states.all():
LOGGER.debug('user_changed_key_changed: need to handle credential state %s', credential_state)
def user_changed_key_removed(self, key):
LOGGER.debug('user_changed_key_removed: %s %s', self, key)
for credential_state in key.credential_states.all():
LOGGER.debug('user_changed_key_removed: need to handle credential state %s', credential_state)
# delete accordingly
def user_changed_group_added(self, group):
LOGGER.debug('user_changed_group_added: %s %s', self, group)
# check if the user has deactivated deployments for this exact group
# if yes: reactivate the deployments
for dep in self.deployments.filter(group=group):
LOGGER.debug('user_changed_group_added: need to activate deployment %s', dep)
def user_changed_group_removed(self, group):
LOGGER.debug('user_changed_group_removed: %s %s', self, group)
# check if the user has deployments which need member ship of this group
# if yes remove them
for dep in self.deployments.filter(group=group):
LOGGER.debug('user_changed_group_removed: need to deactivate deployment %s', dep)
class SSHPublicKey(models.Model):
name = models.CharField(
......@@ -366,14 +449,10 @@ class SSHPublicKey(models.Model):
editable=False,
)
# does not directly delete the key if the key is deployed or removen
# somewhere
# the receiver 'delete_removen_ssh_key' does the actual deletion
def delete_key(self):
# LOGGER.info(self.msg('Deletion of key started'))
# self.deleted = True
# self.save()
LOGGER.info('Direct deletion of SSH key')
LOGGER.info('Deleting SSH key %s', self.name)
for credential_state in self.credential_states.all():
credential_state.set('removal_pending')
self.delete()
# we do not update the user object from here: update_userinfo does that anyway
......
......@@ -7,7 +7,7 @@ from rest_framework import generics, views
from rest_framework.authentication import BasicAuthentication
from rest_framework.response import Response
from ..models.serializers.clients import NewDeploymentSerializer
from ..models.serializers.webpage import NewDeploymentSerializer
from ..models.serializers.clients import RabbitMQInstanceSerializer
from .. import models
......
......@@ -3,6 +3,7 @@ import logging
from django.contrib.auth import logout
from django.contrib.auth.models import Group
from django.conf import settings
from django.shortcuts import get_object_or_404
from rest_framework import status
from rest_framework import views
......@@ -79,13 +80,18 @@ class SSHPublicKeyView(views.APIView):
if 'type' in request.data:
if request.data['type'] == 'remove':
if 'id' in request.data:
key = get_object_or_404(
models.SSHPublicKey,
id=request.data['id']
id=request.data['id'],
)
# we do not delete ssh keys directly, as we need to keep track
# of them until all clients have also deleted them
key.delete_key()
request.user.user_changed_key_removed(key)
return Response({
'deleted': True,
})
......@@ -97,6 +103,9 @@ class SSHPublicKeyView(views.APIView):
key=request.data['key']['key'],
)
key.save()
request.user.user_changed_key_added(key)
return Response(
model_serializers.SSHPublicKeySerializer(
key,
......
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