Skip to content

Allauth: add SAML integration #11262

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions dockerfiles/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ RUN apt-get -y install \
npm \
rclone

# Dependencies for django-allauth SAML support.
# See:
# - https://github.com/SAML-Toolkits/python3-saml#installation
# - https://github.com/xmlsec/python-xmlsec#linux-debian
RUN apt-get -y install \
pkg-config \
libxml2-dev \
libxmlsec1-dev \
libxmlsec1-openssl

# Gets the MinIO mc client used to add buckets upon initialization
# If this client should have issues running inside this image, it is also
# fine to defer it to a separate image.
Expand Down
35 changes: 35 additions & 0 deletions readthedocs/sso/migrations/0002_add_saml_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Generated by Django 4.2.11 on 2024-04-04 20:32

import django.db.models.deletion
from django.db import migrations, models
from django_safemigrate import Safe


class Migration(migrations.Migration):
safe = Safe.before_deploy
dependencies = [
("socialaccount", "0005_socialtoken_nullable_app"),
("sso", "0001_squashed"),
]

operations = [
migrations.AddField(
model_name="ssointegration",
name="saml_app",
field=models.OneToOneField(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="sso_integration",
to="socialaccount.socialapp",
),
),
migrations.AlterField(
model_name="ssointegration",
name="provider",
field=models.CharField(
choices=[("allauth", "AllAuth"), ("email", "Email"), ("saml", "SAML")],
max_length=32,
),
),
]
11 changes: 11 additions & 0 deletions readthedocs/sso/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ class SSOIntegration(models.Model):

PROVIDER_ALLAUTH = "allauth"
PROVIDER_EMAIL = "email"
PROVIDER_SAML = "saml"
PROVIDER_CHOICES = (
(PROVIDER_ALLAUTH, "AllAuth"),
(PROVIDER_EMAIL, "Email"),
(PROVIDER_SAML, "SAML"),
)

name = models.CharField(
Expand All @@ -36,6 +38,15 @@ class SSOIntegration(models.Model):
choices=PROVIDER_CHOICES,
max_length=32,
)

saml_app = models.OneToOneField(
"socialaccount.SocialApp",
related_name="sso_integration",
on_delete=models.CASCADE,
null=True,
blank=True,
)

