Skip to content

Commit

Permalink
Merge pull request #239 from CMSTrackerDPG/oidc_sso
Browse files Browse the repository at this point in the history
  • Loading branch information
nothingface0 authored Jul 15, 2023
2 parents 91b5665 + 078bca4 commit 908c711
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 46 deletions.
14 changes: 3 additions & 11 deletions .github/workflows/django.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,8 @@ jobs:
git config --global url."https://${{ secrets.CERN_GITLAB_USER }}:${{ secrets.CERN_GITLAB_TOKEN }}@gitlab.cern.ch".insteadOf https://gitlab.cern.ch
python -m pip install --upgrade pip
ALLAUTH_INSTALL_DIR=$HOME
mkdir -p $ALLAUTH_INSTALL_DIR
git clone https://github.com/nothingface0/django-allauth.git $ALLAUTH_INSTALL_DIR/django-allauth
cd $ALLAUTH_INSTALL_DIR/django-allauth
git checkout 77368a8
wget https://github.com/pennersr/django-allauth/compare/main...nothingface0:django-allauth:fix_cern_sso.patch --output-document=patch
git apply patch --reject || true
cd -
pip install -r requirements.txt
pip install $ALLAUTH_INSTALL_DIR/django-allauth
pip install --index-url https://test.pypi.org/simple runregistry==1.0.0
pip install --upgrade pytest pytest-django pytest-cov codecov mixer selenium
Expand All @@ -69,7 +60,8 @@ jobs:
DJANGO_SECRET_KEY: ${{ secrets.DJANGO_SECRET_KEY }}
OMS_CLIENT_ID: ${{ secrets.OMS_CLIENT_ID }}
OMS_CLIENT_SECRET: ${{ secrets.OMS_CLIENT_SECRET }}

CERN_SSO_REGISTRATION_CLIENT_ID: ${{ secrets.CERN_SSO_REGISTRATION_CLIENT_ID }}
CERN_SSO_REGISTRATION_CLIENT_SECRET: ${{ secrets.CERN_SSO_REGISTRATION_CLIENT_SECRET }}
run: |
PYTHONWARNINGS=all pytest --ds=dqmhelper.test_ci_settings --cov=./ --ignore addrefrun/tests/test_addrefrun_views.py --ignore certifier/tests/test_certifier_views.py --ignore oms/tests/test_oms_utils.py --ignore oms/tests/test_oms_models.py
codecov
Expand Down
14 changes: 0 additions & 14 deletions .s2i/bin/assemble
Original file line number Diff line number Diff line change
@@ -1,23 +1,9 @@
#!/bin/bash
echo "Before assembling"
git config --global url."https://$CERN_GITLAB_USER:$CERN_GITLAB_TOKEN@gitlab.cern.ch".insteadOf https://gitlab.cern.ch

# TODO: remove once PR in django-allauth merged
ALLAUTH_INSTALL_DIR=$HOME
mkdir -p $ALLAUTH_INSTALL_DIR
git clone https://github.com/nothingface0/django-allauth.git $ALLAUTH_INSTALL_DIR/django-allauth
cd $ALLAUTH_INSTALL_DIR/django-allauth
git checkout 77368a8
wget https://github.com/pennersr/django-allauth/compare/main...nothingface0:django-allauth:fix_cern_sso.patch --output-document=patch
git apply patch --reject || true
cd -


/usr/libexec/s2i/assemble
rc=$?

pip install $ALLAUTH_INSTALL_DIR/django-allauth

# Temporarily install runregistry api client from test PyPI
pip install --index-url https://test.pypi.org/simple runregistry==1.0.0

