providers/oauth: add support for consent stage, cleanup
This commit is contained in:
		| @ -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( | ||||
|  | ||||
| @ -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""" | ||||
| 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( | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer