diff --git a/.github/workflows/semesterly.yml b/.github/workflows/semesterly.yml index fd5fa9fa4b..b1ce88168b 100644 --- a/.github/workflows/semesterly.yml +++ b/.github/workflows/semesterly.yml @@ -50,6 +50,8 @@ jobs: - name: Install Python Dependencies run: | + sudo apt-get --allow-releaseinfo-change update + sudo apt install libxmlsec1 libxmlsec1-dev -y python -m pip install --upgrade pip pip install -r requirements.txt diff --git a/.gitignore b/.gitignore index ddc3df0207..529bea3afc 100644 --- a/.gitignore +++ b/.gitignore @@ -57,4 +57,6 @@ venv/ webpack-stats.json workfile.html cache -stunnel/ \ No newline at end of file +stunnel/ +saml.crt +saml.key \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index e77fd2f5e4..a52970ba42 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,6 +24,9 @@ COPY ./build/local_settings.py /code/semesterly/local_settings.py # Add parser script COPY ./build/run_parser.sh /code/run_parser.sh +RUN apt-get --allow-releaseinfo-change update +RUN apt install libxmlsec1 libxmlsec1-dev -y + RUN pip3 install -r /code/requirements.txt # Install package.json dependencies diff --git a/authpipe/urls.py b/authpipe/urls.py index ee12f66d02..e65a60b91e 100644 --- a/authpipe/urls.py +++ b/authpipe/urls.py @@ -19,4 +19,5 @@ # auth re_path("", include("social_django.urls", namespace="social")), re_path("", include(("django.contrib.auth.urls", "auth"), namespace="auth")), + re_path(r"^saml/metadata$", authpipe.views.saml_metadata_view), ] diff --git a/authpipe/views.py b/authpipe/views.py index defc0f99ee..6eb0b564a4 100644 --- a/authpipe/views.py +++ b/authpipe/views.py @@ -9,3 +9,17 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. +from django.http import HttpResponse +from django.urls import reverse +from social_django.utils import load_strategy, load_backend + +def saml_metadata_view(request): + complete_url = reverse('social:complete', args=("saml", )) + saml_backend = load_backend( + load_strategy(request), + "saml", + redirect_uri=complete_url, + ) + metadata, errors = saml_backend.generate_metadata_xml() + if not errors: + return HttpResponse(content=metadata, content_type='text/xml') \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index a9d793e744..22cf125567 100644 --- a/requirements.txt +++ b/requirements.txt @@ -41,6 +41,7 @@ python-dateutil==2.8.2 social-auth-core==4.0.3 social-auth-app-django==4.0.0 python-memcached==1.59 +python3-saml==1.16.0 python-social-auth==0.2.21 pytz==2017.2 pywebpush==1.4.0 diff --git a/semesterly/settings.py b/semesterly/settings.py index e6676734e1..cc1a2c95bf 100644 --- a/semesterly/settings.py +++ b/semesterly/settings.py @@ -68,6 +68,38 @@ def get_secret(key): USE_X_FORWARDED_HOST = True +SOCIAL_AUTH_SAML_SP_ENTITY_ID = "http://jhu.semester.ly" + +SOCIAL_AUTH_SAML_SP_PUBLIC_CERT = get_secret("SOCIAL_AUTH_SAML_SP_PUBLIC_CERT") + +SOCIAL_AUTH_SAML_SP_PRIVATE_KEY = get_secret("SOCIAL_AUTH_SAML_SP_PRIVATE_KEY") + +SOCIAL_AUTH_SAML_ORG_INFO = { + "en-US": { + "name": "semesterly", + "displayname": "Semester.ly", + "url": "http://jhu.semester.ly", + } +} + +SOCIAL_AUTH_SAML_TECHNICAL_CONTACT = { + "givenName": "Semester.ly", + "emailAddress": "semester.ly@jh.edu", +} + +SOCIAL_AUTH_SAML_SUPPORT_CONTACT = { + "givenName": "Semester.ly", + "emailAddress": "semester.ly@jh.edu", +} + +SOCIAL_AUTH_SAML_ENABLED_IDPS = { + "jhu": { + "entity_id": "https://idp.jh.edu/idp/shibboleth", + "url": "https://idp.jh.edu/idp/profile/SAML2/Redirect/SSO", + "x509cert": get_secret("JHU_SAML_IDP_CERT"), + } +} + SOCIAL_AUTH_FACEBOOK_SCOPE = [ "email", "user_friends", @@ -104,6 +136,7 @@ def get_secret(key): "social_core.backends.google.GooglePlusAuth", "social_core.backends.google.GoogleOAuth2", "social_core.backends.azuread_tenant.AzureADTenantOAuth2", + "social_core.backends.saml.SAMLAuth", ) FIELDS_STORED_IN_SESSION = ["student_token", "login_hash"] @@ -229,6 +262,7 @@ def get_secret(key): "social_core.backends.google.GoogleOAuth2", "social_core.backends.twitter.TwitterOAuth", "social_core.backends.azuread_tenant.AzureADTenantOAuth2", + "social_core.backends.saml.SAMLAuth", "django.contrib.auth.backends.ModelBackend", ) diff --git a/static/js/redux/ui/modals/UserAcquisitionModal.tsx b/static/js/redux/ui/modals/UserAcquisitionModal.tsx index d8208ffec7..83e556f7a7 100644 --- a/static/js/redux/ui/modals/UserAcquisitionModal.tsx +++ b/static/js/redux/ui/modals/UserAcquisitionModal.tsx @@ -79,7 +79,7 @@ const UserAcquisitionModal = () => { className="btn abnb-btn secondary" onClick={() => { const link = document.createElement("a"); - link.href = `/login/azuread-tenant-oauth2/?student_token=${LoginToken}&login_hash=${LoginHash}`; + link.href = `/login/saml/?idp=jhu&student_token=${LoginToken}&login_hash=${LoginHash}`; document.body.appendChild(link); link.click(); }}