Commit 1a270c0b authored by Lukas Burgey's avatar Lukas Burgey

Initialize repository

parents
.secret.key
env
db.cnf
static
deployment
#!/bin/zsh
rsync -ra --exclude=__pycache__ ~/ba/src/backend/django_backend hdf-portal.data.kit.edu:/home/hdf/backend/
ssh hdf-portal.data.kit.edu systemctl restart uwsgi
from django.contrib import admin
from django.apps import apps
from . import models
b = apps.get_app_config('backend')
# register all models of the skat app
for _, model in b.models.items():
admin.site.register(model)
from django.apps import AppConfig
class BackendConfig(AppConfig):
name = 'backend'
from rest_framework.authentication import SessionAuthentication
class CsrfExemptSessionAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
return # To not perform the csrf check previously happening
from urllib.request import Request, urlopen
from .. import models
from django.core.exceptions import ObjectDoesNotExist
from .oidc import OIDC_CLIENT
import json
class OIDCTokenAuthBackend(object):
def get_user_info(self, access_token, token_type='Bearer'):
q = Request(OIDC_CLIENT.provider_info['userinfo_endpoint'])
auth = (token_type + ' ' + access_token)
q.add_header('Authorization', auth)
userinfo_bytes = urlopen(q).read()
return json.loads(userinfo_bytes.decode('UTF-8'))
def authenticate(self, request, token=None):
if token is None:
return None
user_info = self.get_user_info(token)
try:
user = models.User.objects.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 None
from oic.oic import Client
from oic.utils.authn.client import CLIENT_AUTHN_METHOD
from oic.oic.message import RegistrationResponse
import os
OIDC_CLIENT_ID = os.environ.get('OIDC_CLIENT_ID', '')
OIDC_CLIENT_ID_SECRET = os.environ.get('OIDC_CLIENT_ID_SECRET', '')
OIDC_REDIRECT_URI = os.environ.get('OIDC_REDIRECT_URI', '')
OIDC_ISSUER_URI = os.environ.get('OIDC_ISSUER_URI', '')
OIDC_CLIENT = Client(client_authn_method=CLIENT_AUTHN_METHOD)
OIDC_CLIENT.provider_config(OIDC_ISSUER_URI)
info = {"client_id": OIDC_CLIENT_ID, "client_secret": OIDC_CLIENT_ID_SECRET}
client_reg = RegistrationResponse(**info)
OIDC_CLIENT.store_registration_info(client_reg)
from .oidc import OIDC_CLIENT, OIDC_CLIENT_ID, OIDC_REDIRECT_URI
from django.contrib.auth import authenticate, login
from django.db.utils import OperationalError
from django.shortcuts import redirect, render
from django.views import View
from oic import rndstr
from oic.oic.message import AuthorizationResponse
import json
class Auth(View):
def get(self, request, **kwargs):
state = rndstr()
try:
request.session['state'] = state
request.session['nonce'] = rndstr()
except OperationalError:
return redirect('/?error=not_operational')
args = {
'client_id': OIDC_CLIENT_ID,
'response_type': 'code',
'scope': ['openid', 'profile', 'email'],
'redirect_uri': OIDC_REDIRECT_URI,
'state': state,
}
auth_req = OIDC_CLIENT.construct_AuthorizationRequest(
request_args=args)
login_url = auth_req.request(OIDC_CLIENT.authorization_endpoint)
return redirect(login_url)
class AuthCallback(View):
def get(self, request, **kwargs):
try:
state = request.session['state']
except OperationalError:
return redirect('/?error=not_operational')
aresp = OIDC_CLIENT.parse_response(
AuthorizationResponse,
info=json.dumps(request.GET))
assert state == aresp['state']
code = aresp['code']
args = {
'code': code,
}
ac_token_response = OIDC_CLIENT.do_access_token_request(
state=aresp['state'],
request_args=args)
# does fail with 'invalid_token'
# user_info = OIDC_CLIENT.do_user_info_request(
# state=aresp['state'])
# user_info = self.get_user_info(ac_token_response['access_token'])
# try:
# u = models.User.objects.get(sub=user_info['sub'])
# except:
# u = models.user_from_user_info(user_info)
# u.save()
# response = render(request, 'backend/index.html', context)
user = authenticate(request, token=ac_token_response['access_token'])
if user is None:
# authentication failed -> 401
response = render(request, 'backend/index.html', status=401)
else:
# redirect back to the frontend
login(request, user)
response = redirect('https://hdf-portal.data.kit.edu/')
response.set_cookie('sessionid', request.COOKIES['sessionid'])
return response
from django.contrib.auth.models import AbstractUser, Group
from django.db import models
class User(AbstractUser):
sub = models.CharField(max_length=150, blank=True, null=True)
password = models.CharField(max_length=150, blank=True, null=True)
def construct_user(user_info):
return User(
sub=user_info['sub'],
name=user_info['name'],
first_name=user_info['given_name'],
last_name=user_info['family_name'],
email=user_info['email'],
username=user_info['email'],
)
class Site(models.Model):
name = models.CharField(max_length=150, unique=True)
description = models.TextField(max_length=300, blank=True)
def __str__(self):
return self.name
class Service(models.Model):
name = models.CharField(max_length=150, unique=True)
description = models.TextField(max_length=300, blank=True)
site = models.ForeignKey(
Site, related_name='services')
groups = models.ManyToManyField(
Group, related_name='services', blank=True)
def __str__(self):
return self.name + '@' + self.site.name
class SSHPublicKey(models.Model):
name = models.CharField(max_length=150, unique=True)
key = models.TextField(max_length=1000)
user = models.ForeignKey(
User, related_name='ssh_keys')
def __str__(self):
return self.name
class State(object):
def __init__(self, user):
if user.is_authenticated:
self.services = Service.objects.filter(
groups__user=user
)
else:
self.services = []
from django.contrib.auth import logout
from django.db import connections
from django.db.utils import OperationalError
from django.shortcuts import get_object_or_404, redirect
from rest_framework import views, viewsets
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework import status
from . import serializers, models
class OperationalView(views.APIView):
authentication_classes = []
permission_classes = (AllowAny,)
def get(self, request, format=None):
try:
db_conn = connections['default']
db_conn.cursor()
# we check if we can access the state
request.session
except OperationalError:
op = False
else:
op = True
response = {'operational': op}
return Response(response)
class LogoutView(views.APIView):
def get(self, request, format=None):
logout(request)
response = {'logged_in': False}
return Response(response)
class StateView(views.APIView):
permission_classes = (AllowAny,)
def get(self, request, format=None):
response = {
'logged_in': request.user.is_authenticated()
}
if request.user.is_authenticated():
response['user'] = serializers.UserSerializer(request.user).data
response['state'] = serializers.StateSerializer(
models.State(request.user)).data
return Response(response)
class ServiceViewSet(viewsets.ModelViewSet):
serializer_class = serializers.ServiceSerializer
queryset = models.Service.objects.all()
class SSHPublicKeyView(views.APIView):
def get(self, request, format=None):
pass
def post(self, request, format=None):
if 'type' in request.data:
request_type = request.data['type']
if request_type == 'remove':
if 'name' in request.data:
key = get_object_or_404(
models.SSHPublicKey,
name=request.data['name'])
key.delete()
return redirect('/backend/api/state/')
elif request_type == 'add':
if 'key' in request.data:
key = models.SSHPublicKey()
key.user = request.user
key.name = request.data['key']['name']
key.key = request.data['key']['key']
key.save()
return Response({'ok': True},
status=status.HTTP_201_CREATED)
return Response({'ok': False}, status=status.HTTP_400_BAD_REQUEST)
from django.contrib.auth.models import Group
from rest_framework import serializers
from . import models
class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = Group
fields = ['name']
class SiteSerializer(serializers.ModelSerializer):
class Meta:
model = models.Site
fields = ['name']
class ServiceSerializer(serializers.ModelSerializer):
site = SiteSerializer()
groups = GroupSerializer(many=True)
class Meta:
model = models.Site
exclude = ['id']
class SSHPublicKeySerializer(serializers.ModelSerializer):
class Meta:
model = models.SSHPublicKey
fields = ['name', 'key']
class UserSerializer(serializers.ModelSerializer):
groups = GroupSerializer(many=True)
ssh_keys = SSHPublicKeySerializer(many=True)
class Meta:
model = models.User
fields = ['sub', 'email', 'username', 'ssh_keys', 'groups']
class StateSerializer(serializers.Serializer):
services = ServiceSerializer(many=True)
<html>
<head>
<title>Backend</title>
</head>
<body>
{{ data }}
<p>
{{ user_info }}
</p>
</body>
</html>
from django.test import TestCase
# Create your tests here.
"""
Django settings for django_backend project.
Generated by 'django-admin startproject' using Django 1.11.7.
For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
AUTH_USER_MODEL = 'backend.User'
CSRF_HEADER_NAME = 'HTTP_X_CSRFTOKEN'
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
with open('.secret.key') as f:
SECRET_KEY = f.read().strip()
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = [
'hdf-portal.data.kit.edu'
]
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'django_backend.backend',
'corsheaders',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'django_backend.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'django_backend.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'scc-hdfmysql0001_portal',
'USER': os.environ['DB_USER'],
'HOST': 'mysql2g.scc.kit.edu',
'PASSWORD': os.environ['DB_PASSWORD'],
}
}
CORS_ORIGIN_ALLOW_ALL = True
AUTHENTICATION_BACKENDS = [
'django_backend.backend.auth_backend.OIDCTokenAuthBackend',
'django.contrib.auth.backends.ModelBackend',
]
# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/
STATIC_URL = '/backend/static/'
STATIC_ROOT = 'static'
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
# 'rest_framework.authentication.SessionAuthentication',
'django_backend.backend.auth.auth_class.CsrfExemptSessionAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
from django.conf.urls import include, url
from django.contrib import admin
from django_backend.backend.auth import views as auth_views
from rest_framework import routers
from .backend import rest_views
router = routers.DefaultRouter(trailing_slash=False)
router.register(r'services', rest_views.ServiceViewSet)
urlpatterns = [
url(r'^backend/rest/', include(router.urls)),
url(r'^backend/api/state/', rest_views.StateView.as_view()),
url(r'^backend/api/sshkey/', rest_views.SSHPublicKeyView.as_view()),
url(r'^backend/api/operational/', rest_views.OperationalView.as_view()),
url(r'^backend/auth/', auth_views.Auth.as_view()),
url(r'^backend/auth_callback/', auth_views.AuthCallback.as_view()),
url(r'^backend/auth_logout', rest_views.LogoutView.as_view()),
url(r'^backend/admin', admin.site.urls),
]
"""
WSGI config for django_backend project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_backend.settings")
application = get_wsgi_application()
#!/bin/sh
dd if=/dev/random bs=64 count=1 | base64 > .secret.key
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_backend.settings")
try:
from django.core.management import execute_from_command_line
except ImportError:
# The above import may fail for some other reason. Ensure that the
# issue is really that Django is missing to avoid masking other
# exceptions on Python 2.
try:
import django
except ImportError:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
)
raise
execute_from_command_line(sys.argv)
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