wip: refactor login flow

Instead of dealing with in JavaScript, let's do a more classic
HTTP flow.

The main flows work, but there is still at least one to deal with:
when editing a map without being logged in, the server may ask for
login, and in this case we should login THEN reissue the request,
so we need to interrupt the first request in some way,
otherwise the server will still answer with a 403, which is what
happens after this commit.
This commit is contained in:
Yohan Boniface 2024-01-24 11:54:51 +01:00
parent 084bc3d518
commit 49f600adfa
8 changed files with 81 additions and 79 deletions

View file

@ -60,17 +60,3 @@ def can_view_map(view_func):
return view_func(request, *args, **kwargs) return view_func(request, *args, **kwargs)
return wrapper return wrapper
def jsonize_view(view_func):
@wraps(view_func)
def wrapper(request, *args, **kwargs):
response = view_func(request, *args, **kwargs)
response_kwargs = {}
if hasattr(response, "rendered_content"):
response_kwargs["html"] = response.rendered_content
if response.has_header("location"):
response_kwargs["redirect"] = response["location"]
return simple_json_response(**response_kwargs)
return wrapper

View file

@ -37,6 +37,21 @@ input:-moz-placeholder, :-moz-placeholder {
/* **************** */ /* **************** */
/* Login icons */ /* Login icons */
/* **************** */ /* **************** */
body.login {
width: 320px;
margin: auto;
text-align: center;
}
body.login header {
display: flex;
justify-content: center;
}
.login-grid {
display: grid;
justify-content: space-between;
grid-gap: 5px;
grid-template-columns: repeat(auto-fill,92px);
}
.login-grid li, .login-grid li,
.login-grid a { .login-grid a {
display: inline-block; display: inline-block;

View file

@ -73,12 +73,12 @@ export const ServerRequest = Request.extend({
headers['X-CSRFToken'] = token headers['X-CSRFToken'] = token
} }
const response = await Request.prototype.post.call(this, uri, headers, data) const response = await Request.prototype.post.call(this, uri, headers, data)
return this._handle_json_response(response) return await this._handle_json_response(response)
}, },
get: async function (uri, headers) { get: async function (uri, headers) {
const response = await Request.prototype.get.call(this, uri, headers) const response = await Request.prototype.get.call(this, uri, headers)
return this._handle_json_response(response) return await this._handle_json_response(response)
}, },
_handle_json_response: async function (response) { _handle_json_response: async function (response) {
@ -105,10 +105,15 @@ export const ServerRequest = Request.extend({
this.ui.closePanel() this.ui.closePanel()
} else if (data.error) { } else if (data.error) {
this.ui.alert({ content: data.error, level: 'error' }) this.ui.alert({ content: data.error, level: 'error' })
} else if (data.html) { } else if (data.login_required) {
const ui_options = { data } // TODO: stop flow and run request again when user
let listen_options // is logged in
this.ui.openPanel(ui_options) const win = window.open(data.login_required)
window.umap_proceed = () => {
console.log('logged in')
this.fire('login')
win.close()
}
} }
}, },

View file

@ -1,37 +1,51 @@
{% load i18n %} {% extends "base.html" %}
{% if ENABLE_ACCOUNT_LOGIN %} {% load umap_tags i18n %}
<h5>{% trans "Please log in with your account" %}</h5> {% block extra_head %}
<div> {% umap_css %}
{% if form.non_field_errors %} {% endblock extra_head %}
<ul class="form-errors"> {% block body_class %}
{% for error in form.non_field_errors %}<li>{{ error }}</li>{% endfor %} login
{% endblock body_class %}
{% block content %}
<section>
<header class="umap-nav">
{% include "umap/branding.html" %}
</header>
{% if ENABLE_ACCOUNT_LOGIN %}
<h2>{% trans "Please log in with your account" %}</h2>
<div>
{% if form.non_field_errors %}
<ul class="form-errors">
{% for error in form.non_field_errors %}<li>{{ error }}</li>{% endfor %}
</ul>
{% endif %}
<form id="login_form" action="{% url "login" %}" method="post">
{% csrf_token %}
{{ form.username.errors }}
<input type="text"
name="username"
placeholder="{% trans "Username" %}"
autofocus />
{{ form.password.errors }}
<input type="password" name="password" placeholder="{% trans "Password" %}" />
<input type="submit" value="{% trans "Login" %}" />
</form>
</div>
{% endif %}
{% if backends.backends|length %}
<h2>{% trans "Please choose a provider" %}</h2>
<div>
<ul class="login-grid block-grid">
{% for name in backends.backends %}
<li>
<a rel="nofollow"
href="{% url "social:begin" name %}"
class="umap-login-popup login-{{ name }}"
title="{{ name|title }}"></a>
</li>
{% endfor %}
</ul> </ul>
{% endif %} </div>
<form id="login_form" action="{% url "login" %}" method="post"> {% endif %}
{% csrf_token %} </section>
{{ form.username.errors }} {% endblock content %}
<input type="text"
name="username"
placeholder="{% trans "Username" %}"
autofocus />
{{ form.password.errors }}
<input type="password" name="password" placeholder="{% trans "Password" %}" />
<input type="submit" value="{% trans "Login" %}" />
</form>
</div>
{% endif %}
{% if backends.backends|length %}
<h5>{% trans "Please choose a provider" %}</h5>
<div>
<ul class="login-grid block-grid">
{% for name in backends.backends %}
<li>
<a rel="nofollow"
href="{% url "social:begin" name %}"
class="umap-login-popup login-{{ name }}"
title="{{ name|title }}"></a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}

View file

@ -0,0 +1,3 @@
<h1>
<a href="{% url "home" %}">{{ SITE_NAME }}</a>
</h1>

View file

@ -40,24 +40,6 @@
!(function () { !(function () {
const ui = new L.U.UI(document.querySelector('header')) const ui = new L.U.UI(document.querySelector('header'))
const xhr = new L.U.Xhr(ui)
const login = document.querySelector('a.login')
if (login) {
L.DomEvent.on(login, 'click', function (e) {
L.DomEvent.stop(e)
xhr.login({
login_required: this.getAttribute('href'),
redirect: '/',
})
})
}
const logout = document.querySelector('a.logout')
if (logout) {
L.DomEvent.on(logout, 'click', function (e) {
L.DomEvent.stop(e)
xhr.logout(this.getAttribute('href'))
})
}
const getMore = function (e) { const getMore = function (e) {
L.DomEvent.stop(e) L.DomEvent.stop(e)
xhr._ajax({ xhr._ajax({

View file

@ -1,9 +1,7 @@
{% load i18n %} {% load i18n %}
<nav class="umap-nav"> <nav class="umap-nav">
<section> <section>
<h1> {% include "umap/branding.html" %}
<a href="{% url "home" %}">{{ title }}</a>
</h1>
</section> </section>
<section> <section>
<ul> <ul>

View file

@ -15,7 +15,6 @@ from . import views
from .decorators import ( from .decorators import (
can_edit_map, can_edit_map,
can_view_map, can_view_map,
jsonize_view,
login_required_if_not_anonymous_allowed, login_required_if_not_anonymous_allowed,
) )
from .utils import decorated_patterns from .utils import decorated_patterns
@ -50,7 +49,7 @@ urlpatterns = [
] ]
i18n_urls = [ i18n_urls = [
re_path(r"^login/$", jsonize_view(auth_views.LoginView.as_view()), name="login"), re_path(r"^login/$", auth_views.LoginView.as_view(), name="login"),
re_path( re_path(
r"^login/popup/end/$", views.LoginPopupEnd.as_view(), name="login_popup_end" r"^login/popup/end/$", views.LoginPopupEnd.as_view(), name="login_popup_end"
), ),