domains = models.ManyToManyField(
"sso.SSODomain",
related_name="ssointegrations",
Expand Down
2 changes: 2 additions & 0 deletions readthedocs/subscriptions/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
TYPE_AUDIT_LOGS = "audit-logs"
TYPE_AUDIT_PAGEVIEWS = "audit-pageviews"
TYPE_REDIRECTS_LIMIT = "redirects-limit"
TYPE_SSO_SAML = "sso-saml"

FEATURE_TYPES = (
(TYPE_CNAME, _("Custom domain")),
Expand All @@ -31,6 +32,7 @@
(TYPE_PAGEVIEW_ANALYTICS, _("Pageview analytics")),
(TYPE_CONCURRENT_BUILDS, _("Concurrent builds")),
(TYPE_SSO, _("Single sign on (SSO) with Google")),
(TYPE_SSO_SAML, _("Single sign on with SAML")),
(TYPE_CUSTOM_URL, _("Custom URLs")),
(TYPE_AUDIT_LOGS, _("Audit logs")),
(TYPE_AUDIT_PAGEVIEWS, _("Audit logs for every page view")),
Expand Down
22 changes: 6 additions & 16 deletions readthedocs/templates/socialaccount/snippets/provider_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,12 @@
{% get_providers as socialaccount_providers %}

{% for provider in socialaccount_providers %}
{% if provider.id == "openid" %}
{% for brand in provider.get_brands %}
<li>
<form action="{% provider_login_url provider.id openid=brand.openid_url process=process next=next %}" method="post">
{% csrf_token %}
<button
class="socialaccount-provider {{ provider.id }} {{ brand.id }} button"
type="submit"
title="{{ brand.name }}">
{% trans verbiage|default:'Connect to' %} {{ brand.name }}
</button>
</form>
</li>
{% endfor %}
{% endif %}
{% if provider.id != 'bitbucket' %}
{% comment %}
- OpenID is not implemented.
- Bitbucket is deprecated (in favor of their new oauth implementation).
- SAML is handled in another view, we don't want to list all SAML integrations here.
{% endcomment %}
{% if provider.id != 'bitbucket' and provider.id != 'saml' %}
{% if allowed_providers and provider.id in allowed_providers or not allowed_providers %}
<li>
<form action="{% provider_login_url provider.id process=process scope=scope auth_params=auth_params next=next %}" method="post">
Expand Down
17 changes: 16 additions & 1 deletion requirements/deploy.txt
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ django==4.2.11
# django-timezone-field
# djangorestframework
# jsonfield
django-allauth==0.57.2
django-allauth[saml]==0.57.2
# via -r requirements/pip.txt
django-annoying==0.10.6
# via -r requirements/pip.txt
Expand Down Expand Up @@ -225,6 +225,10 @@ idna==3.6
# requests
ipython==8.23.0
# via -r requirements/deploy.in
isodate==0.6.1
# via
# -r requirements/pip.txt
# python3-saml
jedi==0.19.1
# via ipython
jmespath==1.0.1
Expand Down Expand Up @@ -252,6 +256,8 @@ lxml==5.2.1
# via
# -r requirements/pip.txt
# pyquery
# python3-saml
# xmlsec
markdown==3.6
# via -r requirements/pip.txt
matplotlib-inline==0.1.6
Expand Down Expand Up @@ -318,6 +324,10 @@ python3-openid==3.2.0
# via
# -r requirements/pip.txt
# django-allauth
python3-saml==1.16.0
# via
# -r requirements/pip.txt
# django-allauth
pytz==2024.1
# via
# -r requirements/pip.txt
Expand Down Expand Up @@ -361,6 +371,7 @@ six==1.16.0
# asttokens
# django-annoying
# django-elasticsearch-dsl
# isodate
# python-dateutil
# unicode-slugify
slumber==0.7.1
Expand Down Expand Up @@ -440,3 +451,7 @@ websocket-client==1.7.0
# via
# -r requirements/pip.txt
# docker
xmlsec==1.3.13
# via
# -r requirements/pip.txt
# python3-saml
17 changes: 16 additions & 1 deletion requirements/docker.txt
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ django==4.2.11
# django-timezone-field
# djangorestframework
# jsonfield
django-allauth==0.57.2
django-allauth[saml]==0.57.2
# via -r requirements/pip.txt
django-annoying==0.10.6
# via -r requirements/pip.txt
Expand Down Expand Up @@ -240,6 +240,10 @@ ipdb==0.13.13
# via -r requirements/docker.in
ipython==8.23.0
# via ipdb
isodate==0.6.1
# via
# -r requirements/pip.txt
# python3-saml
jedi==0.19.1
# via ipython
jmespath==1.0.1
Expand Down Expand Up @@ -267,6 +271,8 @@ lxml==5.2.1
# via
# -r requirements/pip.txt
# pyquery
# python3-saml
# xmlsec
markdown==3.6
# via -r requirements/pip.txt
markdown-it-py==3.0.0
Expand Down Expand Up @@ -350,6 +356,10 @@ python3-openid==3.2.0
# via
# -r requirements/pip.txt
# django-allauth
python3-saml==1.16.0
# via
# -r requirements/pip.txt
# django-allauth
pytz==2024.1
# via
# -r requirements/pip.txt
Expand Down Expand Up @@ -393,6 +403,7 @@ six==1.16.0
# asttokens
# django-annoying
# django-elasticsearch-dsl
# isodate
# python-dateutil
# unicode-slugify
slumber==0.7.1
Expand Down Expand Up @@ -477,3 +488,7 @@ websocket-client==1.7.0
# docker
wmctrl==0.5
# via pdbpp
xmlsec==1.3.13
# via
# -r requirements/pip.txt
# python3-saml
2 changes: 1 addition & 1 deletion requirements/pip.in
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ tzdata

# 0.58.0 refactored the built-in templates,
# we need to check if we need to update our custom templates.
django-allauth==0.57.2
django-allauth[saml]==0.57.2

requests-oauthlib

Expand Down
14 changes: 12 additions & 2 deletions requirements/pip.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ django==4.2.11
# django-timezone-field
# djangorestframework
# jsonfield
django-allauth==0.57.2
django-allauth[saml]==0.57.2
# via -r requirements/pip.in
django-annoying==0.10.6
# via -r requirements/pip.in
Expand Down Expand Up @@ -170,6 +170,8 @@ gunicorn==21.2.0
# via -r requirements/pip.in
idna==3.6
# via requests
isodate==0.6.1
# via python3-saml
jmespath==1.0.1
# via
# boto3
Expand All @@ -185,7 +187,10 @@ lexid==2021.1006
looseversion==1.3.0
# via bumpver
lxml==5.2.1
# via pyquery
# via
# pyquery
# python3-saml
# xmlsec
markdown==3.6
# via -r requirements/pip.in
oauthlib==3.2.2
Expand Down Expand Up @@ -223,6 +228,8 @@ python-dateutil==2.9.0.post0
# python-crontab
python3-openid==3.2.0
# via django-allauth
python3-saml==1.16.0
# via django-allauth
pytz==2024.1
# via
# -r requirements/pip.in
Expand Down Expand Up @@ -260,6 +267,7 @@ six==1.16.0
# via
# django-annoying
# django-elasticsearch-dsl
# isodate
# python-dateutil
# unicode-slugify
slumber==0.7.1
Expand Down Expand Up @@ -311,6 +319,8 @@ wcwidth==0.2.13
# via prompt-toolkit
websocket-client==1.7.0
# via docker
xmlsec==1.3.13
# via python3-saml

# The following packages are considered to be unsafe in a requirements file:
# pip
17 changes: 16 additions & 1 deletion requirements/testing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ django==4.2.11
# django-timezone-field
# djangorestframework
# jsonfield
django-allauth==0.57.2
django-allauth[saml]==0.57.2
# via -r requirements/pip.txt
django-annoying==0.10.6
# via -r requirements/pip.txt
Expand Down Expand Up @@ -230,6 +230,10 @@ imagesize==1.4.1
# via sphinx
iniconfig==2.0.0
# via pytest
isodate==0.6.1
# via
# -r requirements/pip.txt
# python3-saml
jinja2==3.1.3
# via sphinx
jmespath==1.0.1
Expand Down Expand Up @@ -257,6 +261,8 @@ lxml==5.2.1
# via
# -r requirements/pip.txt
# pyquery
# python3-saml
# xmlsec
markdown==3.6
# via -r requirements/pip.txt
markupsafe==2.1.5
Expand Down Expand Up @@ -333,6 +339,10 @@ python3-openid==3.2.0
# via
# -r requirements/pip.txt
# django-allauth
python3-saml==1.16.0
# via
# -r requirements/pip.txt
# django-allauth
pytz==2024.1
# via
# -r requirements/pip.txt
Expand Down Expand Up @@ -379,6 +389,7 @@ six==1.16.0
# -r requirements/pip.txt
# django-annoying
# django-elasticsearch-dsl
# isodate
# python-dateutil
# unicode-slugify
slumber==0.7.1
Expand Down Expand Up @@ -465,5 +476,9 @@ websocket-client==1.7.0
# via
# -r requirements/pip.txt
# docker
xmlsec==1.3.13
# via
# -r requirements/pip.txt
# python3-saml
yamale==2.2.0
# via -r requirements/testing.in