Unverified Commit 3731aa30 authored by Enrico Zini's avatar Enrico Zini
Browse files

More useful views for inspecting identities. Closes: #2

parents 4f809504 d0512a03
......@@ -127,14 +127,19 @@ class VisitPersonMixin(VisitorMixin):
"key": self.person.lookup_key}) + f"?url={self.request.build_absolute_uri()}",
_("Impersonate"), "random"))
if self.person.is_dd:
res.append(NavLink(reverse("mia_wat_ping", kwargs={"key": self.person.lookup_key}), _("WAT ping")))
res.append(NavLink(
reverse("mia_wat_ping", kwargs={"key": self.person.lookup_key}), _("WAT ping"), "heartbeat"))
from process.views import Emeritus
emeritus_link = Emeritus.get_nonauth_url(self.person, self.request)
if emeritus_link:
res.append(NavLink(emeritus_link, _("One click emeritus")))
res.append(NavLink(emeritus_link, _("One click emeritus"), "bed"))
if self.person.is_am:
res.append(NavLink(
reverse("person_amprofile", kwargs={"key": self.person.lookup_key}), _("AM Profile"), "gear"))
if "view_person_audit_log" in self.visit_perms:
res.append(NavLink(
reverse("signon_identities_person", kwargs={
"key": self.person.lookup_key}), _("Logins"), "sign-in"))
return res
def get_visit_perms(self):
......
{% load i18n %}
{% for identity in identities %}
<div class="card mt-4">
<div class="card-header">
{% with identity.get_provider as provider %}
{% if identity.profile %}<a href="{{identity.profile}}">{% endif %}
{% if provider.icon %}
<img style="vertical-align: text-top; height: 1em" class="mr-2" src="{{STATIC_URL}}{{provider.icon}}"></img>
{% else %}
<span class="mr-2 fa fa-sign-in"></span>
{% endif %}
{{provider.label}}:
{{identity.fullname}} &lt;{{identity.username}}&gt;
{% endwith %}
{% if identity.profile %}</a>{% endif %}
</div>
<div class="card-body">
<h5 class="card-title">
{% if identity.picture %}
<img class="personpic ml-auto" src="{{identity.picture}}"></img>
{% endif %}
{% trans "Account information" %}
</h5>
<table class="table table-sm">
<tr><th>{% trans "Last used" %}</th><td>{{identity.last_used|date:"Y-m-d"}}</td></tr>
{% if identity.subject %}
<tr><th>{% trans "Subject" %}</th><td>{{identity.subject}}</td></tr>
{% endif %}
{% if identity.fullname %}
<tr><th>{% trans "Full name" %}</th><td>{{identity.fullname}}</td></tr>
{% endif %}
{% if identity.username %}
<tr><th>{% trans "User name" %}</th><td>{{identity.username}}</td></tr>
{% endif %}
</table>
<h5 class="card-title">{% trans "Audit log" %}</h5>
<table class="table table-sm">
<thead>
<tr>
<th>{% trans "Date" %}</th>
<th>{% trans "Author" %}</th>
<th>{% trans "Notes" %}</th>
<th>{% trans "Changes" %}</th>
</tr>
</thead>
<tbody>
{% for e in identity.audit_log.all %}
<tr>
<td>{{e.logdate|date:"Y-m-d H:i:s"}}</td>
<td><a href="{{e.author.get_absolute_url}}">{{e.author}}</a></td>
<td>{{e.notes}}</td>
<td>
<ul>
{% for field, old, new in e.get_changes_list %}
<li>{{field}}: {{old}} → {{new}}</li>
{% endfor %}
</ul>
</td>
</tr>
{% empty %}
<tr><td colspan="4" class="text-center"><i>{% trans "No audit log for this identity" %}</i></td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endfor %}
{% extends "nm2-base.html" %}
{% load nm %}
{% load i18n %}
{% block content %}
<h1>{{person.fullname}} identities</h1>
{% if identities %}
{% include "signon/audit_identities.html" with identities=identities only %}
{% endif %}
{% endblock %}
......@@ -6,6 +6,7 @@
<h1>nm.debian.org login</h1>
{% if not impersonator %}
{% if not providers_active and not providers_inactive %}
<p class="lead">
{% blocktrans %}
......@@ -23,19 +24,21 @@
{% for provider in providers_active %}
<li class="row">
<div class="btn btn-info btn-lg disabled col-sm-6">
{% with provider.get_active_identity as identity %}
{% if provider.icon %}
<img style="vertical-align: text-top; height: 1em" class="mr-2" src="{{STATIC_URL}}{{provider.icon}}"></img>
{% else %}
<span class="mr-2 fa fa-sign-in"></span>
{% endif %}
{% if provider.profile %}
<a href="{{provider.profile}}">{{provider.label}}</a>
{% if identity.profile %}
<a href="{{identity.profile}}">{{provider.label}}</a>
{% else %}
{{provider.label}}
{% endif %}
{% if provider.username %}
({{provider.username}})
{% if identity.username %}
({{identity.username}})
{% endif %}
{% endwith %}
</div>
<div class="btn btn-light btn-lg disabled col-sm-6">
{% if provider.get_active_identity %}
......@@ -79,54 +82,13 @@
</ul>
{% endif %}
{% endif %}
{% else %}
<p class="lead">{% trans "Current login information not displayed while impersonating." %}</p>
{% endif %}
{% if identities %}
<h2>Identity audit logs</h2>
{% for identity in identities %}
<div class="card mt-4">
<div class="card-header">
{% with identity.get_provider as provider %}
{% if identity.profile %}<a href="{{identity.profile}}">{% endif %}
{% if provider.icon %}
<img style="vertical-align: text-top; height: 1em" class="mr-2" src="{{STATIC_URL}}{{provider.icon}}"></img>
{% else %}
<span class="mr-2 fa fa-sign-in"></span>
{% endif %}
{{provider.label}}:
{{identity.fullname}} &lt;{{identity.username}}&gt;
{% endwith %}
{% if identity.profile %}</a>{% endif %}
</div>
<table id="audit_log" class="table table-sm card-body mb-0">
<thead>
<tr>
<th>{% trans "Date" %}</th>
<th>{% trans "Author" %}</th>
<th>{% trans "Notes" %}</th>
<th>{% trans "Changes" %}</th>
</tr>
</thead>
<tbody>
{% for e in identity.audit_log.all %}
<tr>
<td>{{e.logdate|date:"Y-m-d H:i:s"}}</td>
<td><a href="{{e.author.get_absolute_url}}">{{e.author}}</a></td>
<td>{{e.notes}}</td>
<td>
<ul>
{% for field, old, new in e.get_changes_list %}
<li>{{field}}: {{old}} → {{new}}</li>
{% endfor %}
</ul>
</td>
</tr>
{% empty %}
<tr><td colspan="4" class="text-center"><i>{% trans "No audit log for this identity" %}</i></td></tr>
{% endfor %}
</tbody>
</table>
</div>
{% endfor %}
<h2>{% trans "Identity audit logs" %}</h2>
{% include "signon/audit_identities.html" with identities=identities only %}
{% endif %}
{% endblock %}
......@@ -3,6 +3,8 @@ from . import views
urlpatterns = [
path('login/', views.Login.as_view(), name='signon_login'),
path('identities/', views.Identities.as_view(), name='signon_identities'),
path('identities/<key>/', views.Identities.as_view(), name='signon_identities_person'),
path('logout/', views.Logout.as_view(), name='signon_logout'),
path('oidc/callback/<name>/', views.OIDCAuthenticationCallbackView.as_view(), name='signon_oidc_callback'),
]
......@@ -4,7 +4,7 @@ from django.views.generic import View, TemplateView
from django import http
from django.shortcuts import redirect
from django.contrib import auth
from backend.mixins import VisitorMixin
from backend.mixins import VisitorMixin, VisitPersonMixin
from backend.models import Person
from .models import Identity
from . import providers
......@@ -45,6 +45,16 @@ class Logout(View):
return redirect("home")
class Identities(VisitPersonMixin, TemplateView):
template_name = "signon/identities.html"
require_visit_perms = "view_person_audit_log"
def get_context_data(self, **kw):
ctx = super().get_context_data(**kw)
ctx["identities"] = self.person.identities.all()
return ctx
class OIDCAuthenticationCallbackView(View):
def get(self, request, *args, **kw):
name = self.kwargs["name"]
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment