Merge pull request #147 from cisagov/nmb/whomami

Add logged-in /whoami page
This commit is contained in:
Neil MartinsenBurrell 2022-09-30 09:20:13 -05:00 committed by GitHub
commit 18ee041e52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 174 additions and 171 deletions

View file

@ -26,7 +26,7 @@ services:
# Run Django in debug mode on local # Run Django in debug mode on local
- DJANGO_DEBUG=True - DJANGO_DEBUG=True
# Tell Django where it is being hosted # Tell Django where it is being hosted
- DJANGO_BASE_URL="localhost:8080" - DJANGO_BASE_URL=http://localhost:8080
# --- These keys are obtained from `.env` file --- # --- These keys are obtained from `.env` file ---
# Set a private JWT signing key for Login.gov # Set a private JWT signing key for Login.gov
- DJANGO_SECRET_LOGIN_KEY - DJANGO_SECRET_LOGIN_KEY

View file

@ -22,8 +22,7 @@ i.e.
@use "uswds-core" as *; @use "uswds-core" as *;
// Test custom style // Test custom style (except this has not enough contrast)
p { //p {
color: color('blue-10v'); // color: color('blue-10v');
} //}

View file

@ -164,6 +164,7 @@ TEMPLATES = [
"django.contrib.auth.context_processors.auth", "django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages", "django.contrib.messages.context_processors.messages",
"registrar.context_processors.language_code", "registrar.context_processors.language_code",
"registrar.context_processors.canonical_path",
], ],
}, },
}, },
@ -379,7 +380,7 @@ AUTHENTICATION_BACKENDS = [
# this is where unauthenticated requests are redirected when using # this is where unauthenticated requests are redirected when using
# the login_required() decorator, LoginRequiredMixin, or AccessMixin # the login_required() decorator, LoginRequiredMixin, or AccessMixin
LOGIN_URL = "openid/openid/login" LOGIN_URL = "openid/login"
# where to go after logging out # where to go after logging out
LOGOUT_REDIRECT_URL = "home" LOGOUT_REDIRECT_URL = "home"
@ -405,10 +406,8 @@ OIDC_PROVIDERS = {
}, },
"client_registration": { "client_registration": {
"client_id": "cisa_dotgov_registrar", "client_id": "cisa_dotgov_registrar",
"redirect_uris": [f"https://{env_base_url}/openid/callback/login/"], "redirect_uris": [f"{env_base_url}/openid/callback/login/"],
"post_logout_redirect_uris": [ "post_logout_redirect_uris": [f"{env_base_url}/openid/callback/logout/"],
f"https://{env_base_url}/openid/callback/logout/"
],
"token_endpoint_auth_method": ["private_key_jwt"], "token_endpoint_auth_method": ["private_key_jwt"],
"sp_private_key": secret_login_key, "sp_private_key": secret_login_key,
}, },

View file

@ -7,10 +7,11 @@ For more information see:
from django.contrib import admin from django.contrib import admin
from django.urls import include, path from django.urls import include, path
from registrar.views import health, index, profile from registrar.views import health, index, profile, whoami
urlpatterns = [ urlpatterns = [
path("", index.index, name="home"), path("", index.index, name="home"),
path("whoami", whoami.whoami, name="whoami"),
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path("health/", health.health), path("health/", health.health),
path("edit_profile/", profile.edit_profile, name="edit-profile"), path("edit_profile/", profile.edit_profile, name="edit-profile"),

View file

@ -11,3 +11,13 @@ def language_code(request):
TEMPLATES dict of our settings file). TEMPLATES dict of our settings file).
""" """
return {"LANGUAGE_CODE": settings.LANGUAGE_CODE} return {"LANGUAGE_CODE": settings.LANGUAGE_CODE}
def canonical_path(request):
"""Add a canonical URL to the template context.
To make a correct "rel=canonical" link in the HTML page, we need to
construct an absolute URL for the page, and we can't do that in the
template itself, so we do it here and pass the information on.
"""
return {"CANONICAL_PATH": request.build_absolute_uri(request.path)}

View file

@ -7,7 +7,7 @@
<meta http-equiv="x-ua-compatible" content="ie=edge"> <meta http-equiv="x-ua-compatible" content="ie=edge">
<title> <title>
{% block title %}{% endblock %} {% block title %}{% endblock %}
{{ site.name }} .gov Registrar
{% block extra_title %}{% endblock %} {% block extra_title %}{% endblock %}
</title> </title>
<meta name="description" content="{% block description %}{% endblock %}"> <meta name="description" content="{% block description %}{% endblock %}">
@ -16,8 +16,6 @@
{% endblock %} {% endblock %}
{% block extra_meta %}{% endblock extra_meta %} {% block extra_meta %}{% endblock extra_meta %}
{# TO-DO: Determine if <link rel="manifest" href="site.webmanifest"> is desirable #}
{# TO-DO: set defaults for these #} {# TO-DO: set defaults for these #}
<link rel="shortcut icon" href="{% static 'img/favicon.png' %}"> <link rel="shortcut icon" href="{% static 'img/favicon.png' %}">
<link rel="apple-touch-icon" href="{% static 'img/touch-icon.png' %}"> <link rel="apple-touch-icon" href="{% static 'img/touch-icon.png' %}">
@ -28,7 +26,7 @@
{% endblock %} {% endblock %}
{% block canonical %} {% block canonical %}
<link rel="canonical" href="{{ current_path }}"> <link rel="canonical" href="{{ CANONICAL_PATH }}">
{% endblock %} {% endblock %}
@ -54,11 +52,7 @@
<header class="usa-banner__header"> <header class="usa-banner__header">
<div class="usa-banner__inner"> <div class="usa-banner__inner">
<div class="grid-col-auto"> <div class="grid-col-auto">
<img <img class="usa-banner__header-flag" src="{% static 'img/us_flag_small.png' %}" alt="U.S. flag" />
class="usa-banner__header-flag"
src="{% static 'img/us_flag_small.png' %}"
alt="U.S. flag"
/>
</div> </div>
<div class="grid-col-fill tablet:grid-col-auto"> <div class="grid-col-fill tablet:grid-col-auto">
<p class="usa-banner__header-text"> <p class="usa-banner__header-text">
@ -68,28 +62,17 @@
Heres how you know Heres how you know
</p> </p>
</div> </div>
<button <button class="usa-accordion__button usa-banner__button" aria-expanded="false"
class="usa-accordion__button usa-banner__button" aria-controls="gov-banner-default-default">
aria-expanded="false"
aria-controls="gov-banner-default-default"
>
<span class="usa-banner__button-text">Heres how you know</span> <span class="usa-banner__button-text">Heres how you know</span>
</button> </button>
</div> </div>
</header> </header>
<div <div class="usa-banner__content usa-accordion__content" id="gov-banner-default-default">
class="usa-banner__content usa-accordion__content"
id="gov-banner-default-default"
>
<div class="grid-row grid-gap-lg"> <div class="grid-row grid-gap-lg">
<div class="usa-banner__guidance tablet:grid-col-6"> <div class="usa-banner__guidance tablet:grid-col-6">
<img <img class="usa-banner__icon usa-media-block__img" src="{% static 'img/icon-dot-gov.svg' %}" role="img"
class="usa-banner__icon usa-media-block__img" alt="" aria-hidden="true" />
src="{% static 'img/icon-dot-gov.svg' %}"
role="img"
alt=""
aria-hidden="true"
/>
<div class="usa-media-block__body"> <div class="usa-media-block__body">
<p> <p>
<strong>Official websites use .gov</strong><br />A <strong>Official websites use .gov</strong><br />A
@ -99,37 +82,20 @@
</div> </div>
</div> </div>
<div class="usa-banner__guidance tablet:grid-col-6"> <div class="usa-banner__guidance tablet:grid-col-6">
<img <img class="usa-banner__icon usa-media-block__img" src="{% static 'img/icon-https.svg' %}" role="img" alt=""
class="usa-banner__icon usa-media-block__img" aria-hidden="true" />
src="{% static 'img/icon-https.svg' %}"
role="img"
alt=""
aria-hidden="true"
/>
<div class="usa-media-block__body"> <div class="usa-media-block__body">
<p> <p>
<strong>Secure .gov websites use HTTPS</strong><br />A <strong>Secure .gov websites use HTTPS</strong><br />A
<strong>lock</strong> ( <strong>lock</strong> (
<span class="icon-lock" <span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" width="52" height="64"
><svg viewBox="0 0 52 64" class="usa-banner__lock-image" role="img"
xmlns="http://www.w3.org/2000/svg" aria-labelledby="banner-lock-title-default banner-lock-description-default" focusable="false">
width="52"
height="64"
viewBox="0 0 52 64"
class="usa-banner__lock-image"
role="img"
aria-labelledby="banner-lock-title-default banner-lock-description-default"
focusable="false"
>
<title id="banner-lock-title-default">Lock</title> <title id="banner-lock-title-default">Lock</title>
<desc id="banner-lock-description-default">A locked padlock</desc> <desc id="banner-lock-description-default">A locked padlock</desc>
<path <path fill="#000000" fill-rule="evenodd"
fill="#000000" d="M26 0c10.493 0 19 8.507 19 19v9h3a4 4 0 0 1 4 4v28a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4h3v-9C7 8.507 15.507 0 26 0zm0 8c-5.979 0-10.843 4.77-10.996 10.712L15 19v9h22v-9c0-6.075-4.925-11-11-11z" />
fill-rule="evenodd" </svg> </span>) or <strong>https://</strong> means youve safely connected to
d="M26 0c10.493 0 19 8.507 19 19v9h3a4 4 0 0 1 4 4v28a4 4 0 0 1-4 4H4a4 4 0 0 1-4-4V32a4 4 0 0 1 4-4h3v-9C7 8.507 15.507 0 26 0zm0 8c-5.979 0-10.843 4.77-10.996 10.712L15 19v9h22v-9c0-6.075-4.925-11-11-11z"
/>
</svg> </span
>) or <strong>https://</strong> means youve safely connected to
the .gov website. Share sensitive information only on official, the .gov website. Share sensitive information only on official,
secure websites. secure websites.
</p> </p>
@ -142,15 +108,14 @@
{% block banner %} {% block banner %}
<header class="usa-header usa-header-extended" role="banner"> <header class="usa-header usa-header-basic" role="navigation">
<div class="usa-nav-container">
<div class="usa-navbar"> <div class="usa-navbar">
{% block logo %} {% block logo %}
<div class="usa-logo" id="extended-logo"> <div class="usa-logo" id="extended-logo">
<em class="usa-logo-text"> <em class="usa-logo__text">
<a href="/" <a href="/" title="Home" aria-label="Home">
title="Home" {% block site_name %}Home{% endblock %}
aria-label="Home">
{% block site_name %}{{ site.name }}{% endblock %}
</a> </a>
</em> </em>
</div> </div>
@ -158,8 +123,23 @@
<button class="usa-menu-btn">Menu</button> <button class="usa-menu-btn">Menu</button>
</div> </div>
{% block usa_nav %} {% block usa_nav %}
<nav>
<button type="button" class="usa-nav__close">
<img src="/public/img/usa-icons/close.svg" role="img" alt="Close" />
</button>
<ul class="usa-nav__primary usa-accordion">
<li class="usa-nav__primary-item">
{% if user.is_authenticated %}
User: <a href="/whoami">{{ user.get_username }}</a>
{% else %}
<a href="/openid/login">Sign in</a>
{% endif %}
</li>
</ul>
</nav>
{% block usa_nav_secondary %}{% endblock %} {% block usa_nav_secondary %}{% endblock %}
{% endblock %} {% endblock %}
</div>
</header> </header>
{% endblock banner %} {% endblock banner %}
{% block usa_overlay %}<div class="usa-overlay"></div>{% endblock %} {% block usa_overlay %}<div class="usa-overlay"></div>{% endblock %}
@ -193,7 +173,7 @@
{% endblock %} {% endblock %}
{% block footer %} {% block footer %}
<div> <div>
<p class="copyright">&copy; {{ now.year }} {{ site.name }}</p> <p class="copyright">&copy; {% now "Y" %} CISA .gov Registrar</p>
</div> </div>
{% endblock %} {% endblock %}
</footer> </footer>
@ -208,8 +188,6 @@
{% block extrascript %}{% endblock %} {% block extrascript %}{% endblock %}
{# asynchronous analytics #}
<script async id="_fed_an_ua_tag" src="https://dap.digitalgov.gov/Universal­Federated­Analytics­M
in.js?agency={{ AGENCY }}"></script>
</body> </body>
</html> </html>

View file

@ -17,7 +17,6 @@
<p>This is the .gov registrar.</p> <p>This is the .gov registrar.</p>
{% if user.is_authenticated %} {% if user.is_authenticated %}
<p><b>Hello {{ user.id }}</b></p>
<p><a href="/openid/logout/">Click here to log out.</a></p> <p><a href="/openid/logout/">Click here to log out.</a></p>
{% else %} {% else %}
<p><a href="/openid/login/">Click here to log in.</a></p> <p><a href="/openid/login/">Click here to log in.</a></p>

View file

@ -2,17 +2,8 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block title %} Hello {% endblock %} {% block title %} Hello {% endblock %}
{% block hero %} {% block content %}
<section class="usa-hero"> <p> Hello {{ user.last_name|default:"No last name given" }}, {{ user.first_name|default:"No first name given" }} &lt;{{ user.email }}&gt;! </p>
<div class="usa-grid">
<div class="usa-hero-callout usa-section-dark"> <p><a href="/openid/logout">Click here to log out</a></p>
<h2>
<span class="usa-hero-callout-alt">This is sample content.</span>
This is only sample content.
</h2>
<p> {{ name }} You'll want to replace it with content of your own.</p>
<button class="usa-button usa-button--accent-cool">Click a usa button</button>
</div>
</div>
</section>
{% endblock %} {% endblock %}

View file

@ -2,14 +2,25 @@ from django.test import Client, TestCase
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
class HealthTest(TestCase): class TestViews(TestCase):
def setUp(self): def setUp(self):
self.client = Client() self.client = Client()
def test_health_check_endpoint(self): def test_health_check_endpoint(self):
response = self.client.get("/health/") response = self.client.get("/health/")
self.assertEqual(response.status_code, 200) self.assertContains(response, "OK", status_code=200)
self.assertContains(response, "OK")
def test_home_page(self):
"""Home page should be available without a login."""
response = self.client.get("/")
self.assertContains(response, "registrar", status_code=200)
self.assertContains(response, "log in")
def test_whoami_page_no_user(self):
"""Whoami page not accessible without a logged-in user."""
response = self.client.get("/whoami")
self.assertEqual(response.status_code, 302)
self.assertIn("?next=/whoami", response.headers["Location"])
class LoggedInTests(TestCase): class LoggedInTests(TestCase):
@ -23,6 +34,13 @@ class LoggedInTests(TestCase):
) )
self.client.force_login(self.user) self.client.force_login(self.user)
def test_whoami_page(self):
"""User information appears on the whoami page."""
response = self.client.get("/whoami")
self.assertContains(response, self.user.first_name)
self.assertContains(response, self.user.last_name)
self.assertContains(response, self.user.email)
def test_edit_profile(self): def test_edit_profile(self):
response = self.client.get("/edit_profile/") response = self.client.get("/edit_profile/")
self.assertContains(response, "Display Name") self.assertContains(response, "Display Name")

View file

@ -2,5 +2,5 @@ from django.shortcuts import render
def index(request): def index(request):
context = {"name": "World!"} """This page is available to anyone without logging in."""
return render(request, "whoami.html", context) return render(request, "home.html")

View file

@ -0,0 +1,8 @@
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
@login_required
def whoami(request):
"""This is the first page someone goes to after logging in."""
return render(request, "whoami.html")