Skip to content
Snippets Groups Projects
Verified Commit 5a5b1724 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Add login flow for Nextcloud Talk

parent 46bb7663
No related tags found
No related merge requests found
......@@ -132,3 +132,7 @@ class EditTermForm(forms.ModelForm):
class Meta:
model = SchoolTerm
fields = ["caption", "date_start", "date_end"]
class NextcloudServerForm(forms.Form):
url = forms.URLField(required=True, label=_("URL of your Nextcloud instance"))
......@@ -87,6 +87,15 @@ MENUS = {
"menu_generator.validators.is_superuser",
],
},
{
"name": _("Third-party services"),
"url": "third_party_services",
"icon": "share",
"validators": [
"menu_generator.validators.is_authenticated",
"menu_generator.validators.is_superuser",
],
},
{
"name": _("Backend Admin"),
"url": "admin:index",
......
......@@ -371,6 +371,9 @@ CONSTANCE_CONFIG = {
"IMPRINT_URL": ("", _("Link to imprint"), "url_field"),
"ADRESSING_NAME_FORMAT": ("german", _("Name format of adresses"), "adressing-select"),
"NOTIFICATION_CHANNELS": (["email"], _("Channels to allow for notifications"), "notifications-select"),
"NEXTCLOUD_TALK_SERVER": ("", _("Nextcloud Talk server configured by connection service"), "url_field"),
"NEXTCLOUD_TALK_LOGIN_NAME": ("", _("Nextcloud Talk server configured by connection service"), str),
"NEXTCLOUD_TALK_APP_PASSWORD": ("", _("Nextcloud Talk server configured by connection service"), str),
}
CONSTANCE_CONFIG_FIELDSETS = {
"General settings": ("SITE_TITLE",),
......
var POLL_INTERVAL = 500;
var w = null;
function poll() {
$.ajax({
url: Urls.pollNextcloudTalk(),
}).done(function (data) {
if (data.done) {
console.log("Polling done");
// Redirect to third-party services home view
window.location.href = Urls.thirdPartyServices();
w.close();
} else {
window.setTimeout(poll, POLL_INTERVAL);
}
}).fail(function () {
window.setTimeout(poll, POLL_INTERVAL);
})
}
$(document).ready(function () {
// Open login URL
var loginData = getJSONScript("login_data");
w = window.open(loginData.login);
console.log("Start polling");
poll();
});
{# -*- engine:django -*- #}
{% extends "core/base.html" %}
{% load i18n material_form static %}
{% block browser_title %}{% blocktrans %}Connect to Nextcloud Talk{% endblocktrans %}{% endblock %}
{% block page_title %}{% blocktrans %}Connect to Nextcloud Talk{% endblocktrans %}{% endblock %}
{% block content %}
{% if step == 1 %}
<form action="" method="post">
{% csrf_token %}
{% form form=form %}{% endform %}
<button class="btn green waves-effect waves-light" type="submit" name="step-1">
<i class="material-icons left">open_in_new</i>
{% trans "Open login page" %}
</button>
</form>
{% elif step == 2 %}
{{ login_data|json_script:"login_data" }}
<p class="flow-text">
{% blocktrans %}
Waiting for successful login ...
{% endblocktrans %}
</p>
<p>
{% blocktrans with url=login_data.login %}
Login window has not opened?
<a href="{{ url }}" target="_blank">Try again.</a>
{% endblocktrans %}
</p>
<script src="{% static "js/helper.js" %}"></script>
<script src="{% static "js/nextcloud_talk.js" %}"></script>
{% endif %}
{% endblock %}
{# -*- engine:django -*- #}
{% extends "core/base.html" %}
{% load i18n %}
{% block browser_title %}{% blocktrans %}Third-party services{% endblocktrans %}{% endblock %}
{% block page_title %}{% blocktrans %}Third-party services{% endblocktrans %}{% endblock %}
{% block content %}
<div class="card">
<div class="card-content">
{% if nextcloud.connected %}
<span class="badge new green right">{% trans "Connected" %}</span>
{% else %}
<span class="badge new red right">{% trans "Not connected" %}</span>
{% endif %}
<span class="card-title">{% trans "Nextcloud Talk" %}</span>
{% if nextcloud.connected %}
<p>
{% blocktrans with login_name=nextcloud.login_name %}
Connected Nextcloud user: {{ login_name }}
{% endblocktrans %}
</p>
<br/>
<a class="btn red waves-effect waves-light" href="{% url "disconnect_nextcloud_talk" %}">
<i class="material-icons left">leak_remove</i>
{% trans "Disconnect" %}
</a>
{% else %}
<p>
{% blocktrans %}
In order to send notifications by Nextcloud Talk you must connect a Nextcloud user which should send the
notifications to the users.
{% endblocktrans %}
</p>
<br/>
<a class="btn green waves-effect waves-light" href="{% url "connect_nextcloud_talk" %}">
<i class="material-icons left">leak_add</i>
{% trans "Connect" %}
</a>
{% endif %}
</div>
</div>
{% endblock %}
......@@ -19,6 +19,10 @@ urlpatterns = [
path("admin/", admin.site.urls),
path("data_management/", views.data_management, name="data_management"),
path("status/", views.system_status, name="system_status"),
path("third_party/", views.third_party_services, name="third_party_services"),
path("third_party/nextcloud_talk/", views.connect_nextcloud_talk, name="connect_nextcloud_talk"),
path("third_party/nextcloud_talk/disconnect/", views.disconnect_nextcloud_talk, name="disconnect_nextcloud_talk"),
path("third_party/nextcloud_talk/poll/", views.poll_nextcloud_talk, name="poll_nextcloud_talk"),
path("school_management", views.school_management, name="school_management"),
path("school/information/edit", views.edit_school, name="edit_school_information"),
path("school/term/edit", views.edit_schoolterm, name="edit_school_term"),
......
from typing import Union
import requests
from constance import config
HEADERS = {
"User-Agent": "AlekSIS",
}
INITIATE_LOGIN_PROCESS_URL = "index.php/login/v2"
def initiate_login_process(nextcloud_url: str) -> dict:
url = nextcloud_url + INITIATE_LOGIN_PROCESS_URL
r = requests.post(url, headers=HEADERS)
return r.json()
def login_process_poll(endpoint: str, token: str) -> Union[dict, bool]:
r = requests.post(endpoint, headers=HEADERS, data={"token": token})
if r.status_code != 200:
return False
return r.json()
def is_connected() -> bool:
return (
config.NEXTCLOUD_TALK_SERVER != ""
and config.NEXTCLOUD_TALK_LOGIN_NAME != ""
and config.NEXTCLOUD_TALK_APP_PASSWORD
)
from typing import Optional
from constance import config
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.http import Http404, HttpRequest, HttpResponse
from django.http import Http404, HttpRequest, HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.translation import ugettext_lazy as _
......@@ -15,10 +16,11 @@ from .forms import (
EditSchoolForm,
EditTermForm,
PersonsAccountsFormSet,
)
NextcloudServerForm)
from .models import Activity, Group, Notification, Person, School, DashboardWidget
from .tables import GroupsTable, PersonsTable
from .util import messages
from .util import messages, nextcloud
from .util.nextcloud import initiate_login_process, login_process_poll
@person_required
......@@ -264,3 +266,80 @@ def notification_mark_read(request: HttpRequest, id_: int) -> HttpResponse:
raise PermissionDenied(_("You are not allowed to mark notifications from other users as read!"))
return redirect("index")
@admin_required
def third_party_services(request: HttpRequest) -> HttpResponse:
context = {}
context["nextcloud"] = {
"connected": nextcloud.is_connected(),
"login_name": config.NEXTCLOUD_TALK_LOGIN_NAME
}
return render(request, "core/third_party_services.html", context)
@admin_required
def connect_nextcloud_talk(request: HttpRequest) -> HttpResponse:
context = {}
if request.method == "GET":
form = NextcloudServerForm()
context["step"] = 1
context["form"] = form
elif request.method == "POST":
if "step-1" in request.POST:
form = NextcloudServerForm(request.POST)
if form.is_valid():
url = form.cleaned_data["url"]
if url[-1] != "/":
url += "/"
r = initiate_login_process(url)
request.session["nextcloud_poll"] = True
request.session["nextcloud_login_data"] = r
context["login_data"] = r
context["step"] = 2
return render(request, "core/connect_nextcloud_talk.html", context)
@admin_required
def disconnect_nextcloud_talk(request: HttpRequest) -> HttpResponse:
config.NEXTCLOUD_TALK_SERVER = ""
config.NEXTCLOUD_TALK_LOGIN_NAME = ""
config.NEXTCLOUD_TALK_APP_PASSWORD = ""
messages.success(request, _("The Nextcloud user was successfully disconnected ."))
return redirect("third_party_services")
@admin_required
def poll_nextcloud_talk(request: HttpRequest) -> HttpResponse:
done = False
if request.session.get("nextcloud_poll", False):
login_data = request.session["nextcloud_login_data"]
r = login_process_poll(login_data["poll"]["endpoint"], login_data["poll"]["token"])
done = r != False
if done:
config.NEXTCLOUD_TALK_SERVER = r["server"]
config.NEXTCLOUD_TALK_LOGIN_NAME = r["loginName"]
config.NEXTCLOUD_TALK_APP_PASSWORD = r["appPassword"]
messages.success(request, _("The Nextcloud user was successfully connected."))
del request.session["nextcloud_login_data"]
del request.session["nextcloud_poll"]
return JsonResponse({"done": done})
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment