Commit f4167cb8 authored by Lukas Burgey's avatar Lukas Burgey
Browse files

Restructure the authentication

parent 11572b13
import logging
import json
from urllib.request import Request, urlopen
from django.db.utils import OperationalError
from django.core.exceptions import ObjectDoesNotExist
from django.db import models as db_models
from oic.oic import Client
from oic.oic.message import RegistrationResponse
from oic.utils.authn.client import CLIENT_AUTHN_METHOD
from ... import models
from . import utils
LOGGER = logging.getLogger(__name__)
......@@ -24,17 +24,21 @@ class OIDCConfig(db_models.Model):
@property
def registration_response(self):
info = {"client_id": self.client_id,
"client_secret": self.client_secret}
info = {
'client_id': self.client_id,
'client_secret': self.client_secret
}
return RegistrationResponse(**info)
@property
def oidc_client(self):
# create the client object if does no yet exist
if self.id not in OIDC_CLIENT:
new_oidc_client = Client(client_authn_method=CLIENT_AUTHN_METHOD)
new_oidc_client.provider_config(self.issuer_uri)
new_oidc_client.store_registration_info(self.registration_response)
OIDC_CLIENT[self.id] = new_oidc_client
return OIDC_CLIENT[self.id]
@property
......@@ -65,12 +69,7 @@ def default_idp():
class OIDCTokenAuthBackend(object):
def get_user_info(self, request, access_token, token_type='Bearer'):
try:
idp_id = request.session['idp_id']
except OperationalError:
LOGGER.error('OperationalError: Unable to get from session')
raise
idp_id = utils.get_session(request, 'idp_id', None)
oidc_config = OIDCConfig.objects.get(id=idp_id)
req = Request(
oidc_config.oidc_client.provider_info['userinfo_endpoint']
......@@ -85,23 +84,26 @@ class OIDCTokenAuthBackend(object):
if token is None:
return None
# get the user info from the idp
user_info = self.get_user_info(request, token)
idp_id = utils.get_session(request, 'idp_id', None)
try:
user = models.User.objects.get(
# if we know the user we return him
oidc_config = OIDCConfig.objects.get(id=idp_id)
return oidc_config.users.get(
sub=user_info['sub']
)
return user
except ObjectDoesNotExist:
# if we do not know the user yet, we create him
user = models.construct_user(user_info)
user.save()
return user
return None
def get_user(self, user_id):
try:
return models.User.objects.get(pk=user_id)
except models.User.DoesNotExist:
return models.User.objects.get(
pk=user_id
)
except ObjectDoesNotExist:
return None
import logging
from django.db.utils import OperationalError
LOGGER = logging.getLogger(__name__)
def get_session(request, key, default):
try:
value = request.session.get(key, None)
except OperationalError as exp:
raise OperationalError('get_session: Error getting from session: {}'.format(exp))
if value is not None:
return value
return default
def set_session(request, key, value):
try:
value = request.session[key] = value
except OperationalError as exp:
raise OperationalError('get_session: Error setting in session: {}'.format(exp))
import json
import logging
from django.contrib.auth import authenticate, login, logout
from django.db.utils import OperationalError
from django.shortcuts import redirect
from django.views import View
from rest_framework import generics, views
......@@ -12,6 +11,7 @@ from oic.oic.message import AuthorizationResponse
from .models import OIDCConfig, default_idp
from .serializers import AuthInfoSerializer
from . import utils
LOGGER = logging.getLogger(__name__)
......@@ -26,51 +26,27 @@ def idp_id_from_request(request):
return default_idp().id
def get_session(request, key, default):
try:
value = request.session.get(key, None)
except OperationalError:
LOGGER.error('Operational: Error getting from session')
raise
if value is not None:
return value
return default
def set_session(request, key, value):
try:
value = request.session[key] = value
except OperationalError:
LOGGER.error('Operational: Error setting in session')
raise
class Auth(View):
def get(self, request):
try:
state = rndstr()
idp_id = idp_id_from_request(request)
oidc_config = OIDCConfig.objects.get(id=idp_id)
set_session(request, 'state', state)
set_session(request, 'idp_id', idp_id)
utils.set_session(request, 'state', state)
utils.set_session(request, 'idp_id', idp_id)
try:
return redirect(
oidc_config.get_auth_request(
oidc_config.oidc_client,
state,
)
)
except Exception:
LOGGER.error('Malformed oidc config: %s', oidc_config)
raise
auth_redirect = oidc_config.get_auth_request(
oidc_config.oidc_client,
state,
)
LOGGER.debug('redirecting user to oidc client %s', oidc_config)
return redirect(auth_redirect)
except Exception as exception:
LOGGER.error('request: %s', exception)
LOGGER.error('Auth: %s', exception)
# the error is deleted from the session when the state is delivered
request.session['error'] = 'Server Error'
return redirect('/')
......@@ -79,8 +55,8 @@ class Auth(View):
class AuthCallback(View):
def get(self, request):
try:
state = get_session(request, 'state', None)
idp_id = get_session(request, 'idp_id', default_idp().id)
state = utils.get_session(request, 'state', None)
idp_id = utils.get_session(request, 'idp_id', default_idp().id)
oidc_config = OIDCConfig.objects.get(id=idp_id)
aresp = oidc_config.oidc_client.parse_response(
......@@ -123,23 +99,24 @@ class AuthCallback(View):
if user is None:
# authentication failed -> "401"
LOGGER.error('User failed to log in')
request.session['error'] = 'Login failed'
utils.set_session(request, 'error', 'Login failed')
# response = HttpResponse('Unauthorized', status=401)
elif not user.is_active:
# user is deactivated -> "403"
LOGGER.info('%s tried to log in', user)
request.session['error'] = 'Account deactivated'
utils.set_session(request, 'error', 'Account deactivated')
# response = HttpResponse('Forbidden', status=403)
else:
# user authenticated -> back to frontend
login(request, user)
LOGGER.debug('authenticated %s', user)
LOGGER.debug('oidc client %s authenticated user as %s', oidc_config, user)
response.set_cookie('sessionid', request.COOKIES['sessionid'])
return response
except Exception as exception:
LOGGER.error('request: %s', exception)
LOGGER.error('AuthCallback: %s', exception)
# the error is deleted from the session when the state is delivered
request.session['error'] = 'Server Error'
return redirect('/')
......@@ -148,6 +125,7 @@ class AuthCallback(View):
class LogoutView(views.APIView):
def post(self, request):
LOGGER.debug('logged out %s', request.user)
# TODO should we logout the user at the oidc client?
logout(request)
return Response({})
......
......@@ -12,6 +12,7 @@ from django.db import models
from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
from .auth.v1.models import OIDCConfig
LOGGER = logging.getLogger(__name__)
......@@ -269,6 +270,12 @@ class User(AbstractUser):
default=True,
editable=False,
)
# the idp which authenticated the user
idp = models.ForeignKey(
OIDCConfig,
related_name='users',
on_delete=models.CASCADE,
)
# we hide deleted keys here
# the full list of ssh keys is self._ssh_keys
......
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