From 8c4cdc81f993bcf4d088d2356a45da8d883bdf01 Mon Sep 17 00:00:00 2001 From: Andrea Lepori Date: Sun, 2 Jan 2022 21:00:12 +0100 Subject: login/register with midata --- accounts/templates/accounts/index.html | 33 ++ accounts/urls.py | 2 + accounts/views.py | 82 ++- client/migrations/0010_auto_20220102_1933.py | 23 + client/models.py | 2 + client/templates/client/index.html | 730 +++++++++++++-------------- client/views.py | 196 +++---- manager/settings.py | 1 + static/pbs_logo.svg | 1 + templates/registration/base_client.html | 36 +- templates/registration/login.html | 24 +- version.txt | 2 +- 12 files changed, 631 insertions(+), 501 deletions(-) create mode 100644 client/migrations/0010_auto_20220102_1933.py create mode 100644 static/pbs_logo.svg diff --git a/accounts/templates/accounts/index.html b/accounts/templates/accounts/index.html index 0af8ce1..874231b 100644 --- a/accounts/templates/accounts/index.html +++ b/accounts/templates/accounts/index.html @@ -1,4 +1,5 @@ {% extends 'registration/base_client.html' %} +{% load static %} {% block title %}Profilo{% endblock %} @@ -10,6 +11,7 @@ {% endblock%} @@ -312,6 +314,37 @@ +
+
+
+
+
+
Collegamento con MiData
+
+
+
+
+ Collega il tuo account con MiData per avere un login unico. Attenzione una volta collegato il + tuo account i dati presenti su MiData dovranno essere modificati sulla piattaforma stessa. +
+
+ +
+
+
{% endblock %} {% block script %} diff --git a/accounts/urls.py b/accounts/urls.py index 15d8124..5f04051 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -8,4 +8,6 @@ urlpatterns = [ path('terms/', views.terms, name='terms'), path('oauth_login/', views.oauth_login, name='oauth_login'), path('auth/', views.auth, name='auth'), + path('oauth_connect/', views.oauth_connect, name='oauth_connect'), + path('auth_connect/', views.auth_connect, name='auth_connect'), ] diff --git a/accounts/views.py b/accounts/views.py index ef2d6c1..06459d7 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -1,21 +1,23 @@ from django.shortcuts import render from django.urls import reverse +from django.shortcuts import redirect from django.conf import settings -from django.contrib.auth.views import LoginView from django.contrib.auth.forms import UserCreationForm +from django.contrib.auth.models import User from django.contrib.auth import login, authenticate from django.http import FileResponse from django.contrib.auth.decorators import login_required from django.views.decorators.debug import sensitive_variables from django.http import HttpResponseRedirect -from client.models import UserCode +from client.models import UserCode, MedicalData from authlib.integrations.django_client import OAuth import dateparser import os import requests +from random import randint from io import BytesIO from PIL import Image, UnidentifiedImageError from pdf2image import convert_from_bytes @@ -24,8 +26,23 @@ from pdf2image.exceptions import ( PDFSyntaxError ) -oauth = OAuth() +def update_token(name, token, refresh_token=None, access_token=None): + if refresh_token: + item = OAuth2Token.find(name=name, refresh_token=refresh_token) + elif access_token: + item = OAuth2Token.find(name=name, access_token=access_token) + else: + return + + # update old token + item.access_token = token['access_token'] + item.refresh_token = token.get('refresh_token') + item.expires_at = token['expires_at'] + item.save() + +oauth = OAuth(update_token=update_token) hitobito = oauth.register(name="hitobito") +api_url = settings.AUTHLIB_OAUTH_CLIENTS["hitobito"]["api_url"] # override to remove help text class RegisterForm(UserCreationForm): @@ -35,20 +52,71 @@ class RegisterForm(UserCreationForm): for fieldname in ['username', 'password1', 'password2']: self.fields[fieldname].help_text = None +# send to hitobito request to get token def oauth_login(request): redirect_uri = request.build_absolute_uri(reverse('auth')) return hitobito.authorize_redirect(request, redirect_uri) +# callback after acquiring token def auth(request): token = hitobito.authorize_access_token(request) - print(token) + + # request data from user account + headers = { + "Authorization" : "Bearer " + token["access_token"], + "X-Scope": "with_roles", + } + resp = requests.get(api_url, headers=headers) + resp_data = resp.json() + + # find user with that id + usercode = UserCode.objects.filter(midata_id=resp_data["id"]) + + if len(usercode) > 0: + # user exist + login(request, usercode[0].user) + return HttpResponseRedirect('/') + + user = User.objects.create_user(resp_data["email"], resp_data["email"]) + + # create new usercode + while (True): + code = randint(100000, 999999) + if len(UserCode.objects.filter(code=code)) == 0: + break + + medic = MedicalData() + medic.save() + userCode = UserCode(user=user, code=code, medic=medic, midata_id=resp_data["id"], midata_token=token["access_token"]) + userCode.save() + + login(request, user) + + return HttpResponseRedirect('/') + +# send to hitobito request to get token +def oauth_connect(request): + redirect_uri = request.build_absolute_uri(reverse('auth_connect')) + return hitobito.authorize_redirect(request, redirect_uri) + +# callback after acquiring token +def auth_connect(request): + token = hitobito.authorize_access_token(request) + + # request data from user account headers = { "Authorization" : "Bearer " + token["access_token"], "X-Scope": "with_roles", } - resp = requests.get("https://demo.hitobito.com/oauth/profile", headers=headers) - print(resp) - print(resp.text) + resp = requests.get(api_url, headers=headers) + resp_data = resp.json() + + # find user with that id + usercode = UserCode.objects.filter(user=user)[0] + usercode.midata_id = resp_data["id"] + usercode.midata_token = token["access_token"] + usercode.save() + return HttpResponseRedirect('/') @sensitive_variables("raw_passsword") diff --git a/client/migrations/0010_auto_20220102_1933.py b/client/migrations/0010_auto_20220102_1933.py new file mode 100644 index 0000000..45d6f08 --- /dev/null +++ b/client/migrations/0010_auto_20220102_1933.py @@ -0,0 +1,23 @@ +# Generated by Django 3.1.4 on 2022-01-02 18:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('client', '0009_merge_20210723_1805'), + ] + + operations = [ + migrations.AddField( + model_name='usercode', + name='midata_id', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='usercode', + name='midata_token', + field=models.CharField(default='', max_length=1024), + ), + ] diff --git a/client/models.py b/client/models.py index db31194..b94806b 100644 --- a/client/models.py +++ b/client/models.py @@ -113,6 +113,8 @@ class UserCode(models.Model): school = models.CharField(default="", max_length=250) year = models.IntegerField(default=0) avs_number = models.CharField(default="", max_length=250) + midata_id = models.IntegerField(default=0) + midata_token = models.CharField(default="", max_length=1024) class GroupSettings(models.Model): group = models.ForeignKey(Group, default=None, on_delete=models.CASCADE) diff --git a/client/templates/client/index.html b/client/templates/client/index.html index ae3392f..e31ce34 100644 --- a/client/templates/client/index.html +++ b/client/templates/client/index.html @@ -16,400 +16,388 @@ -{% if user.is_authenticated %} - {% if user.is_staff or perms.client.approved %} - + + + + + {% endfor %} + {% endif %} +
+ + add + +
{% else %}
-

Se hai già un account clicca login in alto a destra. Altrimenti clicca registrazione

+

Il tuo utente non è ancora stato approvato.

+
+ {% if user_code %} +

Per approvare l'utente invia il seguente codice al capo branca:

+

{{user_code}}

+ {% else %} +

Per approvare l'utente inserire prima i propri dati personali e medici cliccando il proprio nome in alto a destra. Torna in seguito su questa pagina per i prossimi passaggi.

+

Per approvare l'utente inserire prima i propri dati personali e medici cliccando l'icona person in alto. Torna in seguito su questa pagina per i prossimi passaggi.

+ {% endif %}

@@ -419,7 +407,6 @@ {% block script %} $(document).ready(function(){ - {% if user.is_authenticated %} {% if user.is_staff or perms.client.approved %} $('.collapsible').collapsible(); $('.tap-target').tapTarget(); @@ -428,22 +415,19 @@ $(document).ready(function(){ $('.tap-target').tapTarget('open'); {% endif %} {% endif %} - {% endif %} lazyload(); }); -{% if user.is_authenticated %} - {% if user.is_staff or perms.client.approved %} - $('*').click(function(event) { - if (this === event.target) { - $('.tap-target').tapTarget('close'); - } - }); - function send(id) { - var form = document.getElementById('form') - var action = document.getElementById('action') - action.setAttribute('value', id); - form.submit() - } - {% endif %} +{% if user.is_staff or perms.client.approved %} + $('*').click(function(event) { + if (this === event.target) { + $('.tap-target').tapTarget('close'); + } + }); + function send(id) { + var form = document.getElementById('form') + var action = document.getElementById('action') + action.setAttribute('value', id); + form.submit() + } {% endif %} {% endblock %} diff --git a/client/views.py b/client/views.py index bc0b071..41bb3dd 100644 --- a/client/views.py +++ b/client/views.py @@ -12,109 +12,109 @@ from datetime import datetime import pytz from random import randint +@login_required() def index(request): context = {} group_view = False - # check if user is logged - if (request.user.is_authenticated): - if not (request.user.is_staff or request.user.has_perm("client.approved")): - # generate code if user has no code - users = UserCode.objects.filter(user=request.user) - code = None - if (len(users) == 0): - while (True): - code = randint(100000, 999999) - if len(UserCode.objects.filter(code=code)) == 0: - break - medic = MedicalData() - medic.save() - userCode = UserCode(user=request.user, code=code, medic=medic) - userCode.save() - - user_code = None - usercode = UserCode.objects.filter(user=request.user)[0] - if request.user.first_name != "" and request.user.last_name != "" and request.user.email != "" and usercode.phone != "": - user_code = "U" + str(usercode.code) - context = {"user_code": user_code} - else: - # get user group - groups = request.user.groups.all() - - # check if any group has enabled RO documents - if request.user.is_staff or len(groups.filter(name="capi")) == 0: - # if user is staff then not needed - gr = [] - elif request.user.has_perm("client.staff"): - gr = GroupSettings.objects.filter(group__in=groups).filter(view_documents=True).filter(~Q(group=groups[0])) - else: - gr = GroupSettings.objects.filter(group__in=groups).filter(view_documents=True) - - group_view = len(gr) != 0 - - # user action - if request.method == "POST": - # get document id - document = Document.objects.get(id=request.POST["action"][1:]) - - # check if document is valid to modify - if document.user != request.user: - return - - if document.status == "ok" or document.status == "archive": - return - - # execute action - if request.POST["action"][0] == 'f': - # generate approve pdf - template = get_template('client/approve_doc_pdf.html') - context = {'doc': document} - html = template.render(context) - pdf = pdfkit.from_string(html, False) - result = BytesIO(pdf) - result.seek(0) - return FileResponse(result, filename=document.document_type.name+".pdf") - elif request.POST["action"][0] == 'a': - # sign autosign doc - if document.status == "autosign": - document.status = "ok" - document.save() - return HttpResponseRedirect("/") - elif request.POST["action"][0] == 'd': - # delete doc - document.delete() - return HttpResponseRedirect("/") - elif request.POST["action"][0] == 'e': - # edit doc generate context and render edit page - document_type = document.document_type - context = { - 'doctype': document_type, - } - context['doc'] = document - context['personal_data'] = document_type.personal_data - context['medical_data'] = document_type.medical_data - context['custom_data'] = document_type.custom_data - context['keys'] = KeyVal.objects.filter(container=document) - context['custom_message'] = document_type.custom_message - context['custom_message_text'] = document_type.custom_message_text - return edit_wrapper(request, context) - - # show only docs of the user and non archived - documents = Document.objects.filter( - Q(user=request.user) & ~Q(status='archive')).select_related("personal_data", "medical_data", "document_type", "user") - - vac_file = ["/server/media/", "/vac_certificate/doc"] - health_file = ["/server/media/", "/health_care_certificate/doc"] - sign_doc_file = ["/server/media/", "/signed_doc/doc"] - + if not (request.user.is_staff or request.user.has_perm("client.approved")): + # generate code if user has no code + users = UserCode.objects.filter(user=request.user) + code = None + if (len(users) == 0): + while (True): + code = randint(100000, 999999) + if len(UserCode.objects.filter(code=code)) == 0: + break + medic = MedicalData() + medic.save() + userCode = UserCode(user=request.user, code=code, medic=medic) + userCode.save() + + user_code = None + usercode = UserCode.objects.filter(user=request.user)[0] + if request.user.first_name != "" and request.user.last_name != "" and request.user.email != "" and usercode.phone != "": + user_code = "U" + str(usercode.code) + context = {"user_code": user_code} + return render(request, 'client/index.html', context) + + # get user group + groups = request.user.groups.all() + + # check if any group has enabled RO documents + if request.user.is_staff or len(groups.filter(name="capi")) == 0: + # if user is staff then not needed + gr = [] + elif request.user.has_perm("client.staff"): + gr = GroupSettings.objects.filter(group__in=groups).filter(view_documents=True).filter(~Q(group=groups[0])) + else: + gr = GroupSettings.objects.filter(group__in=groups).filter(view_documents=True) + + group_view = len(gr) != 0 + + # user action + if request.method == "POST": + # get document id + document = Document.objects.get(id=request.POST["action"][1:]) + + # check if document is valid to modify + if document.user != request.user: + return + + if document.status == "ok" or document.status == "archive": + return + + # execute action + if request.POST["action"][0] == 'f': + # generate approve pdf + template = get_template('client/approve_doc_pdf.html') + context = {'doc': document} + html = template.render(context) + pdf = pdfkit.from_string(html, False) + result = BytesIO(pdf) + result.seek(0) + return FileResponse(result, filename=document.document_type.name+".pdf") + elif request.POST["action"][0] == 'a': + # sign autosign doc + if document.status == "autosign": + document.status = "ok" + document.save() + return HttpResponseRedirect("/") + elif request.POST["action"][0] == 'd': + # delete doc + document.delete() + return HttpResponseRedirect("/") + elif request.POST["action"][0] == 'e': + # edit doc generate context and render edit page + document_type = document.document_type context = { - "docs": documents, - "base_group": groups[0].name, - "empty": len(documents) == 0, - "group_view": group_view, - "vac_file": vac_file, - "health_file": health_file, - "sign_doc_file": sign_doc_file + 'doctype': document_type, } + context['doc'] = document + context['personal_data'] = document_type.personal_data + context['medical_data'] = document_type.medical_data + context['custom_data'] = document_type.custom_data + context['keys'] = KeyVal.objects.filter(container=document) + context['custom_message'] = document_type.custom_message + context['custom_message_text'] = document_type.custom_message_text + return edit_wrapper(request, context) + + # show only docs of the user and non archived + documents = Document.objects.filter( + Q(user=request.user) & ~Q(status='archive')).select_related("personal_data", "medical_data", "document_type", "user") + + vac_file = ["/server/media/", "/vac_certificate/doc"] + health_file = ["/server/media/", "/health_care_certificate/doc"] + sign_doc_file = ["/server/media/", "/signed_doc/doc"] + + context = { + "docs": documents, + "base_group": groups[0].name, + "empty": len(documents) == 0, + "group_view": group_view, + "vac_file": vac_file, + "health_file": health_file, + "sign_doc_file": sign_doc_file + } return render(request, 'client/index.html', context) diff --git a/manager/settings.py b/manager/settings.py index 358b0b7..b922de7 100644 --- a/manager/settings.py +++ b/manager/settings.py @@ -79,6 +79,7 @@ AUTHLIB_OAUTH_CLIENTS = { 'refresh_token_url': None, 'authorize_url': 'https://demo.hitobito.com/oauth/authorize', 'authorize_params': None, + 'api_url': "https://demo.hitobito.com/oauth/profile", 'client_kwargs': {"grant_type": "authorization_code", "scope": "with_roles"}, } } diff --git a/static/pbs_logo.svg b/static/pbs_logo.svg new file mode 100644 index 0000000..2c987de --- /dev/null +++ b/static/pbs_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/registration/base_client.html b/templates/registration/base_client.html index 3e78397..d90462c 100644 --- a/templates/registration/base_client.html +++ b/templates/registration/base_client.html @@ -90,28 +90,22 @@ {% block breadcrumb %} {% endblock %}
{% block toolbar %} diff --git a/templates/registration/login.html b/templates/registration/login.html index 5a24448..8a2b258 100644 --- a/templates/registration/login.html +++ b/templates/registration/login.html @@ -1,4 +1,5 @@ {% extends 'registration/base_simple.html' %} +{% load static %} {% block title %}Login{% endblock %} @@ -14,8 +15,29 @@ Password dimenticata

- OAuth +



+
+ +
+
+ +
diff --git a/version.txt b/version.txt index 76e6671..0e21dbb 100644 --- a/version.txt +++ b/version.txt @@ -1,2 +1,2 @@ version=0.4 -rev=7 +rev=8 -- cgit v1.2.1