providers/oauth: add support for consent stage, cleanup
This commit is contained in:
parent
c97b946a00
commit
4d81172a48
|
@ -23,7 +23,7 @@ class OAuth2Provider(Provider, AbstractApplication):
|
||||||
def html_setup_urls(self, request: HttpRequest) -> Optional[str]:
|
def html_setup_urls(self, request: HttpRequest) -> Optional[str]:
|
||||||
"""return template and context modal with URLs for authorize, token, openid-config, etc"""
|
"""return template and context modal with URLs for authorize, token, openid-config, etc"""
|
||||||
return render_to_string(
|
return render_to_string(
|
||||||
"oauth2_provider/setup_url_modal.html",
|
"providers/oauth/setup_url_modal.html",
|
||||||
{
|
{
|
||||||
"provider": self,
|
"provider": self,
|
||||||
"authorize_url": request.build_absolute_uri(
|
"authorize_url": request.build_absolute_uri(
|
||||||
|
|
|
@ -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 %}
|
|
|
@ -1 +0,0 @@
|
||||||
{% extends "base/skeleton.html" %}
|
|
|
@ -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 %}
|
|
@ -1,9 +1,11 @@
|
||||||
"""passbook OAuth2 Views"""
|
"""passbook OAuth2 Views"""
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from oauth2_provider.exceptions import OAuthToolkitError
|
from oauth2_provider.exceptions import OAuthToolkitError
|
||||||
|
from oauth2_provider.scopes import get_scopes_backend
|
||||||
from oauth2_provider.views.base import AuthorizationView
|
from oauth2_provider.views.base import AuthorizationView
|
||||||
from structlog import get_logger
|
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.flows.views import SESSION_KEY_PLAN
|
||||||
from passbook.lib.utils.urls import redirect_with_qs
|
from passbook.lib.utils.urls import redirect_with_qs
|
||||||
from passbook.providers.oauth.models import OAuth2Provider
|
from passbook.providers.oauth.models import OAuth2Provider
|
||||||
|
from passbook.stages.consent.stage import PLAN_CONTEXT_CONSENT_TEMPLATE
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
@ -32,9 +35,10 @@ PLAN_CONTEXT_CODE_CHALLENGE = "code_challenge"
|
||||||
PLAN_CONTEXT_CODE_CHALLENGE_METHOD = "code_challenge_method"
|
PLAN_CONTEXT_CODE_CHALLENGE_METHOD = "code_challenge_method"
|
||||||
PLAN_CONTEXT_SCOPE = "scope"
|
PLAN_CONTEXT_SCOPE = "scope"
|
||||||
PLAN_CONTEXT_NONCE = "nonce"
|
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"""
|
"""OAuth2 Flow initializer, checks access to application and starts flow"""
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
|
@ -54,8 +58,11 @@ class AuthorizationFlowInitView(AccessMixin, View):
|
||||||
return redirect("passbook_providers_oauth:oauth2-permission-denied")
|
return redirect("passbook_providers_oauth:oauth2-permission-denied")
|
||||||
# Regardless, we start the planner and return to it
|
# Regardless, we start the planner and return to it
|
||||||
planner = FlowPlanner(provider.authorization_flow)
|
planner = FlowPlanner(provider.authorization_flow)
|
||||||
# planner.use_cache = False
|
|
||||||
planner.allow_empty_flows = True
|
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(
|
plan = planner.plan(
|
||||||
self.request,
|
self.request,
|
||||||
{
|
{
|
||||||
|
@ -65,10 +72,15 @@ class AuthorizationFlowInitView(AccessMixin, View):
|
||||||
PLAN_CONTEXT_REDIRECT_URI: request.GET.get(PLAN_CONTEXT_REDIRECT_URI),
|
PLAN_CONTEXT_REDIRECT_URI: request.GET.get(PLAN_CONTEXT_REDIRECT_URI),
|
||||||
PLAN_CONTEXT_RESPONSE_TYPE: request.GET.get(PLAN_CONTEXT_RESPONSE_TYPE),
|
PLAN_CONTEXT_RESPONSE_TYPE: request.GET.get(PLAN_CONTEXT_RESPONSE_TYPE),
|
||||||
PLAN_CONTEXT_STATE: request.GET.get(PLAN_CONTEXT_STATE),
|
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_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))
|
plan.append(in_memory_stage(OAuth2Stage))
|
||||||
self.request.session[SESSION_KEY_PLAN] = plan
|
self.request.session[SESSION_KEY_PLAN] = plan
|
||||||
return redirect_with_qs(
|
return redirect_with_qs(
|
||||||
|
|
Reference in New Issue