Expand Down
35 changes: 32 additions & 3 deletions dqmhelper/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
https://docs.djangoproject.com/en/2.1/ref/settings/
"""

import os
from pathlib import Path

from decouple import config
from django.contrib.messages import constants as messages

Expand Down Expand Up @@ -76,7 +74,7 @@
"allauth",
"allauth.account",
"allauth.socialaccount",
"allauth.socialaccount.providers.cern",
"allauth.socialaccount.providers.openid_connect",
"widget_tweaks",
"django_extensions",
"django_tables2",
Expand Down Expand Up @@ -227,3 +225,34 @@
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
ACCOUNT_EMAIL_VERIFICATION = "none"
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")


SOCIALACCOUNT_PROVIDERS = {
"openid_connect": {
"SERVERS": [
{
"id": "cern", # 30 characters or less
"name": "CERN",
"server_url": "https://auth.cern.ch/auth/realms/cern",
# Optional token endpoint authentication method.
# May be one of "client_secret_basic", "client_secret_post"
# If omitted, a method from the the server's
# token auth methods list is used
"token_auth_method": "client_secret_post",
"APP": {
"client_id": config("CERN_SSO_REGISTRATION_CLIENT_ID"),
"secret": config("CERN_SSO_REGISTRATION_CLIENT_SECRET"),
},
},
]
}
}

# This is used to get the public key and decode access tokens
# for users when they login. The URL can be found under the
# jwks_uri key of the JSON pointed to by the server_url of
# CERN's well-known config URL:
# https://auth.cern.ch/auth/realms/cern/.well-known/openid-configuration
CERN_SSO_JWKS_URI = (
"https://auth.cern.ch/auth/realms/cern/protocol/openid-connect/certs"
)
1 change: 1 addition & 0 deletions dqmhelper/test_settings.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from .settings import *

if os.environ.get("GITHUB_WORKFLOW"):
Expand Down
5 changes: 2 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ certifi<2023.0.0
channels[daphne]
channels_redis
Django<4.2.0
# TODO: Update this once PR is merged to django-allauth
#-e /home/runner/django-allauth
#django-allauth<1.0.0
django-allauth<1.0.0
django-bootstrap3<24.0
django-filter<24.0
django-ckeditor<7.0.0
Expand All @@ -29,5 +27,6 @@ apscheduler
whitenoise
factory_boy
prettytable
pyjwt
git+https://gitlab.cern.ch/cmsoms/oms-api-client
git+https://github.com/eea/odfpy.git
56 changes: 43 additions & 13 deletions users/signals.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import allauth
import jwt
import django
import allauth
from allauth.account.signals import user_logged_in
from allauth.socialaccount.models import SocialAccount
from allauth.socialaccount.signals import social_account_updated, \
social_account_added, social_account_removed, pre_social_login
from allauth.socialaccount.signals import (
social_account_updated,
social_account_added,
social_account_removed,
pre_social_login,
)
from django.contrib.auth import get_user_model
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver

from users.utilities.logger import get_configured_logger
from users.utilities.utilities import update_user_extradata
from decouple import config
from django.conf import settings

logger = get_configured_logger(loggername=__name__, filename="signals.log")


@receiver(django.contrib.auth.signals.user_logged_in)
def update_users_on_login(sender, user, request, **kwargs):
update_user_extradata(user)
user.save()


@receiver(pre_save, sender=get_user_model())
Expand All @@ -40,37 +47,59 @@ def log_user_logged_out(sender, user, request, **kwargs):
logger.info("User {} has logged out".format(user))


@receiver(pre_social_login)
def log_pre_social_login(request, sociallogin, **kwargs):
def log_pre_social_login(request, sociallogin):
try:
logger.debug("Pre social login for User {}".format(sociallogin.user))
except (get_user_model().DoesNotExist, AttributeError):
logger.debug("Pre social login for non-existing User")


@receiver(pre_social_login)
def pre_social_login(request, sociallogin, **kwargs):
# Get the token from the login and decode it,
# so that we can get cern_roles out of it
key = jwt.PyJWKClient(settings.CERN_SSO_JWKS_URI).get_signing_keys()[0]
sociallogin.account.extra_data = jwt.decode(
jwt=sociallogin.token.token.encode("utf-8"),
key=key.key,
algorithms=["RS256"],
audience=config("CERN_SSO_REGISTRATION_CLIENT_ID"),
)
log_pre_social_login(request, sociallogin=sociallogin)


@receiver(social_account_added)
def log_social_account_added(request, sociallogin, **kwargs):
try:
logger.info("Social Account {} has been added for User {}"
.format(sociallogin.account, sociallogin.user))
logger.info(
"Social Account {} has been added for User {}".format(
sociallogin.account, sociallogin.user
)
)
except (SocialAccount.DoesNotExist, get_user_model().DoesNotExist, AttributeError):
logger.debug("Pre social login for non-existing User")


@receiver(social_account_updated)
def log_social_account_updated(request, sociallogin, **kwargs):
try:
logger.debug("Social Account {} has been updated for User {}"
.format(sociallogin.account, sociallogin.user))
logger.debug(
"Social Account {} has been updated for User {}".format(
sociallogin.account, sociallogin.user
)
)
except (SocialAccount.DoesNotExist, get_user_model().DoesNotExist, AttributeError):
logger.error("Something unexpected happened")


@receiver(social_account_removed)
def log_social_account_removed(request, socialaccount, **kwargs):
try:
logger.info("Social Account {} has been removed from User {}"
.format(socialaccount, socialaccount.user))
logger.info(
"Social Account {} has been removed from User {}".format(
socialaccount, socialaccount.user
)
)
except (get_user_model().DoesNotExist, AttributeError):
logger.error("Something unexpected happened")

Expand All @@ -79,6 +108,7 @@ def log_social_account_removed(request, socialaccount, **kwargs):
def log_user_has_login_failed(sender, credentials, request, **kwargs):
try:
logger.warning(
"User {} has failed to logged in".format(credentials.get("username")))
"User {} has failed to logged in".format(credentials.get("username"))
)
except AttributeError:
logger.error("Username attribute does not exist!")
10 changes: 8 additions & 2 deletions users/tests/test_users_signals.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import pytest
from django.db import IntegrityError
from mixer.backend.django import mixer
from django.contrib.auth import get_user_model
from users.signals import *
from django.test import Client
from users.signals import *
from mixer.backend.django import mixer

pytestmark = pytest.mark.django_db

Expand All @@ -14,6 +14,12 @@ def test_create_user():
assert user


def test_pre_social_login():
# TODO: Add test for verifying functionality
# of users.signals.pre_social_login, if possible
pass


def test_logs():
"""
Just run them once, nothing really to test here
Expand Down

0 comments on commit 908c711

Please sign in to comment.