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,28 +7,26 @@
<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 %}">
{% block viewport_meta %} {% block viewport_meta %}
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
{% 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' %}">
{% block css %} {% block css %}
<link rel="stylesheet" href="{% static 'css/styles.css' %}"> <link rel="stylesheet" href="{% static 'css/styles.css' %}">
<script src="{% static 'js/uswds-init.min.js' %}" defer></script> <script src="{% static 'js/uswds-init.min.js' %}" defer></script>
{% endblock %} {% endblock %}
{% block canonical %} {% block canonical %}
<link rel="canonical" href="{{ current_path }}"> <link rel="canonical" href="{{ CANONICAL_PATH }}">
{% endblock %} {% endblock %}
@ -49,154 +47,136 @@
<script src="{% static 'js/uswds.min.js' %}" defer></script> <script src="{% static 'js/uswds.min.js' %}" defer></script>
<a class="usa-skipnav" href="#main-content">Skip to main content</a> <a class="usa-skipnav" href="#main-content">Skip to main content</a>
<section class="usa-banner" aria-label="Official government website"> <section class="usa-banner" aria-label="Official government website">
<div class="usa-accordion"> <div class="usa-accordion">
<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" </div>
src="{% static 'img/us_flag_small.png' %}" <div class="grid-col-fill tablet:grid-col-auto">
alt="U.S. flag" <p class="usa-banner__header-text">
/> An official website of the United States government
</div> </p>
<div class="grid-col-fill tablet:grid-col-auto"> <p class="usa-banner__header-action" aria-hidden="true">
<p class="usa-banner__header-text"> Heres how you know
An official website of the United States government
</p>
<p class="usa-banner__header-action" aria-hidden="true">
Heres how you know
</p>
</div>
<button
class="usa-accordion__button usa-banner__button"
aria-expanded="false"
aria-controls="gov-banner-default-default"
>
<span class="usa-banner__button-text">Heres how you know</span>
</button>
</div>
</header>
<div
class="usa-banner__content usa-accordion__content"
id="gov-banner-default-default"
>
<div class="grid-row grid-gap-lg">
<div class="usa-banner__guidance tablet:grid-col-6">
<img
class="usa-banner__icon usa-media-block__img"
src="{% static 'img/icon-dot-gov.svg' %}"
role="img"
alt=""
aria-hidden="true"
/>
<div class="usa-media-block__body">
<p>
<strong>Official websites use .gov</strong><br />A
<strong>.gov</strong> website belongs to an official government
organization in the United States.
</p> </p>
</div> </div>
<button class="usa-accordion__button usa-banner__button" aria-expanded="false"
aria-controls="gov-banner-default-default">
<span class="usa-banner__button-text">Heres how you know</span>
</button>
</div> </div>
<div class="usa-banner__guidance tablet:grid-col-6"> </header>
<img <div class="usa-banner__content usa-accordion__content" id="gov-banner-default-default">
class="usa-banner__icon usa-media-block__img" <div class="grid-row grid-gap-lg">
src="{% static 'img/icon-https.svg' %}" <div class="usa-banner__guidance tablet:grid-col-6">
role="img" <img class="usa-banner__icon usa-media-block__img" src="{% static 'img/icon-dot-gov.svg' %}" role="img"
alt="" alt="" aria-hidden="true" />
aria-hidden="true" <div class="usa-media-block__body">
/> <p>
<div class="usa-media-block__body"> <strong>Official websites use .gov</strong><br />A
<p> <strong>.gov</strong> website belongs to an official government
<strong>Secure .gov websites use HTTPS</strong><br />A organization in the United States.
<strong>lock</strong> ( </p>
<span class="icon-lock" </div>
><svg </div>
xmlns="http://www.w3.org/2000/svg" <div class="usa-banner__guidance tablet:grid-col-6">
width="52" <img class="usa-banner__icon usa-media-block__img" src="{% static 'img/icon-https.svg' %}" role="img" alt=""
height="64" aria-hidden="true" />
viewBox="0 0 52 64" <div class="usa-media-block__body">
class="usa-banner__lock-image" <p>
role="img" <strong>Secure .gov websites use HTTPS</strong><br />A
aria-labelledby="banner-lock-title-default banner-lock-description-default" <strong>lock</strong> (
focusable="false" <span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" width="52" height="64"
> viewBox="0 0 52 64" class="usa-banner__lock-image" role="img"
<title id="banner-lock-title-default">Lock</title> aria-labelledby="banner-lock-title-default banner-lock-description-default" focusable="false">
<desc id="banner-lock-description-default">A locked padlock</desc> <title id="banner-lock-title-default">Lock</title>
<path <desc id="banner-lock-description-default">A locked padlock</desc>
fill="#000000" <path fill="#000000" fill-rule="evenodd"
fill-rule="evenodd" 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" />
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,
</svg> </span secure websites.
>) or <strong>https://</strong> means youve safely connected to </p>
the .gov website. Share sensitive information only on official, </div>
secure websites.
</p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </section>
</section>
{% 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"> </a>
{% block site_name %}{{ site.name }}{% endblock %} </em>
</a> </div>
</em>
</div>
{% endblock %} {% endblock %}
<button class="usa-menu-btn">Menu</button> <button class="usa-menu-btn">Menu</button>
</div> </div>
{% block usa_nav %} {% block usa_nav %}
{% block usa_nav_secondary %}{% endblock %} <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 %}
{% endblock %} {% endblock %}
</header> </div>
</header>
{% endblock banner %} {% endblock banner %}
{% block usa_overlay %}<div class="usa-overlay"></div>{% endblock %} {% block usa_overlay %}<div class="usa-overlay"></div>{% endblock %}
<div id="wrapper"> <div id="wrapper">
{% block messages %} {% block messages %}
{% if messages %} {% if messages %}
<ul class="messages"> <ul class="messages">
{% for message in messages %} {% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}> <li{% if message.tags %} class="{{ message.tags }}" {% endif %}>
{{ message }} {{ message }}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block section_nav %}{% endblock %} {% block section_nav %}{% endblock %}
<main id="main-content"> <main id="main-content">
{% block hero %}{% endblock %} {% block hero %}{% endblock %}
{% block content %}{% endblock %} {% block content %}{% endblock %}
</main> </main>
<div role="complementary">{% block complementary %}{% endblock %}</div> <div role="complementary">{% block complementary %}{% endblock %}</div>
{% block content_bottom %}{% endblock %} {% block content_bottom %}{% endblock %}
</div>
<footer id="footer" role="contentinfo">
{% block footer_nav %}
{% endblock %}
{% block footer %}
<div>
<p class="copyright">&copy; {% now "Y" %} CISA .gov Registrar</p>
</div> </div>
{% endblock %}
<footer id="footer" role="contentinfo"> </footer>
{% block footer_nav %}
{% endblock %}
{% block footer %}
<div>
<p class="copyright">&copy; {{ now.year }} {{ site.name }}</p>
</div>
{% endblock %}
</footer>
</div> <!-- /#wrapper --> </div> <!-- /#wrapper -->
{% block init_js %}{% endblock %}{# useful for vars and other initializations #} {% block init_js %}{% endblock %}{# useful for vars and other initializations #}
@ -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

@ -3,21 +3,20 @@
{% block title %} Hello {% endblock %} {% block title %} Hello {% endblock %}
{% block hero %} {% block hero %}
<section class="usa-hero"> <section class="usa-hero">
<div class="usa-grid"> <div class="usa-grid">
<div class="usa-hero-callout usa-section-dark"> <div class="usa-hero-callout usa-section-dark">
<h2> <h2>
<span class="usa-hero-callout-alt">Welcome to the .gov registrar</span> <span class="usa-hero-callout-alt">Welcome to the .gov registrar</span>
</h2> </h2>
</div> </div>
</section> </section>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<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")