providers/oauth: add support for consent stage, cleanup

This commit is contained in:
Jens Langhammer 2020-06-20 23:30:53 +02:00
parent c97b946a00
commit 4d81172a48
6 changed files with 36 additions and 78 deletions

View File

@ -23,7 +23,7 @@ class OAuth2Provider(Provider, AbstractApplication):
def html_setup_urls(self, request: HttpRequest) -> Optional[str]:
"""return template and context modal with URLs for authorize, token, openid-config, etc"""
return render_to_string(
"oauth2_provider/setup_url_modal.html",
"providers/oauth/setup_url_modal.html",
{
"provider": self,
"authorize_url": request.build_absolute_uri(

View File

@ -1,73 +0,0 @@
{% extends "login/base.html" %}
{% load passbook_utils %}
{% load i18n %}
{% block card_title %}
{% trans 'Authorize Application' %}
{% endblock %}
{% block card %}
<form method="POST" class="pf-c-form">
{% csrf_token %}
{% if not error %}
{% csrf_token %}
{% for field in form %}
{% if field.is_hidden %}
{{ field }}
{% endif %}
{% endfor %}
<div class="pf-c-form__group">
<p class="subtitle">
{% blocktrans with remote=application.name %}
You're about to sign into {{ remote }}.
{% endblocktrans %}
</p>
<p>{% trans "Application requires following permissions" %}</p>
<ul class="pf-c-list">
{% for scope in scopes_descriptions %}
<li>{{ scope }}</li>
{% endfor %}
</ul>
{{ form.errors }}
{{ form.non_field_errors }}
</div>
<div class="pf-c-form__group">
<p>
{% blocktrans with user=user %}
You are logged in as {{ user }}. Not you?
{% endblocktrans %}
<a href="{% url 'passbook_flows:default-invalidation' %}">{% trans 'Logout' %}</a>
</p>
</div>
<div class="pf-c-form__group pf-m-action">
<input type="submit" class="pf-c-button pf-m-primary" name="allow" value="{% trans 'Continue' %}">
<a href="{% back %}" class="pf-c-button pf-m-secondary">{% trans "Cancel" %}</a>
</div>
<div class="pf-c-form__group" style="display: none;" id="loading">
<div class="pf-c-form__horizontal-group">
<span class="pf-c-spinner" role="progressbar" aria-valuetext="Loading...">
<span class="pf-c-spinner__clipper"></span>
<span class="pf-c-spinner__lead-ball"></span>
<span class="pf-c-spinner__tail-ball"></span>
</span>
</div>
</div>
{% else %}
<div class="login-group">
<p class="subtitle">
{% blocktrans with err=error.error %}Error: {{ err }}{% endblocktrans %}
</p>
<p>{{ error.description }}</p>
</div>
{% endif %}
</form>
{% endblock %}
{% block scripts %}
<script>
document.querySelector("form").addEventListener("submit", (e) => {
document.getElementById("loading").removeAttribute("style");
});
</script>
{% endblock %}

View File

@ -1 +0,0 @@
{% extends "base/skeleton.html" %}

View File

@ -0,0 +1,20 @@
{% extends 'login/form_with_user.html' %}
{% load i18n %}
{% block beneath_form %}
<div class="pf-c-form__group">
<p>
{% blocktrans with name=context.application.name %}
You're about to sign into {{ name }}.
{% endblocktrans %}
</p>
<p>{% trans "Application requires following permissions" %}</p>
<ul class="pf-c-list">
{% for scope in context.scope_descriptions %}
<li>{{ scope }}</li>
{% endfor %}
</ul>
{{ hidden_inputs }}
</div>
{% endblock %}

View File

@ -1,9 +1,11 @@
"""passbook OAuth2 Views"""
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect
from django.views import View
from oauth2_provider.exceptions import OAuthToolkitError
from oauth2_provider.scopes import get_scopes_backend
from oauth2_provider.views.base import AuthorizationView
from structlog import get_logger
@ -20,6 +22,7 @@ from passbook.flows.stage import StageView
from passbook.flows.views import SESSION_KEY_PLAN
from passbook.lib.utils.urls import redirect_with_qs
from passbook.providers.oauth.models import OAuth2Provider
from passbook.stages.consent.stage import PLAN_CONTEXT_CONSENT_TEMPLATE
LOGGER = get_logger()
@ -32,9 +35,10 @@ PLAN_CONTEXT_CODE_CHALLENGE = "code_challenge"
PLAN_CONTEXT_CODE_CHALLENGE_METHOD = "code_challenge_method"
PLAN_CONTEXT_SCOPE = "scope"
PLAN_CONTEXT_NONCE = "nonce"
PLAN_CONTEXT_SCOPE_DESCRIPTION = "scope_descriptions"
class AuthorizationFlowInitView(AccessMixin, View):
class AuthorizationFlowInitView(AccessMixin, LoginRequiredMixin, View):
"""OAuth2 Flow initializer, checks access to application and starts flow"""
# pylint: disable=unused-argument
@ -54,8 +58,11 @@ class AuthorizationFlowInitView(AccessMixin, View):
return redirect("passbook_providers_oauth:oauth2-permission-denied")
# Regardless, we start the planner and return to it
planner = FlowPlanner(provider.authorization_flow)
# planner.use_cache = False
planner.allow_empty_flows = True
# Save scope descriptions
scopes = request.GET.get(PLAN_CONTEXT_SCOPE)
all_scopes = get_scopes_backend().get_all_scopes()
plan = planner.plan(
self.request,
{
@ -65,10 +72,15 @@ class AuthorizationFlowInitView(AccessMixin, View):
PLAN_CONTEXT_REDIRECT_URI: request.GET.get(PLAN_CONTEXT_REDIRECT_URI),
PLAN_CONTEXT_RESPONSE_TYPE: request.GET.get(PLAN_CONTEXT_RESPONSE_TYPE),
PLAN_CONTEXT_STATE: request.GET.get(PLAN_CONTEXT_STATE),
PLAN_CONTEXT_SCOPE: request.GET.get(PLAN_CONTEXT_SCOPE),
PLAN_CONTEXT_SCOPE: scopes,
PLAN_CONTEXT_NONCE: request.GET.get(PLAN_CONTEXT_NONCE),
PLAN_CONTEXT_SCOPE_DESCRIPTION: [
all_scopes[scope] for scope in scopes.split(" ")
],
PLAN_CONTEXT_CONSENT_TEMPLATE: "providers/oauth/consent.html",
},
)
plan.append(in_memory_stage(OAuth2Stage))
self.request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs(