Auth log
Some checks failed
API CI / api-tests (pull_request) Failing after 30s
CI / test (pull_request) Failing after 32s

This commit is contained in:
Tim Lorsbach
2026-07-23 23:30:05 +02:00
parent 3241bc2648
commit aaa87c5f2f
4 changed files with 81 additions and 10 deletions

View File

@ -1,3 +1,5 @@
import logging
import msal import msal
from django.conf import settings as s from django.conf import settings as s
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
@ -8,6 +10,7 @@ from django.shortcuts import redirect
from epdb.logic import UserManager, GroupManager from epdb.logic import UserManager, GroupManager
from epdb.models import Group from epdb.models import Group
auth_log = logging.getLogger("auth")
def get_msal_app_with_cache(request): def get_msal_app_with_cache(request):
""" """
@ -28,8 +31,21 @@ def get_msal_app_with_cache(request):
return msal_app, cache return msal_app, cache
def get_remote_address(request):
remote_address = ""
if request is not None:
remote_address = request.META.get("HTTP_X_FORWARDED_FOR")
if not remote_address:
remote_address = request.META.get("REMOTE_ADDR", "")
return remote_address
def entra_login(request): def entra_login(request):
auth_log.info(f"Login request from {get_remote_address(request)}")
msal_app = msal.ConfidentialClientApplication( msal_app = msal.ConfidentialClientApplication(
client_id=s.MS_ENTRA_CLIENT_ID, client_id=s.MS_ENTRA_CLIENT_ID,
client_credential=s.MS_ENTRA_CLIENT_SECRET, client_credential=s.MS_ENTRA_CLIENT_SECRET,
@ -54,6 +70,10 @@ def entra_callback(request):
# Acquire token using the flow and callback request # Acquire token using the flow and callback request
result = msal_app.acquire_token_by_auth_code_flow(flow, request.GET) result = msal_app.acquire_token_by_auth_code_flow(flow, request.GET)
if "error" in result:
auth_log.error(f"Login attempt by {get_remote_address(request)} failed due to {result['error']}")
return redirect("/")
# Save the token cache to session # Save the token cache to session
if cache.has_state_changed: if cache.has_state_changed:
request.session["msal_token_cache"] = cache.serialize() request.session["msal_token_cache"] = cache.serialize()
@ -61,7 +81,8 @@ def entra_callback(request):
claims = result["id_token_claims"] claims = result["id_token_claims"]
user_name = claims.get("name") user_name = claims.get("name")
user_email = claims.get("emailaddress", claims.get("email")) # preferred_username is a fallback for 2nd CWID
user_email = claims.get("emailaddress", claims.get("email", claims.get("preferred_username")))
user_oid = claims.get("oid") user_oid = claims.get("oid")
if not all([user_name, user_email, user_oid]): if not all([user_name, user_email, user_oid]):
@ -78,8 +99,10 @@ def entra_callback(request):
u.save() u.save()
else: else:
auth_log.info(f"Registering {user_name} with OID {user_oid}")
u = UserManager.create_user(user_name, user_email, None, uuid=user_oid, is_active=True) u = UserManager.create_user(user_name, user_email, None, uuid=user_oid, is_active=True)
auth_log.info(f"User {user_name} {"(admin)" if u.is_superuser else ""} with OID {user_oid} successfully logged in as {u.username} from {get_remote_address(request)}")
login(request, u) login(request, u)
# EDIT START # EDIT START
@ -102,10 +125,20 @@ def entra_callback(request):
else: else:
g = Group.objects.get(uuid=id) g = Group.objects.get(uuid=id)
for group_uuid in claims.get("groups", []): sync_groups = list(s.ENTRA_GROUPS.keys()) + list(s.ENTRA_SECRET_GROUPS.keys())
if Group.objects.filter(uuid=group_uuid).exists(): user_groups = claims.get("groups", [])
g = Group.objects.get(uuid=group_uuid)
g.user_member.add(u) for uuid in sync_groups:
if uuid in user_groups:
g = Group.objects.get(uuid=uuid)
if not g.user_member.contains(u):
g.user_member.add(u)
auth_log.info(f"Login Group Sync: Adding {u.username} to Group {g.name} ({ uuid })")
else:
g = Group.objects.get(uuid=uuid)
if g.user_member.contains(u):
g.user_member.remove(u)
auth_log.info(f"Login Group Sync: Removing {u.username} from Group {g.name} ({ uuid })")
# EDIT END # EDIT END

View File

@ -35,6 +35,7 @@ from utilities.chem import FormatConverter
from utilities.misc import PackageExporter, PackageImporter from utilities.misc import PackageExporter, PackageImporter
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
auth_log = logging.getLogger("auth")
Package = s.GET_PACKAGE_MODEL() Package = s.GET_PACKAGE_MODEL()
@ -316,13 +317,19 @@ class GroupManager(object):
if isinstance(member, Group): if isinstance(member, Group):
if add_or_remove == "add": if add_or_remove == "add":
group.group_member.add(member) group.group_member.add(member)
auth_log.info(f"{caller.username} ({caller.url}) adds {member.name} ({member.url}) to {group.name} ({group.url})")
else: else:
group.group_member.remove(member) group.group_member.remove(member)
auth_log.info(
f"{caller.username} ({caller.url}) removes {member.name} ({member.url}) to {group.name} ({group.url})")
else: else:
if add_or_remove == "add": if add_or_remove == "add":
group.user_member.add(member) group.user_member.add(member)
auth_log.info(f"{caller.username} ({caller.url}) adds {member.username} ({member.url}) to {group.name} ({group.url})")
else: else:
group.user_member.remove(member) group.user_member.remove(member)
auth_log.info(
f"{caller.username} ({caller.url}) adds {member.username} ({member.url}) to {group.name} ({group.url})")
group.save() group.save()
@ -589,11 +596,25 @@ class PackageManager(object):
if qs.count() != 0: if qs.count() != 0:
logger.info(f"Deleting Perm {qs.first()}") logger.info(f"Deleting Perm {qs.first()}")
qs.delete() qs.delete()
auth_log.info(f"{caller.username} ({caller.url}) revokes {grantee.name} ({grantee.url}) all Permissions on {package.name} ({package.url})")
else: else:
logger.debug(f"No Permission object for {perm_cls} with filter {data} found!") logger.debug(f"No Permission object for {perm_cls} with filter {data} found!")
else: else:
old_perm = None
old_perms_qs = perm_cls.objects.filter(**data)
if old_perms_qs.exists():
old_perm = old_perms_qs.first().permission
_ = perm_cls.objects.update_or_create(defaults={"permission": new_perm}, **data) _ = perm_cls.objects.update_or_create(defaults={"permission": new_perm}, **data)
grantee_name = grantee.username if isinstance(grantee, User) else grantee.name
if old_perm is None:
auth_log.info(f"{caller.username} ({caller.url}) grants {grantee_name} ({grantee.url}) '{new_perm}' Permissions on {package.name} ({package.url})")
else:
auth_log.info(f"{caller.username} ({caller.url}) set {grantee_name} ({grantee.url}) Permissions from '{old_perm}' to '{new_perm}' on {package.name} ({package.url})")
@staticmethod @staticmethod
def grant_read(caller: User, package: Package, grantee: Union[User, Group]): def grant_read(caller: User, package: Package, grantee: Union[User, Group]):
PackageManager.update_permissions(caller, package, grantee, Permission.READ[0]) PackageManager.update_permissions(caller, package, grantee, Permission.READ[0])

View File

@ -61,6 +61,7 @@ from .models import (
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
auth_log = logging.getLogger("auth")
Package = s.GET_PACKAGE_MODEL() Package = s.GET_PACKAGE_MODEL()
@ -147,6 +148,19 @@ def handler500(request):
return render(request, "errors/error.html", context, status=500) return render(request, "errors/error.html", context, status=500)
def delete_with_log(request, obj):
caller = request.user
obj_type = obj.__class__.__name__
try:
obj.delete()
except Exception as e:
logger.info(f"Tried to delete {obj_type}: {obj.name} ({obj.url}) but deletion failed! Exception {e}")
auth_log.error(
f"{caller.username} ({caller.url}) tried to delete {obj_type}: {obj.name} ({obj.url}) but deletion failed!")
raise e
def login(request): def login(request):
context = get_base_context(request) context = get_base_context(request)
@ -1282,7 +1296,8 @@ def package(request, package_uuid):
"You cannot delete the default package. If you want to delete this package you have to set another default package first.", "You cannot delete the default package. If you want to delete this package you have to set another default package first.",
) )
logger.debug(current_package.delete()) delete_with_log(request, current_package)
return redirect(s.SERVER_URL + "/package") return redirect(s.SERVER_URL + "/package")
elif hidden == "publish-package": elif hidden == "publish-package":
for g in Group.objects.filter(public=True): for g in Group.objects.filter(public=True):
@ -2273,7 +2288,7 @@ def package_pathway(request, package_uuid, pathway_uuid):
elif request.method == "POST": elif request.method == "POST":
if hidden := request.POST.get("hidden", None): if hidden := request.POST.get("hidden", None):
if hidden == "delete": if hidden == "delete":
current_pathway.delete() delete_with_log(request, current_pathway)
return redirect(current_package.url + "/pathway") return redirect(current_package.url + "/pathway")
else: else:
return HttpResponseBadRequest() return HttpResponseBadRequest()
@ -2491,7 +2506,7 @@ def package_pathway_node(request, package_uuid, pathway_uuid, node_uuid):
if hidden := request.POST.get("hidden", None): if hidden := request.POST.get("hidden", None):
if hidden == "delete": if hidden == "delete":
# pre_delete signal will take care of edge deletion # pre_delete signal will take care of edge deletion
current_node.delete() delete_with_log(request, current_node)
return redirect(current_pathway.url) return redirect(current_pathway.url)
else: else:
@ -2645,7 +2660,7 @@ def package_pathway_edge(request, package_uuid, pathway_uuid, edge_uuid):
if hidden := request.POST.get("hidden", None): if hidden := request.POST.get("hidden", None):
if hidden == "delete": if hidden == "delete":
current_edge.delete() delete_with_log(request, current_edge)
return redirect(current_pathway.url) return redirect(current_pathway.url)
if "selected-scenarios" in request.POST: if "selected-scenarios" in request.POST:
@ -3007,7 +3022,7 @@ def group(request, group_uuid):
if hidden := request.POST.get("hidden", None): if hidden := request.POST.get("hidden", None):
if hidden == "delete": if hidden == "delete":
current_group.delete() delete_with_log(request, current_group)
return redirect(s.SERVER_URL + "/group") return redirect(s.SERVER_URL + "/group")
else: else:
return HttpResponseBadRequest() return HttpResponseBadRequest()

View File

@ -15,6 +15,7 @@
<i class="glyphicon glyphicon-user"></i> Edit Permissions</a <i class="glyphicon glyphicon-user"></i> Edit Permissions</a
> >
</li> </li>
{% if meta.current_package.get_classification_level_display != "Secret" %}
<li> <li>
<a <a
role="button" role="button"
@ -23,6 +24,7 @@
<i class="glyphicon glyphicon-bullhorn"></i> Publish Package</a <i class="glyphicon glyphicon-bullhorn"></i> Publish Package</a
> >
</li> </li>
{% endif %}
{% endif %} {% endif %}
<li> <li>
<a <a