wip: Add KC-client and basic methods, signal handler

This commit is contained in:
Christian Cueni 2024-06-19 13:26:50 +02:00
parent a14a09f1ca
commit aa3f222112
16 changed files with 562 additions and 282 deletions

View File

@ -631,6 +631,12 @@ OAUTH_SIGNIN_REDIRECT_URI = env(
"OAUTH_SIGNIN_REDIRECT_URI", default="http://localhost:8000/sso/callback" "OAUTH_SIGNIN_REDIRECT_URI", default="http://localhost:8000/sso/callback"
) )
OAUTH_SIGNIN_URL = env("OAUTH_SIGNIN_URL", default="")
OAUTH_SIGNIN_REALM = env("OAUTH_SIGNIN_REALM", default="vbv")
OAUTH_SIGNIN_ADMIN_CLIENT_ID = env("OAUTH_SIGNIN_ADMIN_CLIENT_ID", default="")
OAUTH_SIGNIN_ADMIN_CLIENT_SECRET = env("OAUTH_SIGNIN_ADMIN_CLIENT_SECRET", default="")
OAUTH_SYNC_ROLES = env.bool("OAUTH_SYNC_ROLES", default=False)
GRAPHENE = { GRAPHENE = {
"SCHEMA": "vbv_lernwelt.core.schema.schema", "SCHEMA": "vbv_lernwelt.core.schema.schema",
"SCHEMA_OUTPUT": "../client/src/gql/schema.graphql", "SCHEMA_OUTPUT": "../client/src/gql/schema.graphql",

View File

@ -2,79 +2,84 @@
# This file is autogenerated by pip-compile with Python 3.10 # This file is autogenerated by pip-compile with Python 3.10
# by the following command: # by the following command:
# #
# pip-compile --output-file=requirements-dev.txt requirements-dev.in # pip-compile requirements-dev.in
# #
aniso8601==9.0.1 aniso8601==9.0.1
# via graphene # via graphene
anyascii==0.3.2 anyascii==0.3.2
# via wagtail # via wagtail
anyio==3.7.1 anyio==4.4.0
# via watchfiles # via
appnope==0.1.3 # httpx
# via ipython # watchfiles
argon2-cffi==21.3.0 argon2-cffi==23.1.0
# via -r requirements.in # via -r requirements.in
argon2-cffi-bindings==21.2.0 argon2-cffi-bindings==21.2.0
# via argon2-cffi # via argon2-cffi
asgiref==3.7.2 asgiref==3.8.1
# via django # via
astroid==2.15.6 # django
# django-cors-headers
# django-stubs
astroid==3.2.2
# via pylint # via pylint
asttokens==2.2.1 asttokens==2.4.1
# via stack-data # via stack-data
async-timeout==4.0.2 async-property==0.2.2
# via python-keycloak
async-timeout==4.0.3
# via redis # via redis
attrs==23.1.0 attrs==23.2.0
# via # via
# jsonschema # jsonschema
# referencing # referencing
# usort # usort
authlib==1.2.1 authlib==1.3.1
# via -r requirements.in # via -r requirements.in
azure-core==1.29.1 azure-core==1.30.2
# via # via
# azure-identity # azure-identity
# azure-storage-blob # azure-storage-blob
azure-identity==1.14.0 azure-identity==1.17.0
# via -r requirements.in # via -r requirements.in
azure-storage-blob==12.17.0 azure-storage-blob==12.20.0
# via -r requirements.in # via -r requirements.in
backcall==0.2.0 bcrypt==4.1.3
# via ipython
bcrypt==4.0.1
# via paramiko # via paramiko
beautifulsoup4==4.11.2 beautifulsoup4==4.11.2
# via wagtail # via wagtail
black==23.7.0 black==24.4.2
# via # via
# -r requirements-dev.in # -r requirements-dev.in
# ufmt # ufmt
boto3==1.28.23 boto3==1.34.129
# via -r requirements.in # via -r requirements.in
botocore==1.31.23 botocore==1.34.129
# via # via
# boto3 # boto3
# s3transfer # s3transfer
brotli==1.0.9 brotli==1.1.0
# via whitenoise # via whitenoise
build==0.10.0 build==1.2.1
# via pip-tools # via pip-tools
caprover-api @ git+https://github.com/iterativ/Caprover-API.git@5013f8fc929e8e3281b9d609e968a782e8e99530 caprover-api @ git+https://github.com/iterativ/Caprover-API.git@5013f8fc929e8e3281b9d609e968a782e8e99530
# via -r requirements-dev.in # via -r requirements-dev.in
certifi==2023.7.22 certifi==2024.6.2
# via # via
# httpcore
# httpx
# requests # requests
# sentry-sdk # sentry-sdk
cffi==1.15.1 cffi==1.16.0
# via # via
# argon2-cffi-bindings # argon2-cffi-bindings
# cryptography # cryptography
# pynacl # pynacl
cfgv==3.3.1 cfgv==3.4.0
# via pre-commit # via pre-commit
charset-normalizer==3.2.0 charset-normalizer==3.3.2
# via requests # via requests
click==8.1.6 click==8.1.7
# via # via
# -r requirements.in # -r requirements.in
# black # black
@ -84,17 +89,18 @@ click==8.1.6
# ufmt # ufmt
# usort # usort
# uvicorn # uvicorn
concurrent-log-handler==0.9.24 concurrent-log-handler==0.9.25
# via -r requirements.in # via -r requirements.in
coverage==7.2.7 coverage==7.5.3
# via # via
# -r requirements-dev.in # -r requirements-dev.in
# django-coverage-plugin # django-coverage-plugin
cryptography==41.0.3 cryptography==42.0.8
# via # via
# authlib # authlib
# azure-identity # azure-identity
# azure-storage-blob # azure-storage-blob
# jwcrypto
# msal # msal
# paramiko # paramiko
# pyjwt # pyjwt
@ -104,13 +110,15 @@ decorator==5.1.1
# ipython # ipython
defusedxml==0.7.1 defusedxml==0.7.1
# via willow # via willow
dill==0.3.7 deprecation==2.1.0
# via python-keycloak
dill==0.3.8
# via pylint # via pylint
distlib==0.3.7 distlib==0.3.8
# via virtualenv # via virtualenv
dj-database-url==2.0.0 dj-database-url==2.2.0
# via -r requirements.in # via -r requirements.in
django==3.2.20 django==3.2.25
# via # via
# -r requirements.in # -r requirements.in
# dj-database-url # dj-database-url
@ -137,96 +145,99 @@ django==3.2.20
# jsonfield # jsonfield
# wagtail # wagtail
# wagtail-localize # wagtail-localize
django-click==2.3.0 django-click==2.4.0
# via -r requirements.in # via -r requirements.in
django-constance==3.1.0 django-constance==3.1.0
# via -r requirements.in # via -r requirements.in
django-cors-headers==4.2.0 django-cors-headers==4.3.1
# via -r requirements.in # via -r requirements.in
django-coverage-plugin==3.1.0 django-coverage-plugin==3.1.0
# via -r requirements-dev.in # via -r requirements-dev.in
django-csp==3.7 django-csp==3.8
# via -r requirements.in # via -r requirements.in
django-debug-toolbar==4.1.0 django-debug-toolbar==4.3.0
# via -r requirements-dev.in # via -r requirements-dev.in
django-extensions==3.2.3 django-extensions==3.2.3
# via -r requirements-dev.in # via -r requirements-dev.in
django-filter==23.2 django-filter==23.5
# via wagtail # via wagtail
django-ipware==5.0.0 django-ipware==7.0.1
# via -r requirements.in # via -r requirements.in
django-jsonform==2.18.0 django-jsonform==2.22.0
# via -r requirements.in # via -r requirements.in
django-model-utils==4.3.1 django-model-utils==4.5.1
# via # via
# -r requirements.in # -r requirements.in
# django-notifications-hq # django-notifications-hq
django-modelcluster==6.0 django-modelcluster==6.3
# via wagtail # via wagtail
django-notifications-hq==1.8.2 django-notifications-hq==1.8.3
# via -r requirements.in # via -r requirements.in
django-permissionedforms==0.1 django-permissionedforms==0.1
# via wagtail # via wagtail
django-picklefield==3.1 django-picklefield==3.2
# via django-constance # via django-constance
django-ratelimit==4.1.0 django-ratelimit==4.1.0
# via -r requirements.in # via -r requirements.in
django-redis==5.3.0 django-redis==5.4.0
# via -r requirements.in # via -r requirements.in
django-storages==1.13.2 django-storages==1.14.3
# via -r requirements.in # via -r requirements.in
django-stubs==4.2.3 django-stubs==5.0.2
# via # via
# -r requirements-dev.in # -r requirements-dev.in
# djangorestframework-stubs # djangorestframework-stubs
django-stubs-ext==4.2.2 django-stubs-ext==5.0.2
# via django-stubs # via django-stubs
django-taggit==4.0.0 django-taggit==4.0.0
# via wagtail # via wagtail
django-treebeard==4.7 django-treebeard==4.7.1
# via wagtail # via wagtail
djangorestframework==3.14.0 django-watchfiles @ https://github.com/q0w/django-watchfiles/archive/issue-1.zip
# via -r requirements-dev.in
djangorestframework==3.15.1
# via # via
# -r requirements.in # -r requirements.in
# drf-spectacular # drf-spectacular
# wagtail # wagtail
djangorestframework-stubs==3.14.2 djangorestframework-stubs==3.15.0
# via -r requirements-dev.in # via -r requirements-dev.in
draftjs-exporter==2.1.7 draftjs-exporter==2.1.7
# via wagtail # via wagtail
drf-spectacular==0.26.4 drf-spectacular==0.27.2
# via -r requirements.in # via -r requirements.in
environs==9.5.0 environs==11.0.0
# via -r requirements.in # via -r requirements.in
et-xmlfile==1.1.0 et-xmlfile==1.1.0
# via openpyxl # via openpyxl
exceptiongroup==1.1.2 exceptiongroup==1.2.1
# via # via
# anyio # anyio
# ipython
# pytest # pytest
execnet==2.0.2 execnet==2.1.1
# via pytest-xdist # via pytest-xdist
executing==1.2.0 executing==2.0.1
# via stack-data # via stack-data
factory-boy==3.3.0 factory-boy==3.3.0
# via # via
# -r requirements-dev.in # -r requirements-dev.in
# wagtail-factories # wagtail-factories
faker==19.3.0 faker==25.8.0
# via factory-boy # via factory-boy
filelock==3.12.2 filelock==3.15.1
# via virtualenv # via virtualenv
filetype==1.2.0 filetype==1.2.0
# via willow # via willow
flake8==6.1.0 flake8==7.1.0
# via # via
# -r requirements-dev.in # -r requirements-dev.in
# flake8-isort # flake8-isort
flake8-isort==6.0.0 flake8-isort==6.1.1
# via -r requirements-dev.in # via -r requirements-dev.in
freezegun==1.2.2 freezegun==1.5.1
# via -r requirements-dev.in # via -r requirements-dev.in
gitdb==4.0.10 gitdb==4.0.11
# via gitdb2 # via gitdb2
gitdb2==4.0.2 gitdb2==4.0.2
# via gitpython # via gitpython
@ -234,7 +245,7 @@ gitpython==3.0.6
# via trufflehog # via trufflehog
graphene==3.3 graphene==3.3
# via graphene-django # via graphene-django
graphene-django==3.1.5 graphene-django==3.2.2
# via wagtail-grapple # via wagtail-grapple
graphql-core==3.2.3 graphql-core==3.2.3
# via # via
@ -245,19 +256,26 @@ graphql-relay==3.2.0
# via # via
# graphene # graphene
# graphene-django # graphene-django
gunicorn==21.2.0 gunicorn==22.0.0
# via -r requirements.in # via -r requirements.in
h11==0.14.0 h11==0.14.0
# via uvicorn # via
# httpcore
# uvicorn
html5lib==1.1 html5lib==1.1
# via wagtail # via wagtail
httptools==0.6.0 httpcore==1.0.5
# via httpx
httptools==0.6.1
# via uvicorn # via uvicorn
identify==2.5.26 httpx==0.27.0
# via python-keycloak
identify==2.5.36
# via pre-commit # via pre-commit
idna==3.4 idna==3.7
# via # via
# anyio # anyio
# httpx
# requests # requests
inflection==0.5.1 inflection==0.5.1
# via drf-spectacular # via drf-spectacular
@ -265,15 +283,15 @@ iniconfig==2.0.0
# via pytest # via pytest
ipdb==0.13.13 ipdb==0.13.13
# via -r requirements-dev.in # via -r requirements-dev.in
ipython==8.14.0 ipython==8.25.0
# via ipdb # via ipdb
isodate==0.6.1 isodate==0.6.1
# via azure-storage-blob # via azure-storage-blob
isort==5.12.0 isort==5.13.2
# via # via
# flake8-isort # flake8-isort
# pylint # pylint
jedi==0.19.0 jedi==0.19.1
# via ipython # via ipython
jmespath==1.0.1 jmespath==1.0.1
# via # via
@ -281,21 +299,21 @@ jmespath==1.0.1
# botocore # botocore
jsonfield==3.1.0 jsonfield==3.1.0
# via django-notifications-hq # via django-notifications-hq
jsonschema==4.19.0 jsonschema==4.22.0
# via drf-spectacular # via drf-spectacular
jsonschema-specifications==2023.7.1 jsonschema-specifications==2023.12.1
# via jsonschema # via jsonschema
jwcrypto==1.5.6
# via python-keycloak
l18n==2021.3 l18n==2021.3
# via wagtail # via wagtail
lazy-object-proxy==1.9.0 libcst==1.4.0
# via astroid
libcst==1.0.1
# via # via
# ufmt # ufmt
# usort # usort
marshmallow==3.20.1 marshmallow==3.21.3
# via environs # via environs
matplotlib-inline==0.1.6 matplotlib-inline==0.1.7
# via ipython # via ipython
mccabe==0.7.0 mccabe==0.7.0
# via # via
@ -305,148 +323,166 @@ moreorless==0.4.0
# via # via
# ufmt # ufmt
# usort # usort
msal==1.23.0 msal==1.28.1
# via # via
# azure-identity # azure-identity
# msal-extensions # msal-extensions
msal-extensions==1.0.0 msal-extensions==1.1.0
# via azure-identity # via azure-identity
mypy==1.4.1 mypy==1.10.0
# via # via -r requirements-dev.in
# -r requirements-dev.in
# django-stubs
# djangorestframework-stubs
mypy-extensions==1.0.0 mypy-extensions==1.0.0
# via # via
# black # black
# mypy # mypy
# typing-inspect newrelic==9.11.0
newrelic==8.11.0
# via -r requirements.in # via -r requirements.in
nodeenv==1.8.0 nodeenv==1.9.1
# via pre-commit # via pre-commit
openpyxl==3.1.2 openpyxl==3.1.4
# via # via
# -r requirements.in # -r requirements.in
# wagtail # wagtail
packaging==23.1 packaging==24.1
# via # via
# black # black
# build # build
# deprecation
# gunicorn # gunicorn
# marshmallow # marshmallow
# msal-extensions
# pytest # pytest
# pytest-sugar # pytest-sugar
<<<<<<< HEAD
paramiko==3.3.1 paramiko==3.3.1
# via # via
# -r requirements.in # -r requirements.in
# sftpserver # sftpserver
parso==0.8.3 parso==0.8.3
=======
paramiko==3.4.0
# via -r requirements.in
parso==0.8.4
>>>>>>> 9e6b9a1e (wip: Add KC-client and basic methods, signal handler)
# via jedi # via jedi
pathspec==0.11.2 pathspec==0.12.1
# via # via
# black # black
# trailrunner # trailrunner
pexpect==4.8.0 pexpect==4.9.0
# via ipython # via ipython
pickleshare==0.7.5 pillow==10.3.0
# via ipython
pillow==10.0.0
# via # via
# -r requirements.in # -r requirements.in
# pillow-heif # pillow-heif
# wagtail # wagtail
pillow-heif==0.13.0 pillow-heif==0.16.0
# via willow # via willow
pip-tools==7.3.0 pip-tools==7.4.1
# via -r requirements-dev.in # via -r requirements-dev.in
platformdirs==3.10.0 platformdirs==4.2.2
# via # via
# black # black
# pylint # pylint
# virtualenv # virtualenv
pluggy==1.2.0 pluggy==1.5.0
# via pytest # via pytest
polib==1.2.0 polib==1.2.0
# via wagtail-localize # via wagtail-localize
portalocker==2.7.0 portalocker==2.8.2
# via # via
# concurrent-log-handler # concurrent-log-handler
# msal-extensions # msal-extensions
pre-commit==3.3.3 pre-commit==3.7.1
# via -r requirements-dev.in # via -r requirements-dev.in
promise==2.3 promise==2.3
# via graphene-django # via graphene-django
prompt-toolkit==3.0.39 prompt-toolkit==3.0.47
# via ipython # via ipython
psycopg2-binary==2.9.7 psycopg2-binary==2.9.9
# via -r requirements.in # via -r requirements.in
ptyprocess==0.7.0 ptyprocess==0.7.0
# via pexpect # via pexpect
pure-eval==0.2.2 pure-eval==0.2.2
# via stack-data # via stack-data
pycodestyle==2.11.0 pycodestyle==2.12.0
# via flake8 # via flake8
pycparser==2.21 pycparser==2.22
# via cffi # via cffi
pycryptodome==3.18.0 pycryptodome==3.20.0
# via -r requirements.in # via -r requirements.in
pyflakes==3.1.0 pyflakes==3.2.0
# via flake8 # via flake8
pygments==2.16.1 pygments==2.18.0
# via ipython # via ipython
pyjwt[crypto]==2.8.0 pyjwt[crypto]==2.8.0
<<<<<<< HEAD
# via msal # via msal
pylint==2.17.5 pylint==2.17.5
=======
# via
# msal
# pyjwt
pylint==3.2.3
>>>>>>> 9e6b9a1e (wip: Add KC-client and basic methods, signal handler)
# via # via
# pylint-django # pylint-django
# pylint-plugin-utils # pylint-plugin-utils
pylint-django==2.5.3 pylint-django==2.5.5
# via -r requirements-dev.in # via -r requirements-dev.in
pylint-plugin-utils==0.8.2 pylint-plugin-utils==0.8.2
# via pylint-django # via pylint-django
pynacl==1.5.0 pynacl==1.5.0
# via paramiko # via paramiko
pyproject-hooks==1.0.0 pyproject-hooks==1.1.0
# via build # via
pytest==7.4.0 # build
# pip-tools
pytest==8.2.2
# via # via
# -r requirements-dev.in # -r requirements-dev.in
# pytest-django # pytest-django
# pytest-order # pytest-order
# pytest-sugar # pytest-sugar
# pytest-xdist # pytest-xdist
pytest-django==4.5.2 pytest-django==4.8.0
# via -r requirements-dev.in # via -r requirements-dev.in
<<<<<<< HEAD
pytest-order==1.2.1 pytest-order==1.2.1
# via -r requirements-dev.in # via -r requirements-dev.in
pytest-sugar==0.9.7 pytest-sugar==0.9.7
=======
pytest-sugar==1.0.0
>>>>>>> 9e6b9a1e (wip: Add KC-client and basic methods, signal handler)
# via -r requirements-dev.in # via -r requirements-dev.in
pytest-xdist==3.5.0 pytest-xdist==3.6.1
# via -r requirements-dev.in # via -r requirements-dev.in
python-dateutil==2.8.2 python-dateutil==2.9.0.post0
# via # via
# -r requirements.in # -r requirements.in
# botocore # botocore
# faker # faker
# freezegun # freezegun
python-dotenv==1.0.0 python-dotenv==1.0.1
# via # via
# environs # environs
# uvicorn # uvicorn
python-http-client==3.3.7 python-http-client==3.3.7
# via sendgrid # via sendgrid
python-ipware==3.0.0
# via django-ipware
python-json-logger==2.0.7 python-json-logger==2.0.7
# via -r requirements.in # via -r requirements.in
python-slugify==8.0.1 python-keycloak==4.1.0
# via -r requirements.in # via -r requirements.in
pytz==2023.3 python-slugify==8.0.4
# via -r requirements.in
pytz==2024.1
# via # via
# -r requirements.in # -r requirements.in
# django # django
# django-modelcluster # django-modelcluster
# django-notifications-hq # django-notifications-hq
# djangorestframework
# l18n # l18n
pyyaml==6.0.1 pyyaml==6.0.1
# via # via
@ -455,30 +491,34 @@ pyyaml==6.0.1
# libcst # libcst
# pre-commit # pre-commit
# uvicorn # uvicorn
redis==4.6.0 redis==5.0.6
# via # via
# -r requirements.in # -r requirements.in
# django-redis # django-redis
referencing==0.30.2 referencing==0.35.1
# via # via
# jsonschema # jsonschema
# jsonschema-specifications # jsonschema-specifications
requests==2.31.0 requests==2.32.3
# via # via
# azure-core # azure-core
# caprover-api # caprover-api
# djangorestframework-stubs # djangorestframework-stubs
# msal # msal
# python-keycloak
# requests-toolbelt
# wagtail # wagtail
rpds-py==0.9.2 requests-toolbelt==1.0.0
# via python-keycloak
rpds-py==0.18.1
# via # via
# jsonschema # jsonschema
# referencing # referencing
s3transfer==0.6.1 s3transfer==0.10.1
# via boto3 # via boto3
sendgrid==6.10.0 sendgrid==6.11.0
# via -r requirements.in # via -r requirements.in
sentry-sdk==1.29.2 sentry-sdk==2.5.1
# via -r requirements.in # via -r requirements.in
sftpserver @ git+https://github.com/lonetwin/sftpserver.git@1d16896d3f0f90d63d1caaf4e199f2a9dde6456f sftpserver @ git+https://github.com/lonetwin/sftpserver.git@1d16896d3f0f90d63d1caaf4e199f2a9dde6456f
# via -r requirements-dev.in # via -r requirements-dev.in
@ -491,29 +531,31 @@ six==1.16.0
# l18n # l18n
# promise # promise
# python-dateutil # python-dateutil
smmap==5.0.0 smmap==5.0.1
# via gitdb # via gitdb
sniffio==1.3.0 sniffio==1.3.1
# via anyio # via
soupsieve==2.4.1 # anyio
# httpx
soupsieve==2.5
# via beautifulsoup4 # via beautifulsoup4
sqlparse==0.4.4 sqlparse==0.5.0
# via # via
# django # django
# django-debug-toolbar # django-debug-toolbar
stack-data==0.6.2 stack-data==0.6.3
# via ipython # via ipython
starkbank-ecdsa==2.2.0 starkbank-ecdsa==2.2.0
# via sendgrid # via sendgrid
stdlibs==2022.10.9 stdlibs==2024.5.15
# via usort # via usort
structlog==23.1.0 structlog==24.2.0
# via -r requirements.in # via -r requirements.in
swapper==1.3.0 swapper==1.3.0
# via django-notifications-hq # via django-notifications-hq
telepath==0.3.1 telepath==0.3.1
# via wagtail # via wagtail
termcolor==2.3.0 termcolor==2.4.0
# via pytest-sugar # via pytest-sugar
text-unidecode==1.3 text-unidecode==1.3
# via # via
@ -530,9 +572,8 @@ tomli==2.0.1
# mypy # mypy
# pip-tools # pip-tools
# pylint # pylint
# pyproject-hooks
# pytest # pytest
tomlkit==0.12.1 tomlkit==0.12.5
# via # via
# pylint # pylint
# ufmt # ufmt
@ -540,7 +581,7 @@ trailrunner==1.4.0
# via # via
# ufmt # ufmt
# usort # usort
traitlets==5.9.0 traitlets==5.14.3
# via # via
# ipython # ipython
# matplotlib-inline # matplotlib-inline
@ -548,82 +589,95 @@ trufflehog==2.2.1
# via -r requirements-dev.in # via -r requirements-dev.in
trufflehogregexes==0.0.7 trufflehogregexes==0.0.7
# via trufflehog # via trufflehog
types-pytz==2023.3.0.0 types-pyyaml==6.0.12.20240311
# via django-stubs
types-pyyaml==6.0.12.11
# via # via
# django-stubs # django-stubs
# djangorestframework-stubs # djangorestframework-stubs
types-requests==2.31.0.2 types-requests==2.32.0.20240602
# via djangorestframework-stubs # via djangorestframework-stubs
types-urllib3==1.26.25.14 typing-extensions==4.12.2
# via types-requests
typing-extensions==4.7.1
# via # via
# anyio
# asgiref # asgiref
# astroid # astroid
# azure-core # azure-core
# azure-identity
# azure-storage-blob # azure-storage-blob
# black
# dj-database-url # dj-database-url
# django-stubs # django-stubs
# django-stubs-ext # django-stubs-ext
# djangorestframework-stubs # djangorestframework-stubs
# libcst # ipython
# jwcrypto
# mypy # mypy
# typing-inspect
# ufmt # ufmt
# uvicorn # uvicorn
# wagtail-localize # wagtail-localize
typing-inspect==0.9.0 ufmt==2.7.0
# via libcst
ufmt==2.2.0
# via -r requirements-dev.in # via -r requirements-dev.in
uritemplate==4.1.1 uritemplate==4.1.1
# via drf-spectacular # via drf-spectacular
urllib3==1.26.16 urllib3==2.2.2
# via # via
# botocore # botocore
# requests # requests
# sentry-sdk # sentry-sdk
usort==1.0.7 # types-requests
usort==1.0.8.post1
# via ufmt # via ufmt
uvicorn[standard]==0.23.2 uvicorn[standard]==0.30.1
# via -r requirements.in # via -r requirements.in
uvloop==0.17.0 uvloop==0.19.0
# via uvicorn # via uvicorn
virtualenv==20.24.2 virtualenv==20.26.2
# via pre-commit # via pre-commit
wagtail==5.1 wagtail==5.2.5
# via # via
# -r requirements.in # -r requirements.in
# wagtail-factories # wagtail-factories
# wagtail-grapple # wagtail-grapple
# wagtail-headless-preview # wagtail-headless-preview
# wagtail-localize # wagtail-localize
wagtail-factories==4.1.0 wagtail-factories==4.2.1
# via -r requirements.in # via -r requirements.in
wagtail-grapple==0.20.0 wagtail-grapple==0.25.1
# via -r requirements.in # via -r requirements.in
wagtail-headless-preview==0.6.0 wagtail-headless-preview==0.8.0
# via wagtail-grapple # via wagtail-grapple
wagtail-localize==1.5.1 wagtail-localize==1.9
# via -r requirements.in # via -r requirements.in
<<<<<<< HEAD
watchfiles==0.19.0 watchfiles==0.19.0
# via uvicorn # via uvicorn
wcwidth==0.2.6 wcwidth==0.2.6
=======
watchfiles==0.22.0
# via
# django-watchfiles
# uvicorn
wcwidth==0.2.13
>>>>>>> 9e6b9a1e (wip: Add KC-client and basic methods, signal handler)
# via prompt-toolkit # via prompt-toolkit
webencodings==0.5.1 webencodings==0.5.1
# via html5lib # via html5lib
websockets==11.0.3 websockets==12.0
# via uvicorn # via uvicorn
wheel==0.41.1 wheel==0.43.0
# via pip-tools # via pip-tools
whitenoise[brotli]==6.5.0 whitenoise[brotli]==6.6.0
# via -r requirements.in # via -r requirements.in
<<<<<<< HEAD
willow[heif]==1.6.1 willow[heif]==1.6.1
# via wagtail # via wagtail
wrapt==1.15.0 wrapt==1.15.0
# via astroid # via astroid
=======
willow[heif]==1.6.3
# via
# wagtail
# willow
>>>>>>> 9e6b9a1e (wip: Add KC-client and basic methods, signal handler)
# The following packages are considered to be unsafe in a requirements file: # The following packages are considered to be unsafe in a requirements file:
# pip # pip

View File

@ -52,3 +52,5 @@ azure-identity
boto3 boto3
openpyxl openpyxl
newrelic newrelic
python-keycloak

View File

@ -2,79 +2,90 @@
# This file is autogenerated by pip-compile with Python 3.10 # This file is autogenerated by pip-compile with Python 3.10
# by the following command: # by the following command:
# #
# pip-compile --output-file=requirements.txt requirements.in # pip-compile requirements.in
# #
aniso8601==9.0.1 aniso8601==9.0.1
# via graphene # via graphene
anyascii==0.3.2 anyascii==0.3.2
# via wagtail # via wagtail
anyio==3.7.1 anyio==4.4.0
# via watchfiles # via
argon2-cffi==21.3.0 # httpx
# watchfiles
argon2-cffi==23.1.0
# via -r requirements.in # via -r requirements.in
argon2-cffi-bindings==21.2.0 argon2-cffi-bindings==21.2.0
# via argon2-cffi # via argon2-cffi
asgiref==3.7.2 asgiref==3.8.1
# via django # via
async-timeout==4.0.2 # django
# django-cors-headers
async-property==0.2.2
# via python-keycloak
async-timeout==4.0.3
# via redis # via redis
attrs==23.1.0 attrs==23.2.0
# via # via
# jsonschema # jsonschema
# referencing # referencing
authlib==1.2.1 authlib==1.3.1
# via -r requirements.in # via -r requirements.in
azure-core==1.29.1 azure-core==1.30.2
# via # via
# azure-identity # azure-identity
# azure-storage-blob # azure-storage-blob
azure-identity==1.14.0 azure-identity==1.17.0
# via -r requirements.in # via -r requirements.in
azure-storage-blob==12.17.0 azure-storage-blob==12.20.0
# via -r requirements.in # via -r requirements.in
bcrypt==4.0.1 bcrypt==4.1.3
# via paramiko # via paramiko
beautifulsoup4==4.11.2 beautifulsoup4==4.11.2
# via wagtail # via wagtail
boto3==1.28.23 boto3==1.34.129
# via -r requirements.in # via -r requirements.in
botocore==1.31.23 botocore==1.34.129
# via # via
# boto3 # boto3
# s3transfer # s3transfer
brotli==1.0.9 brotli==1.1.0
# via whitenoise # via whitenoise
certifi==2023.7.22 certifi==2024.6.2
# via # via
# httpcore
# httpx
# requests # requests
# sentry-sdk # sentry-sdk
cffi==1.15.1 cffi==1.16.0
# via # via
# argon2-cffi-bindings # argon2-cffi-bindings
# cryptography # cryptography
# pynacl # pynacl
charset-normalizer==3.2.0 charset-normalizer==3.3.2
# via requests # via requests
click==8.1.6 click==8.1.7
# via # via
# -r requirements.in # -r requirements.in
# django-click # django-click
# uvicorn # uvicorn
concurrent-log-handler==0.9.24 concurrent-log-handler==0.9.25
# via -r requirements.in # via -r requirements.in
cryptography==41.0.3 cryptography==42.0.8
# via # via
# authlib # authlib
# azure-identity # azure-identity
# azure-storage-blob # azure-storage-blob
# jwcrypto
# msal # msal
# paramiko # paramiko
# pyjwt # pyjwt
defusedxml==0.7.1 defusedxml==0.7.1
# via willow # via willow
dj-database-url==2.0.0 deprecation==2.1.0
# via python-keycloak
dj-database-url==2.2.0
# via -r requirements.in # via -r requirements.in
django==3.2.20 django==3.2.25
# via # via
# -r requirements.in # -r requirements.in
# dj-database-url # dj-database-url
@ -97,66 +108,66 @@ django==3.2.20
# jsonfield # jsonfield
# wagtail # wagtail
# wagtail-localize # wagtail-localize
django-click==2.3.0 django-click==2.4.0
# via -r requirements.in # via -r requirements.in
django-constance==3.1.0 django-constance==3.1.0
# via -r requirements.in # via -r requirements.in
django-cors-headers==4.2.0 django-cors-headers==4.3.1
# via -r requirements.in # via -r requirements.in
django-csp==3.7 django-csp==3.8
# via -r requirements.in # via -r requirements.in
django-filter==23.2 django-filter==23.5
# via wagtail # via wagtail
django-ipware==5.0.0 django-ipware==7.0.1
# via -r requirements.in # via -r requirements.in
django-jsonform==2.18.0 django-jsonform==2.22.0
# via -r requirements.in # via -r requirements.in
django-model-utils==4.3.1 django-model-utils==4.5.1
# via # via
# -r requirements.in # -r requirements.in
# django-notifications-hq # django-notifications-hq
django-modelcluster==6.0 django-modelcluster==6.3
# via wagtail # via wagtail
django-notifications-hq==1.8.2 django-notifications-hq==1.8.3
# via -r requirements.in # via -r requirements.in
django-permissionedforms==0.1 django-permissionedforms==0.1
# via wagtail # via wagtail
django-picklefield==3.1 django-picklefield==3.2
# via django-constance # via django-constance
django-ratelimit==4.1.0 django-ratelimit==4.1.0
# via -r requirements.in # via -r requirements.in
django-redis==5.3.0 django-redis==5.4.0
# via -r requirements.in # via -r requirements.in
django-storages==1.13.2 django-storages==1.14.3
# via -r requirements.in # via -r requirements.in
django-taggit==4.0.0 django-taggit==4.0.0
# via wagtail # via wagtail
django-treebeard==4.7 django-treebeard==4.7.1
# via wagtail # via wagtail
djangorestframework==3.14.0 djangorestframework==3.15.1
# via # via
# -r requirements.in # -r requirements.in
# drf-spectacular # drf-spectacular
# wagtail # wagtail
draftjs-exporter==2.1.7 draftjs-exporter==2.1.7
# via wagtail # via wagtail
drf-spectacular==0.26.4 drf-spectacular==0.27.2
# via -r requirements.in # via -r requirements.in
environs==9.5.0 environs==11.0.0
# via -r requirements.in # via -r requirements.in
et-xmlfile==1.1.0 et-xmlfile==1.1.0
# via openpyxl # via openpyxl
exceptiongroup==1.1.2 exceptiongroup==1.2.1
# via anyio # via anyio
factory-boy==3.3.0 factory-boy==3.3.0
# via wagtail-factories # via wagtail-factories
faker==19.3.0 faker==25.8.0
# via factory-boy # via factory-boy
filetype==1.2.0 filetype==1.2.0
# via willow # via willow
graphene==3.3 graphene==3.3
# via graphene-django # via graphene-django
graphene-django==3.1.5 graphene-django==3.2.2
# via wagtail-grapple # via wagtail-grapple
graphql-core==3.2.3 graphql-core==3.2.3
# via # via
@ -167,17 +178,24 @@ graphql-relay==3.2.0
# via # via
# graphene # graphene
# graphene-django # graphene-django
gunicorn==21.2.0 gunicorn==22.0.0
# via -r requirements.in # via -r requirements.in
h11==0.14.0 h11==0.14.0
# via uvicorn # via
# httpcore
# uvicorn
html5lib==1.1 html5lib==1.1
# via wagtail # via wagtail
httptools==0.6.0 httpcore==1.0.5
# via httpx
httptools==0.6.1
# via uvicorn # via uvicorn
idna==3.4 httpx==0.27.0
# via python-keycloak
idna==3.7
# via # via
# anyio # anyio
# httpx
# requests # requests
inflection==0.5.1 inflection==0.5.1
# via drf-spectacular # via drf-spectacular
@ -189,106 +207,119 @@ jmespath==1.0.1
# botocore # botocore
jsonfield==3.1.0 jsonfield==3.1.0
# via django-notifications-hq # via django-notifications-hq
jsonschema==4.19.0 jsonschema==4.22.0
# via drf-spectacular # via drf-spectacular
jsonschema-specifications==2023.7.1 jsonschema-specifications==2023.12.1
# via jsonschema # via jsonschema
jwcrypto==1.5.6
# via python-keycloak
l18n==2021.3 l18n==2021.3
# via wagtail # via wagtail
marshmallow==3.20.1 marshmallow==3.21.3
# via environs # via environs
msal==1.23.0 msal==1.28.1
# via # via
# azure-identity # azure-identity
# msal-extensions # msal-extensions
msal-extensions==1.0.0 msal-extensions==1.1.0
# via azure-identity # via azure-identity
newrelic==8.11.0 newrelic==9.11.0
# via -r requirements.in # via -r requirements.in
openpyxl==3.1.2 openpyxl==3.1.4
# via # via
# -r requirements.in # -r requirements.in
# wagtail # wagtail
packaging==23.1 packaging==24.1
# via # via
# deprecation
# gunicorn # gunicorn
# marshmallow # marshmallow
paramiko==3.3.1 # msal-extensions
paramiko==3.4.0
# via -r requirements.in # via -r requirements.in
pillow==10.0.0 pillow==10.3.0
# via # via
# -r requirements.in # -r requirements.in
# pillow-heif # pillow-heif
# wagtail # wagtail
pillow-heif==0.13.0 pillow-heif==0.16.0
# via willow # via willow
polib==1.2.0 polib==1.2.0
# via wagtail-localize # via wagtail-localize
portalocker==2.7.0 portalocker==2.8.2
# via # via
# concurrent-log-handler # concurrent-log-handler
# msal-extensions # msal-extensions
promise==2.3 promise==2.3
# via graphene-django # via graphene-django
psycopg2-binary==2.9.7 psycopg2-binary==2.9.9
# via -r requirements.in # via -r requirements.in
pycparser==2.21 pycparser==2.22
# via cffi # via cffi
pycryptodome==3.18.0 pycryptodome==3.20.0
# via -r requirements.in # via -r requirements.in
pyjwt[crypto]==2.8.0 pyjwt[crypto]==2.8.0
# via msal # via
# msal
# pyjwt
pynacl==1.5.0 pynacl==1.5.0
# via paramiko # via paramiko
python-dateutil==2.8.2 python-dateutil==2.9.0.post0
# via # via
# -r requirements.in # -r requirements.in
# botocore # botocore
# faker # faker
python-dotenv==1.0.0 python-dotenv==1.0.1
# via # via
# environs # environs
# uvicorn # uvicorn
python-http-client==3.3.7 python-http-client==3.3.7
# via sendgrid # via sendgrid
python-ipware==3.0.0
# via django-ipware
python-json-logger==2.0.7 python-json-logger==2.0.7
# via -r requirements.in # via -r requirements.in
python-slugify==8.0.1 python-keycloak==4.1.0
# via -r requirements.in # via -r requirements.in
pytz==2023.3 python-slugify==8.0.4
# via -r requirements.in
pytz==2024.1
# via # via
# -r requirements.in # -r requirements.in
# django # django
# django-modelcluster # django-modelcluster
# django-notifications-hq # django-notifications-hq
# djangorestframework
# l18n # l18n
pyyaml==6.0.1 pyyaml==6.0.1
# via # via
# drf-spectacular # drf-spectacular
# uvicorn # uvicorn
redis==4.6.0 redis==5.0.6
# via # via
# -r requirements.in # -r requirements.in
# django-redis # django-redis
referencing==0.30.2 referencing==0.35.1
# via # via
# jsonschema # jsonschema
# jsonschema-specifications # jsonschema-specifications
requests==2.31.0 requests==2.32.3
# via # via
# azure-core # azure-core
# msal # msal
# python-keycloak
# requests-toolbelt
# wagtail # wagtail
rpds-py==0.9.2 requests-toolbelt==1.0.0
# via python-keycloak
rpds-py==0.18.1
# via # via
# jsonschema # jsonschema
# referencing # referencing
s3transfer==0.6.1 s3transfer==0.10.1
# via boto3 # via boto3
sendgrid==6.10.0 sendgrid==6.11.0
# via -r requirements.in # via -r requirements.in
sentry-sdk==1.29.2 sentry-sdk==2.5.1
# via -r requirements.in # via -r requirements.in
six==1.16.0 six==1.16.0
# via # via
@ -298,15 +329,17 @@ six==1.16.0
# l18n # l18n
# promise # promise
# python-dateutil # python-dateutil
sniffio==1.3.0 sniffio==1.3.1
# via anyio # via
soupsieve==2.4.1 # anyio
# httpx
soupsieve==2.5
# via beautifulsoup4 # via beautifulsoup4
sqlparse==0.4.4 sqlparse==0.5.0
# via django # via django
starkbank-ecdsa==2.2.0 starkbank-ecdsa==2.2.0
# via sendgrid # via sendgrid
structlog==23.1.0 structlog==24.2.0
# via -r requirements.in # via -r requirements.in
swapper==1.3.0 swapper==1.3.0
# via django-notifications-hq # via django-notifications-hq
@ -316,47 +349,52 @@ text-unidecode==1.3
# via # via
# graphene-django # graphene-django
# python-slugify # python-slugify
typing-extensions==4.7.1 typing-extensions==4.12.2
# via # via
# anyio
# asgiref # asgiref
# azure-core # azure-core
# azure-identity
# azure-storage-blob # azure-storage-blob
# dj-database-url # dj-database-url
# jwcrypto
# uvicorn # uvicorn
# wagtail-localize # wagtail-localize
uritemplate==4.1.1 uritemplate==4.1.1
# via drf-spectacular # via drf-spectacular
urllib3==1.26.16 urllib3==2.2.2
# via # via
# botocore # botocore
# requests # requests
# sentry-sdk # sentry-sdk
uvicorn[standard]==0.23.2 uvicorn[standard]==0.30.1
# via -r requirements.in # via -r requirements.in
uvloop==0.17.0 uvloop==0.19.0
# via uvicorn # via uvicorn
wagtail==5.1 wagtail==5.2.5
# via # via
# -r requirements.in # -r requirements.in
# wagtail-factories # wagtail-factories
# wagtail-grapple # wagtail-grapple
# wagtail-headless-preview # wagtail-headless-preview
# wagtail-localize # wagtail-localize
wagtail-factories==4.1.0 wagtail-factories==4.2.1
# via -r requirements.in # via -r requirements.in
wagtail-grapple==0.20.0 wagtail-grapple==0.25.1
# via -r requirements.in # via -r requirements.in
wagtail-headless-preview==0.6.0 wagtail-headless-preview==0.8.0
# via wagtail-grapple # via wagtail-grapple
wagtail-localize==1.5.1 wagtail-localize==1.9
# via -r requirements.in # via -r requirements.in
watchfiles==0.19.0 watchfiles==0.22.0
# via uvicorn # via uvicorn
webencodings==0.5.1 webencodings==0.5.1
# via html5lib # via html5lib
websockets==11.0.3 websockets==12.0
# via uvicorn # via uvicorn
whitenoise[brotli]==6.5.0 whitenoise[brotli]==6.6.0
# via -r requirements.in # via -r requirements.in
willow[heif]==1.6.1 willow[heif]==1.6.3
# via wagtail # via
# wagtail
# willow

View File

@ -13,6 +13,11 @@ from vbv_lernwelt.core.model_utils import find_available_slug
from vbv_lernwelt.core.models import User from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.serializer_helpers import get_course_serializer_class from vbv_lernwelt.course.serializer_helpers import get_course_serializer_class
from vbv_lernwelt.files.models import UploadFile from vbv_lernwelt.files.models import UploadFile
from vbv_lernwelt.sso.role_sync.client import (
add_roles_to_user,
remove_roles_from_user,
update_roles_for_user,
)
class CircleContactType(Enum): class CircleContactType(Enum):
@ -293,6 +298,26 @@ class CourseSessionUser(models.Model):
def __str__(self): def __str__(self):
return f"{self.user} ({self.course_session.title})" return f"{self.user} ({self.course_session.title})"
def save(self, *args, **kwargs):
if self.created_at is None:
add_roles_to_user(
self.user, [(self.course_session.course.slug, [self.role])]
)
else:
old_csu = CourseSessionUser.objects.get(pk=self.pk)
update_roles_for_user(
self.user,
add_course_roles=[(self.course_session.course.slug, [self.role])],
remove_course_roles=[(self.course_session.course.slug, [old_csu.role])],
)
super().save(*args, **kwargs)
@classmethod
def remove_sso_roles_from_user(cls, instance: "CourseSessionUser"):
remove_roles_from_user(
instance.user, [(instance.course_session.course.slug, [instance.role])]
)
class CircleDocument(models.Model): class CircleDocument(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

View File

@ -1,10 +1,15 @@
from django.db.models.signals import post_save from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver from django.dispatch import receiver
from vbv_lernwelt.course.models import Course, CourseConfiguration from vbv_lernwelt.course.models import Course, CourseConfiguration, CourseSessionUser
@receiver(post_save, sender=Course) @receiver(post_save, sender=Course)
def create_course_configuration(sender, instance, created, **kwargs): def create_course_configuration(sender, instance, created, **kwargs):
if created: if created:
CourseConfiguration.objects.create(course=instance) CourseConfiguration.objects.create(course=instance)
@receiver(post_delete, sender=CourseSessionUser)
def after_delete(sender, instance, **kwargs):
CourseSessionUser.remove_sso_roles_from_user(instance)

View File

@ -4,5 +4,4 @@ from vbv_lernwelt.course_session_group.models import CourseSessionGroup
@admin.register(CourseSessionGroup) @admin.register(CourseSessionGroup)
class CourseSessionAssignmentAdmin(admin.ModelAdmin): class CourseSessionAssignmentAdmin(admin.ModelAdmin): ...
...

View File

@ -153,9 +153,9 @@ def fetch_course_session_all_users(courses: List[int], excluded_domains=None):
def generate_export_response(cs_users: List[User]) -> HttpResponse: def generate_export_response(cs_users: List[User]) -> HttpResponse:
response = HttpResponse(content_type="text/csv; charset=utf-8") response = HttpResponse(content_type="text/csv; charset=utf-8")
response[ response["Content-Disposition"] = (
"Content-Disposition" f"attachment; filename=edoniq_user_export_{date.today().strftime('%Y%m%d')}.csv"
] = f"attachment; filename=edoniq_user_export_{date.today().strftime('%Y%m%d')}.csv" )
response.write("\ufeff".encode("utf8")) # UTF-8 BOM response.write("\ufeff".encode("utf8")) # UTF-8 BOM

View File

@ -125,9 +125,9 @@ def _handle_feedback_export_action(course_seesions, file_name):
response = HttpResponse( response = HttpResponse(
content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
) )
response[ response["Content-Disposition"] = (
"Content-Disposition" f"attachment; filename={make_export_filename(file_name)}"
] = f"attachment; filename={make_export_filename(file_name)}" )
response.write(excel_bytes) response.write(excel_bytes)
return response return response

View File

@ -493,6 +493,7 @@ def create_or_update_user(
sso_id: str = None, sso_id: str = None,
contract_number: str = "", contract_number: str = "",
date_of_birth: str = "", date_of_birth: str = "",
intermediate_sso_id: str = "", # from keycloak
) -> User: ) -> User:
logger.debug( logger.debug(
"create_or_update_user", "create_or_update_user",
@ -537,6 +538,9 @@ def create_or_update_user(
user.first_name = first_name or user.first_name user.first_name = first_name or user.first_name
user.last_name = last_name or user.last_name user.last_name = last_name or user.last_name
user.username = email user.username = email
user.additional_json_data = user.additional_json_data | {
"intermediate_sso_id": intermediate_sso_id
}
user.set_unusable_password() user.set_unusable_password()
user.save() user.save()

View File

@ -65,9 +65,9 @@ class TestNotificationService(TestCase):
self.assertFalse(notification.emailed) self.assertFalse(notification.emailed)
def test_send_notification_with_email(self): def test_send_notification_with_email(self):
self.recipient.additional_json_data[ self.recipient.additional_json_data["email_notification_categories"] = (
"email_notification_categories" json.dumps(["USER_INTERACTION"])
] = json.dumps(["USER_INTERACTION"]) )
self.recipient.save() self.recipient.save()
verb = "Anne hat deinen Auftrag bewertet" verb = "Anne hat deinen Auftrag bewertet"
@ -146,9 +146,9 @@ class TestNotificationService(TestCase):
self.assertFalse(notification.emailed) self.assertFalse(notification.emailed)
# when the email was not sent, yet it will still send it afterwards... # when the email was not sent, yet it will still send it afterwards...
self.recipient.additional_json_data[ self.recipient.additional_json_data["email_notification_categories"] = (
"email_notification_categories" json.dumps(["USER_INTERACTION"])
] = json.dumps(["USER_INTERACTION"]) )
self.recipient.save() self.recipient.save()
result = self.notification_service._send_notification( result = self.notification_service._send_notification(
@ -188,9 +188,9 @@ class TestNotificationService(TestCase):
self.assertFalse(self._has_sent_emails()) self.assertFalse(self._has_sent_emails())
# Assert mail is sent if corresponding email notification type is enabled # Assert mail is sent if corresponding email notification type is enabled
self.recipient.additional_json_data[ self.recipient.additional_json_data["email_notification_categories"] = (
"email_notification_categories" json.dumps(["USER_INTERACTION"])
] = json.dumps(["USER_INTERACTION"]) )
self.recipient.save() self.recipient.save()
self.notification_service._send_notification( self.notification_service._send_notification(
sender=self.sender, sender=self.sender,

View File

@ -39,9 +39,9 @@ class SelfEvaluationFeedbackSerializer(serializers.ModelSerializer):
return obj.learning_unit.get_circle().title return obj.learning_unit.get_circle().title
def get_criteria(self, obj): def get_criteria(self, obj):
performance_criteria: List[ performance_criteria: List[PerformanceCriteria] = (
PerformanceCriteria obj.learning_unit.performancecriteria_set.all()
] = obj.learning_unit.performancecriteria_set.all() )
criteria = [] criteria = []

View File

@ -0,0 +1,92 @@
import unicodedata
from typing import Dict, List, Tuple
from django.conf import settings
from keycloak import KeycloakAdmin, KeycloakOpenIDConnection
from vbv_lernwelt.core.models import User
from vbv_lernwelt.sso.role_sync.roles import ROLE_IDS, SSO_ROLES
CourseRolesType = List[Tuple[str, List[str]]]
if settings.OAUTH_SYNC_ROLES:
keycloak_connection = KeycloakOpenIDConnection(
server_url=settings.OAUTH_SIGNIN_URL,
realm_name=settings.OAUTH_SIGNIN_REALM,
user_realm_name=settings.OAUTH_SIGNIN_REALM,
client_id=settings.OAUTH_SIGNIN_ADMIN_CLIENT_ID,
client_secret_key=settings.OAUTH_SIGNIN_ADMIN_CLIENT_SECRET,
verify=True,
)
keycloak_admin = KeycloakAdmin(connection=keycloak_connection)
# todo: handle errors
def add_roles_to_user(user: User, course_roles: CourseRolesType):
user_id = user.additional_json_data.get("intermediate_sso_id", "")
if settings.OAUTH_SYNC_ROLES and user_id:
request_roles = _get_role_request_data(course_roles)
keycloak_admin.assign_realm_roles(
user_id=user_id,
roles=request_roles,
)
return True
return False
def remove_roles_from_user(user: User, course_roles: CourseRolesType):
user_id = user.additional_json_data.get("intermediate_sso_id", "")
if settings.OAUTH_SYNC_ROLES and user_id:
request_roles = _get_role_request_data(course_roles)
keycloak_admin.delete_realm_roles_of_user(
user_id=user_id,
roles=request_roles,
)
return True
return False
def update_roles_for_user(
user: User, add_course_roles: CourseRolesType, remove_course_roles: CourseRolesType
):
if settings.OAUTH_SYNC_ROLES:
add_roles_to_user(user, add_course_roles)
remove_roles_from_user(user, remove_course_roles)
return True
return False
def get_roles_for_user(user_id: str):
if settings.OAUTH_SYNC_ROLES:
return keycloak_admin.get_realm_roles_of_user(
user_id=user_id,
)
return []
# create sso-ID user and set roles
# sync
def _get_role_request_data(course_roles: CourseRolesType) -> List[Dict[str, str]]:
request_roles = []
for item in course_roles:
course_slug, roles = item
sanitized_course_slug = _remove_accents(course_slug)
oauth_roles = _create_role_names(sanitized_course_slug, roles)
return request_roles + [
{"id": ROLE_IDS[role], "name": role} for role in oauth_roles
]
return request_roles
def _create_role_names(course_slug: str, roles: list) -> List[str]:
return [SSO_ROLES[course_slug][role] for role in roles]
def _remove_accents(input_str) -> str:
nfkd_form = unicodedata.normalize("NFKD", input_str)
return "".join([char for char in nfkd_form if not unicodedata.combining(char)])

View File

@ -0,0 +1,54 @@
SSO_ROLES = {
"uberbetriebliche-kurse": {
"MEMBER": "myvbv-uberbetriebliche-kurse-member",
"EXPERT": "myvbv-uberbetriebliche-kurse-expert",
"SUPERVISOR": "myvbv-uberbetriebliche-kurse-supervisor",
"LEARNING_MENTOR": "myvbv-uberbetriebliche-kurse-mentor",
},
"cours-interentreprises": {
"MEMBER": "myvbv-cours-interentreprises-member",
"EXPERT": "myvbv-cours-interentreprises-expert",
"SUPERVISOR": "myvbv-cours-interentreprises-supervisor",
"LEARNING_MENTOR": "myvbv-cours-interentreprises-mentor",
},
"corso-interaziendale": {
"MEMBER": "myvbv-corso-interaziendale-member",
"EXPERT": "myvbv-corso-interaziendale-expert",
"SUPERVISOR": "myvbv-corso-interaziendale-supervisor",
"LEARNING_MENTOR": "myvbv-corso-interaziendale-mentor",
},
"versicherungsvermittler-in": {
"MEMBER": "myvbv-versicherungsvermittler-in-member",
"LEARNING_MENTOR": "myvbv-versicherungsvermittler-in-mentor",
},
"intermediaire-dassurance": {
"MEMBER": "myvbv-intermediaire-dassurance-member",
"LEARNING_MENTOR": "myvbv-intermediaire-dassurance-mentor",
},
"intermediarioa-assicurativoa": {
"MEMBER": "myvbv-intermediarioa-assicurativoa-member",
"LEARNING_MENTOR": "myvbv-intermediarioa-assicurativoa-mentor",
},
}
# https://sso.test.b.lernetz.host/auth/admin/vbv/console/#/vbv/roles
ROLE_IDS = {
"myvbv-uberbetriebliche-kurse-member": "0725f2d4-c3f3-48b7-83ec-06acfae630e6",
"myvbv-uberbetriebliche-kurse-expert": "c7e33cb6-d227-4764-9b8e-d42af79fb46d",
"myvbv-uberbetriebliche-kurse-supervisor": "d88a7486-7ff4-475c-b840-2e1e0a9decb8",
"myvbv-uberbetriebliche-kurse-mentor": "db5f0e24-9512-4752-8c51-26b3aa6f7f6a",
"myvbv-cours-interentreprises-member": "458c65f4-e969-4ba7-a546-77948641bc0b",
"myvbv-cours-interentreprises-expert": "2ef51fc6-1e5a-427c-b4a9-314249ea24db",
"myvbv-cours-interentreprises-supervisor": "23e5994e-c499-42e8-b956-bb098be793e1",
"myvbv-cours-interentreprises-mentor": "cb37a093-32a3-479b-981e-0604b6b71f5e",
"myvbv-corso-interaziendale-member": "4d9cfc61-b555-44b1-a52d-76231d12f0cd",
"myvbv-corso-interaziendale-expert": "b2da77bd-c3c8-4d1e-9757-c016eaf219e3",
"myvbv-corso-interaziendale-supervisor": "8e9ea3e4-e814-4704-906e-d39f595811eb",
"myvbv-corso-interaziendale-mentor": "36fae39d-67f0-4ed6-9a1c-7d383be9e463",
"myvbv-versicherungsvermittler-in-member": "3ab4eab2-7d7c-43bb-a927-4cf54f24ccc2",
"myvbv-versicherungsvermittler-in-mentor": "12bf374a-293b-4abe-b255-7899eae31908",
"myvbv-intermediaire-dassurance-member": "5400fdae-2c37-4738-8667-0bcb50ed3609",
"myvbv-intermediaire-dassurance-mentor": "3bd737f9-731a-4548-aaf5-4c80175f2759",
"myvbv-intermediarioa-assicurativoa-member": "9fbaaa0f-cf8c-45f2-93f6-7174cb18a982",
"myvbv-intermediarioa-assicurativoa-mentor": "46b12e54-682e-44c0-b506-eab820138b66",
}

View File

@ -116,6 +116,7 @@ def authorize_signin(request):
sso_id=id_token.get("oid"), sso_id=id_token.get("oid"),
first_name=id_token.get("given_name", ""), first_name=id_token.get("given_name", ""),
last_name=id_token.get("family_name", ""), last_name=id_token.get("family_name", ""),
intermediate_sso_id=id_token.get("sub"),
) )
dj_login(request, user) dj_login(request, user)