From 796db1e83b4f940ace16974066f05f043ae3fa9a Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Mon, 4 Apr 2022 11:46:39 +0200 Subject: [PATCH] Remove models, update settings --- server/config/settings/base.py | 15 +++ server/example.env | 6 ++ server/requirements/requirements-dev.txt | 129 +++++++++++++---------- server/requirements/requirements.in | 1 + server/requirements/requirements.txt | 60 +++++++---- server/vbv_lernwelt/sso/client.py | 29 +++++ server/vbv_lernwelt/sso/models.py | 3 - server/vbv_lernwelt/sso/urls.py | 14 +++ server/vbv_lernwelt/sso/views.py | 32 +++++- 9 files changed, 205 insertions(+), 84 deletions(-) create mode 100644 server/vbv_lernwelt/sso/client.py delete mode 100644 server/vbv_lernwelt/sso/models.py create mode 100644 server/vbv_lernwelt/sso/urls.py diff --git a/server/config/settings/base.py b/server/config/settings/base.py index c53a501c..961afe03 100644 --- a/server/config/settings/base.py +++ b/server/config/settings/base.py @@ -448,6 +448,21 @@ if "django_redis.cache.RedisCache" in env("VBV_DJANGO_CACHE_BACKEND", default="" } } +# OAuth/OpenId Connect + +OAUTH = { + "client_name": env("OAUTH_CLIENT_NAME", default="lernetz"), + "client_id": env("OAUTH_CLIENT_ID", default=""), + "client_secret": env("OAUTH_CLIENT_SECRET", default=""), + "access_token_url": env("OAUTH_ACCESS_TOKEN_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/token"), + "authorize_url": env("OAUTH_AUTHORIZE_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/tokenk"), + "api_base_url": env("OAUTH_API_BASE_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/"), + "local_redirect_uri": env("OAUTH_LOCAL_DIRECT_URI", default="http://localhost:8000/api/oauth/callback/"), + 'client_kwargs': { + 'scope': '', + } +} + if DJANGO_DEV_MODE == "development": # http://whitenoise.evans.io/en/latest/django.html#using-whitenoise-in-development INSTALLED_APPS = ["whitenoise.runserver_nostatic"] + INSTALLED_APPS # noqa F405 diff --git a/server/example.env b/server/example.env index f5e90aee..0549d8cd 100644 --- a/server/example.env +++ b/server/example.env @@ -1,3 +1,9 @@ export VBV_DATABASE_URL='postgres://vbv_lernwelt@localhost:5432/vbv_lernwelt' #export VBV_DJANGO_LOGGING_CONF=VBV_DJANGO_LOGGING_CONF_CONSOLE_COLOR export VBV_DJANGO_DEBUG=True +export OAUTH_CLIENT_ID=iterativ +export OAUTH_CLIENT_SECRET=abced-1234 +export OAUTH_ACCESS_TOKEN_URL=https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/token +export OAUTH_AUTHORIZE_URL=https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/auth +export OAUTH_API_BASE_URL=https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/ +export OAUTH_LOCAL_REDIRECT_URI=http://localhost:8000/api/oauth/callback/ diff --git a/server/requirements/requirements-dev.txt b/server/requirements/requirements-dev.txt index b0cfb994..0e1a44f2 100644 --- a/server/requirements/requirements-dev.txt +++ b/server/requirements/requirements-dev.txt @@ -2,8 +2,10 @@ # This file is autogenerated by pip-compile with python 3.10 # To update, run: # -# pip-compile --output-file=requirements-dev.txt requirements-dev.in +# pip-compile requirements-dev.in # +anyio==3.5.0 + # via watchgod appnope==0.1.2 # via ipython argon2-cffi==21.3.0 @@ -14,52 +16,60 @@ asgiref==3.5.0 # via # django # uvicorn -astroid==2.9.3 +astroid==2.11.2 # via pylint asttokens==2.0.5 # via stack-data +async-timeout==4.0.2 + # via redis attrs==21.4.0 # via # jsonschema # pytest +authlib==1.0.0 + # via -r requirements.in backcall==0.2.0 # via ipython -black==22.1.0 - # via - # -r requirements-dev.in - # ipython +black==22.3.0 + # via -r requirements-dev.in certifi==2021.10.8 # via # requests # sentry-sdk cffi==1.15.0 - # via argon2-cffi-bindings + # via + # argon2-cffi-bindings + # cryptography cfgv==3.3.1 # via pre-commit -charset-normalizer==2.0.11 +charset-normalizer==2.0.12 # via requests -click==8.0.3 +click==8.1.1 # via # black # django-click # pip-tools # uvicorn -concurrent-log-handler==0.9.19 +concurrent-log-handler==0.9.20 # via -r requirements.in coreapi==2.3.3 # via djangorestframework-stubs coreschema==0.0.4 # via coreapi -coverage==6.3.1 +coverage==6.3.2 # via # -r requirements-dev.in # django-coverage-plugin +cryptography==36.0.2 + # via authlib decorator==5.1.1 # via # ipdb # ipython deprecated==1.2.13 # via redis +dill==0.3.4 + # via pylint distlib==0.3.4 # via virtualenv dj-database-url==0.5.0 @@ -87,7 +97,7 @@ django-debug-toolbar==3.2.4 # via -r requirements-dev.in django-extensions==3.1.5 # via -r requirements-dev.in -django-htmx==1.8.0 +django-htmx==1.9.0 # via -r requirements.in django-ipware==4.0.2 # via -r requirements.in @@ -97,11 +107,11 @@ django-ratelimit==3.0.1 # via -r requirements.in django-redis==5.2.0 # via -r requirements.in -django-stubs==1.9.0 +django-stubs==1.10.1 # via # -r requirements-dev.in # djangorestframework-stubs -django-stubs-ext==0.3.1 +django-stubs-ext==0.4.0 # via django-stubs djangorestframework==3.13.1 # via @@ -109,17 +119,17 @@ djangorestframework==3.13.1 # drf-spectacular djangorestframework-stubs==1.4.0 # via -r requirements-dev.in -drf-spectacular==0.21.2 +drf-spectacular==0.22.0 # via -r requirements.in environs==9.5.0 # via -r requirements.in -executing==0.8.2 +executing==0.8.3 # via stack-data factory-boy==3.2.1 # via -r requirements-dev.in -faker==12.0.0 +faker==13.3.4 # via factory-boy -filelock==3.4.2 +filelock==3.6.0 # via virtualenv flake8==4.0.1 # via @@ -139,19 +149,21 @@ h11==0.13.0 # via uvicorn hiredis==2.0.0 # via -r requirements.in -httptools==0.3.0 +httptools==0.4.0 # via uvicorn -identify==2.4.7 +identify==2.4.12 # via pre-commit idna==3.3 - # via requests + # via + # anyio + # requests inflection==0.5.1 # via drf-spectacular iniconfig==1.1.1 # via pytest ipdb==0.13.9 # via -r requirements-dev.in -ipython==8.0.1 +ipython==8.2.0 # via ipdb isort==5.10.1 # via @@ -161,15 +173,15 @@ itypes==1.2.0 # via coreapi jedi==0.18.1 # via ipython -jinja2==3.0.3 +jinja2==3.1.1 # via coreschema jsonschema==4.4.0 # via drf-spectacular lazy-object-proxy==1.7.1 # via astroid -markupsafe==2.0.1 +markupsafe==2.1.1 # via jinja2 -marshmallow==3.14.1 +marshmallow==3.15.0 # via environs matplotlib-inline==0.1.3 # via ipython @@ -177,7 +189,7 @@ mccabe==0.6.1 # via # flake8 # pylint -mypy==0.931 +mypy==0.942 # via # -r requirements-dev.in # django-stubs @@ -190,6 +202,7 @@ nodeenv==1.6.0 # via pre-commit packaging==21.3 # via + # marshmallow # pytest # pytest-sugar # redis @@ -203,22 +216,22 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -pillow==9.0.0 +pillow==9.0.1 # via -r requirements.in -pip-tools==6.4.0 +pip-tools==6.5.1 # via -r requirements-dev.in -platformdirs==2.4.1 +platformdirs==2.5.1 # via # black # pylint # virtualenv pluggy==1.0.0 # via pytest -portalocker==2.3.2 +portalocker==2.4.0 # via concurrent-log-handler pre-commit==2.17.0 # via -r requirements-dev.in -prompt-toolkit==3.0.26 +prompt-toolkit==3.0.28 # via ipython psycopg2-binary==2.9.3 # via -r requirements.in @@ -236,11 +249,11 @@ pyflakes==2.4.0 # via flake8 pygments==2.11.2 # via ipython -pylint==2.12.2 +pylint==2.13.4 # via # pylint-django # pylint-plugin-utils -pylint-django==2.5.0 +pylint-django==2.5.3 # via -r requirements-dev.in pylint-plugin-utils==0.7 # via pylint-django @@ -248,7 +261,7 @@ pyparsing==3.0.7 # via packaging pyrsistent==0.18.1 # via jsonschema -pytest==6.2.5 +pytest==7.1.1 # via # -r requirements-dev.in # pytest-django @@ -259,15 +272,15 @@ pytest-sugar==0.9.4 # via -r requirements-dev.in python-dateutil==2.8.2 # via faker -python-dotenv==0.19.2 +python-dotenv==0.20.0 # via # environs # uvicorn python-json-logger==2.0.2 # via -r requirements.in -python-slugify==5.0.2 +python-slugify==6.1.1 # via -r requirements.in -pytz==2021.3 +pytz==2022.1 # via # -r requirements.in # django @@ -277,7 +290,7 @@ pyyaml==6.0 # drf-spectacular # pre-commit # uvicorn -redis==4.1.2 +redis==4.2.1 # via # -r requirements.in # django-redis @@ -285,7 +298,7 @@ requests==2.27.1 # via # coreapi # djangorestframework-stubs -sentry-sdk==1.5.4 +sentry-sdk==1.5.8 # via -r requirements.in six==1.16.0 # via @@ -295,32 +308,34 @@ six==1.16.0 # virtualenv smmap==5.0.0 # via gitdb +sniffio==1.2.0 + # via anyio sqlparse==0.4.2 # via # django # django-debug-toolbar -stack-data==0.1.4 +stack-data==0.2.0 # via ipython structlog==21.5.0 # via -r requirements.in termcolor==1.1.0 # via pytest-sugar -testfixtures==6.18.3 +testfixtures==6.18.5 # via flake8-isort text-unidecode==1.3 # via python-slugify toml==0.10.2 # via - # django-stubs # ipdb # pre-commit - # pylint - # pytest -tomli==2.0.0 +tomli==2.0.1 # via # black + # django-stubs # mypy # pep517 + # pylint + # pytest traitlets==5.1.1 # via # ipython @@ -329,11 +344,11 @@ trufflehog==2.2.1 # via -r requirements-dev.in trufflehogregexes==0.0.7 # via trufflehog -types-pytz==2021.3.4 +types-pytz==2021.3.6 # via django-stubs -types-pyyaml==6.0.4 +types-pyyaml==6.0.5 # via django-stubs -typing-extensions==4.0.1 +typing-extensions==4.1.1 # via # django-stubs # django-stubs-ext @@ -343,33 +358,33 @@ uritemplate==4.1.1 # via # coreapi # drf-spectacular -urllib3==1.26.8 +urllib3==1.26.9 # via # requests # sentry-sdk -uvicorn[standard]==0.17.1 +uvicorn[standard]==0.17.6 # via -r requirements.in uvloop==0.16.0 # via uvicorn -virtualenv==20.13.0 +virtualenv==20.14.0 # via pre-commit -watchdog==2.1.6 +watchdog==2.1.7 # via werkzeug -watchgod==0.7 +watchgod==0.8.1 # via # -r requirements-dev.in # uvicorn wcwidth==0.2.5 # via prompt-toolkit -websockets==10.1 +websockets==10.2 # via uvicorn -werkzeug[watchdog]==2.0.2 +werkzeug[watchdog]==2.1.0 # via -r requirements-dev.in wheel==0.37.1 # via pip-tools -whitenoise==5.3.0 +whitenoise==6.0.0 # via -r requirements.in -wrapt==1.13.3 +wrapt==1.14.0 # via # astroid # deprecated diff --git a/server/requirements/requirements.in b/server/requirements/requirements.in index ac184ff0..292a80b4 100644 --- a/server/requirements/requirements.in +++ b/server/requirements/requirements.in @@ -1,3 +1,4 @@ +Authlib pytz # https://github.com/stub42/pytz python-slugify # https://github.com/un33k/python-slugify Pillow # https://github.com/python-pillow/Pillow diff --git a/server/requirements/requirements.txt b/server/requirements/requirements.txt index 5b212856..2a89d07f 100644 --- a/server/requirements/requirements.txt +++ b/server/requirements/requirements.txt @@ -2,8 +2,10 @@ # This file is autogenerated by pip-compile with python 3.10 # To update, run: # -# pip-compile --output-file=requirements.txt requirements.in +# pip-compile requirements.in # +anyio==3.5.0 + # via watchgod argon2-cffi==21.3.0 # via -r requirements.in argon2-cffi-bindings==21.2.0 @@ -12,18 +14,26 @@ asgiref==3.5.0 # via # django # uvicorn +async-timeout==4.0.2 + # via redis attrs==21.4.0 # via jsonschema +authlib==1.0.0 + # via -r requirements.in certifi==2021.10.8 # via sentry-sdk cffi==1.15.0 - # via argon2-cffi-bindings -click==8.0.3 + # via + # argon2-cffi-bindings + # cryptography +click==8.1.1 # via # django-click # uvicorn -concurrent-log-handler==0.9.19 +concurrent-log-handler==0.9.20 # via -r requirements.in +cryptography==36.0.2 + # via authlib deprecated==1.2.13 # via redis dj-database-url==0.5.0 @@ -41,7 +51,7 @@ django-click==2.3.0 # via -r requirements.in django-cors-headers==3.11.0 # via -r requirements.in -django-htmx==1.8.0 +django-htmx==1.9.0 # via -r requirements.in django-ipware==4.0.2 # via -r requirements.in @@ -55,7 +65,7 @@ djangorestframework==3.13.1 # via # -r requirements.in # drf-spectacular -drf-spectacular==0.21.2 +drf-spectacular==0.22.0 # via -r requirements.in environs==9.5.0 # via -r requirements.in @@ -65,19 +75,23 @@ h11==0.13.0 # via uvicorn hiredis==2.0.0 # via -r requirements.in -httptools==0.3.0 +httptools==0.4.0 # via uvicorn +idna==3.3 + # via anyio inflection==0.5.1 # via drf-spectacular jsonschema==4.4.0 # via drf-spectacular -marshmallow==3.14.1 +marshmallow==3.15.0 # via environs packaging==21.3 - # via redis -pillow==9.0.0 + # via + # marshmallow + # redis +pillow==9.0.1 # via -r requirements.in -portalocker==2.3.2 +portalocker==2.4.0 # via concurrent-log-handler psycopg2-binary==2.9.3 # via -r requirements.in @@ -87,15 +101,15 @@ pyparsing==3.0.7 # via packaging pyrsistent==0.18.1 # via jsonschema -python-dotenv==0.19.2 +python-dotenv==0.20.0 # via # environs # uvicorn python-json-logger==2.0.2 # via -r requirements.in -python-slugify==5.0.2 +python-slugify==6.1.1 # via -r requirements.in -pytz==2021.3 +pytz==2022.1 # via # -r requirements.in # django @@ -104,12 +118,14 @@ pyyaml==6.0 # via # drf-spectacular # uvicorn -redis==4.1.2 +redis==4.2.1 # via # -r requirements.in # django-redis -sentry-sdk==1.5.4 +sentry-sdk==1.5.8 # via -r requirements.in +sniffio==1.2.0 + # via anyio sqlparse==0.4.2 # via django structlog==21.5.0 @@ -118,19 +134,19 @@ text-unidecode==1.3 # via python-slugify uritemplate==4.1.1 # via drf-spectacular -urllib3==1.26.8 +urllib3==1.26.9 # via sentry-sdk -uvicorn[standard]==0.17.1 +uvicorn[standard]==0.17.6 # via -r requirements.in uvloop==0.16.0 # via uvicorn -watchgod==0.7 +watchgod==0.8.1 # via uvicorn -websockets==10.1 +websockets==10.2 # via uvicorn -whitenoise==5.3.0 +whitenoise==6.0.0 # via -r requirements.in -wrapt==1.13.3 +wrapt==1.14.0 # via deprecated # The following packages are considered to be unsafe in a requirements file: diff --git a/server/vbv_lernwelt/sso/client.py b/server/vbv_lernwelt/sso/client.py new file mode 100644 index 00000000..cfabe4c2 --- /dev/null +++ b/server/vbv_lernwelt/sso/client.py @@ -0,0 +1,29 @@ +from authlib.integrations.django_client import OAuth +from django.conf import settings + +# # https://docs.authlib.org/en/latest/client/frameworks.html#frameworks-clients +# def fetch_token(_name, request): +# try: +# token = OAuth2Token.objects.get( +# user=request.user +# ) +# return token.to_token() +# except (OAuth2Token.DoesNotExist, TypeError): +# return None + + +# oauth = OAuth(fetch_token=fetch_token) +oauth = OAuth() +oauth.register( + name=settings.OAUTH["client_name"], + client_id=settings.OAUTH["client_id"], + client_secret=settings.OAUTH["client_secret"], + request_token_url=None, + request_token_params=None, + access_token_url=settings.OAUTH["access_token_url"], + access_token_params=None, + authorize_url=settings.OAUTH["authorize_url"], + authorize_params=None, + api_base_url=settings.OAUTH["api_base_url"], + client_kwargs=settings.OAUTH["client_kwargs"] +) diff --git a/server/vbv_lernwelt/sso/models.py b/server/vbv_lernwelt/sso/models.py deleted file mode 100644 index 71a83623..00000000 --- a/server/vbv_lernwelt/sso/models.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/server/vbv_lernwelt/sso/urls.py b/server/vbv_lernwelt/sso/urls.py new file mode 100644 index 00000000..efc9b219 --- /dev/null +++ b/server/vbv_lernwelt/sso/urls.py @@ -0,0 +1,14 @@ +from django.urls import path +from django.conf.urls import url, include +from rest_framework.routers import DefaultRouter + +from . import views + +router = DefaultRouter() + +app_name = 'sso' +urlpatterns = [ + path(r'^login/', views.login, name='login'), + path(r'^callback/', views.authorize, name='authorize'), + url(r"^sso/", include(router.urls)), +] diff --git a/server/vbv_lernwelt/sso/views.py b/server/vbv_lernwelt/sso/views.py index 91ea44a2..91e7eb57 100644 --- a/server/vbv_lernwelt/sso/views.py +++ b/server/vbv_lernwelt/sso/views.py @@ -1,3 +1,31 @@ -from django.shortcuts import render +import logging +from authlib.integrations.base_client import OAuthError +from django.conf import settings +from django.shortcuts import redirect +from sentry_sdk import capture_exception +from django.contrib.auth import login as dj_login -# Create your views here. +from vbv_lernwelt.sso.client import oauth + +logger = logging.getLogger(__name__) + +OAUTH_REDIRECT = 'oauth-redirect' + + +def login(request): + hep_oauth_client = oauth.create_client('hep') + redirect_uri = settings.OAUTH_LOCAL_REDIRECT_URI + return hep_oauth_client.authorize_redirect(request, redirect_uri) + + +def authorize(request): + try: + logger.debug(request) + except OAuthError as e: + logger.warning(f'OAuth error: {e}') + if not settings.DEBUG: + capture_exception(e) + return redirect(f'/{OAUTH_REDIRECT}?state=someerror') + + + return redirect(f'/{OAUTH_REDIRECT}?state=success')