Skip to content
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

Admin: Add Google SSO for Compiler users #1855

Merged
merged 23 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1ce210d
feat(admin): first pass @ django_google_sso; allow compiler.la domains
machikoyasuda Jan 17, 2024
60d3d95
fix(settings): allow wikimedia link
machikoyasuda Jan 17, 2024
33edf7d
fix(pyproject): require django-google-sso
machikoyasuda Jan 17, 2024
e1a61c9
fix(csp): add admin.js files, add google sso user icons to allowlist
machikoyasuda Jan 17, 2024
f603890
fix(tests): only install django-google-sso, only add sso url if admin
machikoyasuda Jan 17, 2024
7360fd1
fix(settings): move app to installed_apps if admin
machikoyasuda Jan 17, 2024
bbb64a1
feat(admin): fetch and save admin user's ggoogle email, first and las…
machikoyasuda Jan 17, 2024
575ef39
refactor(settings): save allowable_domains as a dictionary in Terraform
machikoyasuda Jan 17, 2024
b8e0acb
refactor(settings): add google sso svg, remove wikimedia from csp
machikoyasuda Jan 17, 2024
35a6a46
fix(pyproject): unpin version
machikoyasuda Jan 17, 2024
8c21ca2
fix(pyproject): use ==
machikoyasuda Jan 17, 2024
de6f66d
feat(admin): create allow list for staff and admin + terraform vars
machikoyasuda Jan 19, 2024
38e9a07
fix(settings): remove 120.. from allowed_hosts
machikoyasuda Jan 30, 2024
0bee9a9
refactor(sso): use requests, not httpx
machikoyasuda Jan 30, 2024
660f269
feat(test): test in progress
machikoyasuda Feb 2, 2024
0d5984e
fix(tests): use mocker; fixed tests yay
machikoyasuda Feb 6, 2024
2e28ec7
chore: undo test changes
machikoyasuda Feb 6, 2024
a0c4b2e
refactor(admin): move code to Admin.py
machikoyasuda Feb 6, 2024
4f98e63
fix(settings): remove if ADMIN checks
machikoyasuda Feb 6, 2024
0c28146
fix(test): fix test
machikoyasuda Feb 7, 2024
5c00e19
fix: rename to core, remove unused file
machikoyasuda Feb 7, 2024
49c78a3
fix(tests): remove tests, un-remove admin check
machikoyasuda Feb 7, 2024
147cd51
fix: undo admin fixture
machikoyasuda Feb 7, 2024
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
23 changes: 22 additions & 1 deletion benefits/core/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
The core application: Admin interface configuration.
"""

from django.conf import settings
import requests

from django.conf import settings

if settings.ADMIN:
import logging
Expand All @@ -21,3 +22,23 @@
]:
logger.debug(f"Register {model.__name__}")
admin.site.register(model)

def pre_login_user(user, request):
logger.debug(f"Running pre-login callback for user: {user.username}")
token = request.session.get("google_sso_access_token")
if token:
headers = {
"Authorization": f"Bearer {token}",
}

# Request Google user info to get name and email
url = "https://www.googleapis.com/oauth2/v3/userinfo"
response = requests.get(url, headers=headers, timeout=settings.REQUESTS_TIMEOUT)
user_data = response.json()
logger.debug(f"Updating admin user data from Google for user with email: {user_data['email']}")

user.first_name = user_data["given_name"]
user.last_name = user_data["family_name"]
user.username = user_data["email"]
user.email = user_data["email"]
user.save()
26 changes: 24 additions & 2 deletions benefits/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def _filter_empty(ls):

ADMIN = os.environ.get("DJANGO_ADMIN", "False").lower() == "true"
thekaveman marked this conversation as resolved.
Show resolved Hide resolved

ALLOWED_HOSTS = _filter_empty(os.environ.get("DJANGO_ALLOWED_HOSTS", "localhost,127.0.0.1").split(","))
ALLOWED_HOSTS = _filter_empty(os.environ.get("DJANGO_ALLOWED_HOSTS", "localhost").split(","))

# Application definition

Expand All @@ -37,11 +37,27 @@ def _filter_empty(ls):
]

if ADMIN:
GOOGLE_SSO_CLIENT_ID = os.environ.get("GOOGLE_SSO_CLIENT_ID", "secret")
GOOGLE_SSO_PROJECT_ID = os.environ.get("GOOGLE_SSO_PROJECT_ID", "benefits-admin")
GOOGLE_SSO_CLIENT_SECRET = os.environ.get("GOOGLE_SSO_CLIENT_SECRET", "secret")
GOOGLE_SSO_ALLOWABLE_DOMAINS = _filter_empty(os.environ.get("GOOGLE_SSO_ALLOWABLE_DOMAINS", "compiler.la").split(","))
GOOGLE_SSO_STAFF_LIST = _filter_empty(os.environ.get("GOOGLE_SSO_STAFF_LIST", "").split(","))
GOOGLE_SSO_SUPERUSER_LIST = _filter_empty(os.environ.get("GOOGLE_SSO_SUPERUSER_LIST", "").split(","))
GOOGLE_SSO_LOGO_URL = "/static/img/icon/google_sso_logo.svg"
GOOGLE_SSO_SAVE_ACCESS_TOKEN = True
GOOGLE_SSO_PRE_LOGIN_CALLBACK = "benefits.core.admin.pre_login_user"
GOOGLE_SSO_SCOPES = [
"openid",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile",
]

INSTALLED_APPS.extend(
[
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django_google_sso", # Add django_google_sso
]
)

Expand Down Expand Up @@ -282,7 +298,11 @@ def _filter_empty(ls):
if len(env_frame_src) > 0:
CSP_FRAME_SRC = env_frame_src

CSP_IMG_SRC = ["'self'", "data:"]
CSP_IMG_SRC = [
"'self'",
"data:",
"*.googleusercontent.com",
]

# Configuring strict Content Security Policy
# https://django-csp.readthedocs.io/en/latest/nonce.html
Expand All @@ -294,9 +314,11 @@ def _filter_empty(ls):
CSP_REPORT_URI = [sentry.SENTRY_CSP_REPORT_URI]

CSP_SCRIPT_SRC = [
"'self'",
"https://cdn.amplitude.com/libs/",
"https://cdn.jsdelivr.net/",
"*.littlepay.com",
"https://code.jquery.com/jquery-3.6.0.min.js",
]
env_script_src = _filter_empty(os.environ.get("DJANGO_CSP_SCRIPT_SRC", "").split(","))
CSP_SCRIPT_SRC.extend(env_script_src)
Expand Down
7 changes: 7 additions & 0 deletions benefits/static/img/icon/google_sso_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions benefits/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@ def trigger_error(request):

logger.debug("Register admin urls")
urlpatterns.append(path("admin/", admin.site.urls))
urlpatterns.append(path("google_sso/", include("django_google_sso.urls", namespace="django_google_sso")))
else:
logger.debug("Skip url registrations for admin")
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ dependencies = [
"Authlib==1.3.0",
"Django==5.0.1",
"django-csp==3.7",
"django-google-sso==5.0.0",
"eligibility-api==2023.9.1",
"requests==2.31.0",
"sentry-sdk==1.39.2",
Expand Down
9 changes: 9 additions & 0 deletions terraform/app_service.tf
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ resource "azurerm_linux_web_app" "main" {

"HEALTHCHECK_USER_AGENTS" = local.is_dev ? null : "${local.secret_prefix}healthcheck-user-agents)",

# Google SSO for Admin

"GOOGLE_SSO_CLIENT_ID" = "${local.secret_prefix}google-sso-client-id",
"GOOGLE_SSO_PROJECT_ID" = "${local.secret_prefix}google-sso-project-id",
"GOOGLE_SSO_CLIENT_SECRET" = "${local.secret_prefix}google-sso-client-secret",
"GOOGLE_SSO_ALLOWABLE_DOMAINS" = "${local.secret_prefix}google-sso-allowable-domains",
"GOOGLE_SSO_STAFF_LIST" = "${local.secret_prefix}google-sso-staff-list",
"GOOGLE_SSO_SUPERUSER_LIST" = "${local.secret_prefix}google-sso-superuser-list"

# Sentry
"SENTRY_DSN" = "${local.secret_prefix}sentry-dsn)",
"SENTRY_ENVIRONMENT" = local.env_name,
Expand Down
Loading