# OAuth Propagation

* adds user propagation without django-simple-sso
This commit is contained in:
2024-05-10 10:40:19 +02:00
parent 8ff3cb9adc
commit 572348f9f1
6 changed files with 83 additions and 4 deletions

7
user/views/__init__.py Normal file
View File

@@ -0,0 +1,7 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 10.05.24
"""

69
user/views/propagate.py Normal file
View File

@@ -0,0 +1,69 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 10.05.24
"""
import base64
import hashlib
import json
from cryptography.fernet import Fernet
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpRequest, JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from konova.sub_settings.sso_settings import OAUTH_CLIENT_ID
from user.models import User
class PropagateUserView(View):
""" Receives user data to be stored in db
Used if new user gets access to application and needs to be created in application before first login (e.g.
proper rights management)
"""
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def post(self, request: HttpRequest, *args, **kwargs):
# Decrypt
encrypted_body = request.body
hash = hashlib.md5()
hash.update(OAUTH_CLIENT_ID.encode("utf-8"))
key = base64.urlsafe_b64encode(hash.hexdigest().encode("utf-8"))
fernet = Fernet(key)
body = fernet.decrypt(encrypted_body).decode("utf-8")
body = json.loads(body)
try:
status = "updated"
user = User.objects.get(username=body.get('username'))
# Update user data, excluding some changes
skipable_attrs = {
"username",
"is_staff",
"is_superuser",
}
for _attr, _val in body.items():
if _attr in skipable_attrs:
continue
setattr(user, _attr, _val)
except ObjectDoesNotExist:
user = User(**body)
status = "created"
user.set_unusable_password()
user.save()
data = {
"success": True,
"status": status
}
return JsonResponse(data)

237
user/views/views.py Normal file
View File

@@ -0,0 +1,237 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.urls import reverse
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.mailer import Mailer
from konova.utils.message_templates import FORM_INVALID
from user.forms.modals.team import NewTeamModalForm, EditTeamModalForm, RemoveTeamModalForm, LeaveTeamModalForm
from user.forms.modals.user import UserContactForm
from user.forms.team import TeamDataForm
from user.forms.user import UserNotificationForm, UserAPITokenForm
from user.models import User, Team
from django.http import HttpRequest, Http404
from django.shortcuts import render, redirect, get_object_or_404
from django.utils.translation import gettext_lazy as _
from konova.contexts import BaseContext
from konova.decorators import any_group_check, default_group_required, login_required_modal
@login_required
@any_group_check
def index_view(request: HttpRequest):
""" Renders the user's data index view
Args:
request ():
Returns:
"""
template = "user/index.html"
context = {
"user": request.user,
TAB_TITLE_IDENTIFIER: _("User settings"),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
@any_group_check
def notifications_view(request: HttpRequest):
""" Renders the notifications settings view
Args:
request ():
Returns:
"""
template = "user/notifications.html"
user = request.user
form = UserNotificationForm(user=user, data=request.POST or None)
if request.method == "POST":
if form.is_valid():
form.save()
messages.success(
request,
_("Notifications edited")
)
return redirect("user:index")
elif request.method == "GET":
# Implicit
pass
else:
raise NotImplementedError
context = {
"user": user,
"form": form,
TAB_TITLE_IDENTIFIER: _("User notifications"),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
@default_group_required
def api_token_view(request: HttpRequest):
""" Handles the request for user api frontend settings
Args:
request (HttpRequest): The incoming request
Returns:
"""
template = "user/token.html"
user = request.user
form = UserAPITokenForm(request.POST or None, instance=user)
if request.method == "POST":
if form.is_valid():
token = form.save()
messages.info(request, _("New token generated. Administrators need to validate."))
mailer = Mailer()
mailer.send_mail_verify_api_token(user)
return redirect("user:api-token")
else:
messages.error(request, FORM_INVALID, extra_tags="danger")
elif request.method != "GET":
raise NotImplementedError
context = {
"user": user,
"form": form,
TAB_TITLE_IDENTIFIER: _("User API token"),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required_modal
@login_required
def contact_view(request: HttpRequest, id: str):
""" Renders contact modal view of a users contact data
Args:
request (HttpRequest): The incoming request
id (str): The user's id
Returns:
"""
user = get_object_or_404(User, id=id)
form = UserContactForm(request.POST or None, instance=user, request=request)
template = "modal/modal_form.html"
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(
request,
template,
context
)
@login_required_modal
@login_required
def data_team_view(request: HttpRequest, id: str):
""" Renders team data
Args:
request (HttpRequest): The incoming request
id (str): The team's id
Returns:
"""
team = get_object_or_404(Team, id=id)
form = TeamDataForm(request.POST or None, instance=team, request=request)
template = "modal/modal_form.html"
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(
request,
template,
context
)
@login_required
def index_team_view(request: HttpRequest):
template = "user/team/index.html"
user = request.user
context = {
"teams": user.shared_teams,
"tab_title": _("Teams"),
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required_modal
@login_required
def new_team_view(request: HttpRequest):
form = NewTeamModalForm(request.POST or None, request=request)
return form.process_request(
request,
_("New team added"),
redirect_url=reverse("user:team-index")
)
@login_required_modal
@login_required
def edit_team_view(request: HttpRequest, id: str):
team = get_object_or_404(Team, id=id)
user_is_admin = team.is_user_admin(request.user)
if not user_is_admin:
raise Http404()
form = EditTeamModalForm(request.POST or None, instance=team, request=request)
return form.process_request(
request,
_("Team edited"),
redirect_url=reverse("user:team-index")
)
@login_required_modal
@login_required
def remove_team_view(request: HttpRequest, id: str):
team = get_object_or_404(Team, id=id)
user_is_admin = team.is_user_admin(request.user)
if not user_is_admin:
raise Http404()
form = RemoveTeamModalForm(request.POST or None, instance=team, request=request)
return form.process_request(
request,
_("Team removed"),
redirect_url=reverse("user:team-index")
)
@login_required_modal
@login_required
def leave_team_view(request: HttpRequest, id: str):
team = get_object_or_404(Team, id=id)
user = request.user
is_user_team_member = team.users.filter(id=user.id).exists()
if not is_user_team_member:
messages.info(
request,
_("You are not a member of this team")
)
return redirect("user:team-index")
form = LeaveTeamModalForm(request.POST or None, instance=team, request=request)
return form.process_request(
request,
_("Left Team"),
redirect_url=reverse("user:team-index")
)