Commit 62712de5 authored by Lukas Burgey's avatar Lukas Burgey

Move User model to separate package

parent 8e5e6d08
......@@ -4,14 +4,14 @@
import json
import logging
from django.contrib.auth.models import AbstractUser, Group
from django.contrib.auth.models import Group
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django_mysql.models import JSONField
from .broker_models import RabbitMQInstance
from .auth.v1.models import OIDCConfig
from .user_models import User
LOGGER = logging.getLogger(__name__)
......@@ -25,249 +25,12 @@ STATE_CHOICES = (
('failed', 'Failed'),
('rejected', 'Rejected'),
)
def user_info_default():
return {}
def questionnaire_default():
return {}
def credential_default():
return {}
class User(AbstractUser):
TYPE_CHOICES = (
('apiclient', 'API-Client'),
('oidcuser', 'OIDC User'),
('admin', 'Admin'),
)
user_type = models.CharField(
max_length=20,
choices=TYPE_CHOICES,
default='apiclient',
)
sub = models.CharField(
max_length=150,
blank=True,
null=True,
editable=False,
)
password = models.CharField(
max_length=150,
blank=True,
null=True,
)
# the real state of the user
# (self.is_active is the supposed state of the user)
_is_active = models.BooleanField(
default=True,
editable=False,
)
# the idp which authenticated the user
idp = models.ForeignKey(
OIDCConfig,
related_name='users',
on_delete=models.CASCADE,
blank=True,
null=True,
editable=False,
)
userinfo = JSONField(
default=user_info_default,
null=True,
blank=True,
editable=False,
)
@property
def profile_name(self):
if 'email' in self.userinfo:
return self.userinfo['email']
elif 'name' in self.userinfo:
return self.userinfo['name']
return self.id
@property
def deployment_states(self):
states = []
for deployment in self.deployments.all():
for state in deployment.states.all():
states.append(state)
return states
@property
def deployment_state_items(self):
items = []
for state in self.deployment_states:
for item in state.state_items.all():
items.append(item)
return items
# returns the user as identified by userinfo and idp
# if the user does not exists
@classmethod
def get_user(cls, userinfo, idp):
if 'sub' not in userinfo:
raise ValueError('get_user needs a userinfo which contains the users subject')
try:
user = cls.objects.get(
sub=userinfo['sub'],
idp=idp,
)
user.update_userinfo(userinfo)
user.save()
return user
except cls.DoesNotExist:
return cls.construct_from_userinfo(userinfo, idp)
@classmethod
def construct_from_userinfo(cls, userinfo, idp):
if 'sub' not in userinfo:
raise Exception('Missing attribute in userinfo: sub')
sub = userinfo['sub']
username = sub
email = ''
if 'email' in userinfo:
username = userinfo['email']
user = cls(
user_type='oidcuser',
username=username,
sub=sub,
email=email,
idp=idp,
)
user.update_userinfo(userinfo)
user.save()
return user
@classmethod
def construct_client(cls, username, password):
LOGGER.debug('APICLIENT: new client %s', username)
client = cls(
username=username,
user_type='apiclient',
)
client.set_password(password)
return client
# we hide deleted keys here
# the full list of ssh keys is self._ssh_keys
@property
def ssh_keys(self):
return self._ssh_keys.filter(deleted=False)
@property
def is_active_at_clients(self):
return self._is_active
def __str__(self):
if self.user_type == 'admin':
return 'ADMIN {}'.format(self.username)
elif self.user_type == 'oidcuser':
if not self.is_active:
return 'DEACTIVATED USER {}'.format(self.username)
return 'USER {}'.format(self.username)
elif self.user_type == 'apiclient':
try:
return 'APICLIENT {}@{}'.format(self.username, self.site)
except Site.DoesNotExist:
return 'APICLIENT {}'.format(self.username)
else:
raise Exception()
def msg(self, msg):
return '[{}] {}'.format(self, msg)
# oidcuser: remove and delete all credentials and delete the user
def remove(self):
if self.user_type == 'oidcuser':
self.deactivate()
# FIXME: deleting the user brings problems:
# the deletion cascades down to DeploymentState and DeploymentStateItem
# but these need to be conserved so all clients removals can be tracked
LOGGER.info(self.msg('Deleting'))
self.delete()
def activate(self):
if self._is_active:
LOGGER.error(self.msg('already activated'))
return
self.is_active = True
self._is_active = True
self.save()
LOGGER.info(self.msg('activated'))
# oidcuser: deploy the according credentials
if self.user_type == 'oidcuser':
for dep in self.deployments.all():
dep.activate()
def deactivate(self):
if not self._is_active:
LOGGER.error(self.msg('already deactivated'))
return
self.is_active = False
self._is_active = False
self.save()
LOGGER.info(self.msg('deactivated'))
# oidcuser: remove all credentials
if self.user_type == 'oidcuser':
for dep in self.deployments.all():
dep.deactivate()
def update_userinfo(self, userinfo):
self.userinfo = userinfo
self.save()
if 'sub' not in userinfo:
raise Exception('Missing attribute in userinfo: sub')
groups = userinfo.get('groups', [])
for group_name in groups:
try:
group = Group.objects.get(name=group_name)
self.groups.add(group)
except Group.DoesNotExist:
LOGGER.info('Adding group %s', group_name)
group = Group(name=group_name)
group.save()
self.groups.add(group)
# include the ssh key from unity
unity_key_value = userinfo.get('ssh_key', '')
unity_key_name = 'unity_key'
try:
key = self._ssh_keys.get(name=unity_key_name)
if key.key != unity_key_value:
key.delete_key()
raise SSHPublicKey.DoesNotExist()
except SSHPublicKey.DoesNotExist:
key = SSHPublicKey(
name=unity_key_name,
key=unity_key_value,
user=self,
)
key.save()
class Site(models.Model):
client = models.OneToOneField(
User,
......
import logging
from django.contrib.auth.models import AbstractUser, Group
from django.db import models
from django_mysql.models import JSONField
from .auth.v1.models import OIDCConfig
LOGGER = logging.getLogger(__name__)
def user_info_default():
return {}
class User(AbstractUser):
TYPE_CHOICES = (
('apiclient', 'API-Client'),
('oidcuser', 'OIDC User'),
('admin', 'Admin'),
)
user_type = models.CharField(
max_length=20,
choices=TYPE_CHOICES,
default='apiclient',
)
sub = models.CharField(
max_length=150,
blank=True,
null=True,
editable=False,
)
password = models.CharField(
max_length=150,
blank=True,
null=True,
)
# the real state of the user
# (self.is_active is the supposed state of the user)
_is_active = models.BooleanField(
default=True,
editable=False,
)
# the idp which authenticated the user
idp = models.ForeignKey(
OIDCConfig,
related_name='users',
on_delete=models.CASCADE,
blank=True,
null=True,
editable=False,
)
userinfo = JSONField(
default=user_info_default,
null=True,
blank=True,
editable=False,
)
@property
def profile_name(self):
if 'email' in self.userinfo:
return self.userinfo['email']
elif 'name' in self.userinfo:
return self.userinfo['name']
return self.id
@property
def deployment_states(self):
states = []
for deployment in self.deployments.all():
for state in deployment.states.all():
states.append(state)
return states
@property
def deployment_state_items(self):
items = []
for state in self.deployment_states:
for item in state.state_items.all():
items.append(item)
return items
# returns the user as identified by userinfo and idp
# if the user does not exists
@classmethod
def get_user(cls, userinfo, idp):
if 'sub' not in userinfo:
raise ValueError('get_user needs a userinfo which contains the users subject')
try:
user = cls.objects.get(
sub=userinfo['sub'],
idp=idp,
)
user.update_userinfo(userinfo)
user.save()
return user
except cls.DoesNotExist:
return cls.construct_from_userinfo(userinfo, idp)
@classmethod
def construct_from_userinfo(cls, userinfo, idp):
if 'sub' not in userinfo:
raise Exception('Missing attribute in userinfo: sub')
sub = userinfo['sub']
username = sub
email = ''
if 'email' in userinfo:
username = userinfo['email']
user = cls(
user_type='oidcuser',
username=username,
sub=sub,
email=email,
idp=idp,
)
user.update_userinfo(userinfo)
user.save()
return user
@classmethod
def construct_client(cls, username, password):
LOGGER.debug('APICLIENT: new client %s', username)
client = cls(
username=username,
user_type='apiclient',
)
client.set_password(password)
return client
# we hide deleted keys here
# the full list of ssh keys is self._ssh_keys
@property
def ssh_keys(self):
return self._ssh_keys.filter(deleted=False)
@property
def is_active_at_clients(self):
return self._is_active
def __str__(self):
if self.user_type == 'admin':
return 'ADMIN {}'.format(self.username)
elif self.user_type == 'oidcuser':
if not self.is_active:
return 'DEACTIVATED USER {}'.format(self.username)
return 'USER {}'.format(self.username)
elif self.user_type == 'apiclient':
from .models import Site
try:
return 'APICLIENT {}@{}'.format(self.username, self.site)
except Site.DoesNotExist:
return 'APICLIENT {}'.format(self.username)
else:
raise Exception()
def msg(self, msg):
return '[{}] {}'.format(self, msg)
# oidcuser: remove and delete all credentials and delete the user
def remove(self):
if self.user_type == 'oidcuser':
self.deactivate()
# FIXME: deleting the user brings problems:
# the deletion cascades down to DeploymentState and DeploymentStateItem
# but these need to be conserved so all clients removals can be tracked
LOGGER.info(self.msg('Deleting'))
self.delete()
def activate(self):
if self._is_active:
LOGGER.error(self.msg('already activated'))
return
self.is_active = True
self._is_active = True
self.save()
LOGGER.info(self.msg('activated'))
# oidcuser: deploy the according credentials
if self.user_type == 'oidcuser':
for dep in self.deployments.all():
dep.activate()
def deactivate(self):
if not self._is_active:
LOGGER.error(self.msg('already deactivated'))
return
self.is_active = False
self._is_active = False
self.save()
LOGGER.info(self.msg('deactivated'))
# oidcuser: remove all credentials
if self.user_type == 'oidcuser':
for dep in self.deployments.all():
dep.deactivate()
def update_userinfo(self, userinfo):
self.userinfo = userinfo
self.save()
if 'sub' not in userinfo:
raise Exception('Missing attribute in userinfo: sub')
groups = userinfo.get('groups', [])
for group_name in groups:
try:
group = Group.objects.get(name=group_name)
self.groups.add(group)
except Group.DoesNotExist:
LOGGER.info('Adding group %s', group_name)
group = Group(name=group_name)
group.save()
self.groups.add(group)
# include the ssh key from unity
unity_key_value = userinfo.get('ssh_key', '')
unity_key_name = 'unity_key'
from .models import SSHPublicKey
try:
key = self._ssh_keys.get(name=unity_key_name)
if key.key != unity_key_value:
key.delete_key()
raise SSHPublicKey.DoesNotExist()
except SSHPublicKey.DoesNotExist:
key = SSHPublicKey(
name=unity_key_name,
key=unity_key_value,
user=self,
)
key.save()
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