auth_saml: move related object code to authenticators app (#53442)
This commit is contained in:
parent
700a5bb196
commit
b24fad1bd2
|
@ -104,3 +104,79 @@ class AuthenticatorDeletion(AuthenticatorEvents):
|
|||
(authenticator,) = event.get_typed_references(BaseAuthenticator)
|
||||
authenticator = authenticator or event.get_data('authenticator_name')
|
||||
return _('deletion of authenticator "%s"') % authenticator
|
||||
|
||||
|
||||
class AuthenticatorRelatedObjectEvents(AuthenticatorEvents):
|
||||
@classmethod
|
||||
def record(cls, *, user, session, related_object, data=None):
|
||||
data = data or {}
|
||||
data.update({'related_object': related_object.get_journal_text()})
|
||||
super().record(user=user, session=session, authenticator=related_object.authenticator, data=data)
|
||||
|
||||
|
||||
class AuthenticatorRelatedObjectCreation(AuthenticatorRelatedObjectEvents):
|
||||
name = 'authenticator.related_object.creation'
|
||||
label = _('Authenticator related object creation')
|
||||
|
||||
@classmethod
|
||||
def get_message(cls, event, context):
|
||||
(authenticator,) = event.get_typed_references(BaseAuthenticator)
|
||||
authenticator = authenticator or event.get_data('authenticator_name')
|
||||
related_object = event.get_data('related_object')
|
||||
if context != authenticator:
|
||||
return _('creation of object "{related_object}" in authenticator "{authenticator}"').format(
|
||||
related_object=related_object, authenticator=authenticator
|
||||
)
|
||||
else:
|
||||
return _('creation of object "%s"') % related_object
|
||||
|
||||
|
||||
class AuthenticatorRelatedObjectEdit(AuthenticatorRelatedObjectEvents):
|
||||
name = 'authenticator.related_object.edit'
|
||||
label = _('Authenticator related object edit')
|
||||
|
||||
@classmethod
|
||||
def record(cls, *, user, session, form):
|
||||
super().record(
|
||||
user=user,
|
||||
session=session,
|
||||
related_object=form.instance,
|
||||
data=form_to_old_new(form),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_message(cls, event, context):
|
||||
(authenticator,) = event.get_typed_references(BaseAuthenticator)
|
||||
authenticator = authenticator or event.get_data('authenticator_name')
|
||||
related_object = event.get_data('related_object')
|
||||
new = event.get_data('new') or {}
|
||||
edited_attributes = ', '.join(new) or ''
|
||||
if context != authenticator:
|
||||
return _(
|
||||
'edit of object "{related_object}" in authenticator "{authenticator}" ({change})'
|
||||
).format(
|
||||
related_object=related_object,
|
||||
authenticator=authenticator,
|
||||
change=edited_attributes,
|
||||
)
|
||||
else:
|
||||
return _('edit of object "{related_object}" ({change})').format(
|
||||
related_object=related_object, change=edited_attributes
|
||||
)
|
||||
|
||||
|
||||
class AuthenticatorRelatedObjectDeletion(AuthenticatorRelatedObjectEvents):
|
||||
name = 'authenticator.related_object.deletion'
|
||||
label = _('Authenticator related object deletion')
|
||||
|
||||
@classmethod
|
||||
def get_message(cls, event, context):
|
||||
(authenticator,) = event.get_typed_references(BaseAuthenticator)
|
||||
authenticator = authenticator or event.get_data('authenticator_name')
|
||||
related_object = event.get_data('related_object')
|
||||
if context != authenticator:
|
||||
return _('deletion of object "{related_object}" in authenticator "{authenticator}"').format(
|
||||
related_object=related_object, authenticator=authenticator
|
||||
)
|
||||
else:
|
||||
return _('deletion of object "%s"') % related_object
|
||||
|
|
|
@ -81,5 +81,20 @@ urlpatterns = required(
|
|||
views.order,
|
||||
name='a2-manager-authenticators-order',
|
||||
),
|
||||
path(
|
||||
'authenticators/<int:authenticator_pk>/<slug:model_name>/add/',
|
||||
views.add_related_object,
|
||||
name='a2-manager-authenticators-add-related-object',
|
||||
),
|
||||
path(
|
||||
'authenticators/<int:authenticator_pk>/<slug:model_name>/<int:pk>/edit/',
|
||||
views.edit_related_object,
|
||||
name='a2-manager-authenticators-edit-related-object',
|
||||
),
|
||||
path(
|
||||
'authenticators/<int:authenticator_pk>/<slug:model_name>/<int:pk>/delete/',
|
||||
views.delete_related_object,
|
||||
name='a2-manager-authenticators-delete-related-object',
|
||||
),
|
||||
],
|
||||
)
|
||||
|
|
|
@ -141,6 +141,24 @@ class BaseAuthenticator(models.Model):
|
|||
return True
|
||||
|
||||
|
||||
class AuthenticatorRelatedObjectBase(models.Model):
|
||||
authenticator = models.ForeignKey(BaseAuthenticator, on_delete=models.CASCADE)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def get_journal_text(self):
|
||||
return '%s (%s)' % (self._meta.verbose_name, self.pk)
|
||||
|
||||
@property
|
||||
def model_name(self):
|
||||
return self._meta.model_name
|
||||
|
||||
@property
|
||||
def verbose_name_plural(self):
|
||||
return self._meta.verbose_name_plural
|
||||
|
||||
|
||||
class LoginPasswordAuthenticator(BaseAuthenticator):
|
||||
remember_me = models.PositiveIntegerField(
|
||||
_('Remember me duration'),
|
||||
|
|
|
@ -35,6 +35,9 @@
|
|||
<div class='pk-tabs'>
|
||||
<div class="pk-tabs--tab-list" role="tablist">
|
||||
<button aria-controls="panel-description" aria-selected="true" id="tab-description" role="tab" tabindex="0">{% trans "Description" %}</button>
|
||||
{% for model in object.related_models %}
|
||||
<button aria-controls="panel-{{ model.model_name }}" aria-selected="false" id="tab-{{ model.model_name }}" role="tab" tabindex="-1">{{ model.verbose_name_plural }}</button>
|
||||
{% endfor %}
|
||||
{% block extra-tab-buttons %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
@ -47,6 +50,11 @@
|
|||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% for model, objects in object.related_models.items %}
|
||||
<div aria-labelledby="tab-{{ model.model_name }}" hidden="" id="panel-{{ model.model_name }}" role="tabpanel" tabindex="0">
|
||||
{% include 'authentic2/authenticators/related_object_list.html' with object_list=objects model_name=model.model_name %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% block extra-tab-list %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
|
|
@ -14,9 +14,11 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.apps import apps
|
||||
from django.contrib import messages
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.forms.models import modelform_factory
|
||||
from django.http import Http404, HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils.functional import cached_property
|
||||
|
@ -215,3 +217,73 @@ class AuthenticatorsOrderView(AuthenticatorsMixin, FormView):
|
|||
|
||||
|
||||
order = AuthenticatorsOrderView.as_view()
|
||||
|
||||
|
||||
class AuthenticatorRelatedObjectMixin(MediaMixin, TitleMixin):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.authenticator = get_object_or_404(
|
||||
BaseAuthenticator.authenticators.all(), pk=kwargs.get('authenticator_pk')
|
||||
)
|
||||
|
||||
model_name = kwargs.get('model_name')
|
||||
if model_name not in (x._meta.model_name for x in self.authenticator.related_models):
|
||||
raise Http404()
|
||||
self.model = apps.get_model(self.authenticator._meta.app_label, model_name)
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_form_class(self):
|
||||
return modelform_factory(self.model, self.authenticator.related_object_form_class)
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
if not kwargs.get('instance'):
|
||||
kwargs['instance'] = self.model()
|
||||
kwargs['instance'].authenticator = self.authenticator
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
return (
|
||||
reverse('a2-manager-authenticator-detail', kwargs={'pk': self.authenticator.pk})
|
||||
+ '#open:%s' % self.model._meta.model_name
|
||||
)
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
return self.model._meta.verbose_name
|
||||
|
||||
|
||||
class RelatedObjectAddView(AuthenticatorRelatedObjectMixin, CreateView):
|
||||
template_name = 'authentic2/manager/form.html'
|
||||
|
||||
def form_valid(self, form):
|
||||
resp = super().form_valid(form)
|
||||
self.request.journal.record('authenticator.related_object.creation', related_object=form.instance)
|
||||
return resp
|
||||
|
||||
|
||||
add_related_object = RelatedObjectAddView.as_view()
|
||||
|
||||
|
||||
class RelatedObjectEditView(AuthenticatorRelatedObjectMixin, UpdateView):
|
||||
template_name = 'authentic2/manager/form.html'
|
||||
|
||||
def form_valid(self, form):
|
||||
resp = super().form_valid(form)
|
||||
self.request.journal.record('authenticator.related_object.edit', form=form)
|
||||
return resp
|
||||
|
||||
|
||||
edit_related_object = RelatedObjectEditView.as_view()
|
||||
|
||||
|
||||
class RelatedObjectDeleteView(AuthenticatorRelatedObjectMixin, DeleteView):
|
||||
template_name = 'authentic2/authenticators/authenticator_delete_form.html'
|
||||
title = ''
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
self.request.journal.record('authenticator.related_object.deletion', related_object=self.get_object())
|
||||
return super().delete(*args, **kwargs)
|
||||
|
||||
|
||||
delete_related_object = RelatedObjectDeleteView.as_view()
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
# authentic2 - versatile identity manager
|
||||
# Copyright (C) 2010-2022 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from authentic2.apps.authenticators.journal_event_types import AuthenticatorEvents
|
||||
from authentic2.apps.authenticators.models import BaseAuthenticator
|
||||
from authentic2.apps.journal.utils import form_to_old_new
|
||||
|
||||
|
||||
class AuthenticatorRelatedObjectEvents(AuthenticatorEvents):
|
||||
@classmethod
|
||||
def record(cls, *, user, session, related_object, data=None):
|
||||
data = data or {}
|
||||
data.update({'related_object': related_object.get_journal_text()})
|
||||
super().record(user=user, session=session, authenticator=related_object.authenticator, data=data)
|
||||
|
||||
|
||||
class AuthenticatorRelatedObjectCreation(AuthenticatorRelatedObjectEvents):
|
||||
name = 'authenticator.related_object.creation'
|
||||
label = _('Authenticator related object creation')
|
||||
|
||||
@classmethod
|
||||
def get_message(cls, event, context):
|
||||
(authenticator,) = event.get_typed_references(BaseAuthenticator)
|
||||
authenticator = authenticator or event.get_data('authenticator_name')
|
||||
related_object = event.get_data('related_object')
|
||||
if context != authenticator:
|
||||
return _('creation of object "{related_object}" in authenticator "{authenticator}"').format(
|
||||
related_object=related_object, authenticator=authenticator
|
||||
)
|
||||
else:
|
||||
return _('creation of object "%s"') % related_object
|
||||
|
||||
|
||||
class AuthenticatorRelatedObjectEdit(AuthenticatorRelatedObjectEvents):
|
||||
name = 'authenticator.related_object.edit'
|
||||
label = _('Authenticator related object edit')
|
||||
|
||||
@classmethod
|
||||
def record(cls, *, user, session, form):
|
||||
super().record(
|
||||
user=user,
|
||||
session=session,
|
||||
related_object=form.instance,
|
||||
data=form_to_old_new(form),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_message(cls, event, context):
|
||||
(authenticator,) = event.get_typed_references(BaseAuthenticator)
|
||||
authenticator = authenticator or event.get_data('authenticator_name')
|
||||
related_object = event.get_data('related_object')
|
||||
new = event.get_data('new') or {}
|
||||
edited_attributes = ', '.join(new) or ''
|
||||
if context != authenticator:
|
||||
return _(
|
||||
'edit of object "{related_object}" in authenticator "{authenticator}" ({change})'
|
||||
).format(
|
||||
related_object=related_object,
|
||||
authenticator=authenticator,
|
||||
change=edited_attributes,
|
||||
)
|
||||
else:
|
||||
return _('edit of object "{related_object}" ({change})').format(
|
||||
related_object=related_object, change=edited_attributes
|
||||
)
|
||||
|
||||
|
||||
class AuthenticatorRelatedObjectDeletion(AuthenticatorRelatedObjectEvents):
|
||||
name = 'authenticator.related_object.deletion'
|
||||
label = _('Authenticator related object deletion')
|
||||
|
||||
@classmethod
|
||||
def get_message(cls, event, context):
|
||||
(authenticator,) = event.get_typed_references(BaseAuthenticator)
|
||||
authenticator = authenticator or event.get_data('authenticator_name')
|
||||
related_object = event.get_data('related_object')
|
||||
if context != authenticator:
|
||||
return _('deletion of object "{related_object}" in authenticator "{authenticator}"').format(
|
||||
related_object=related_object, authenticator=authenticator
|
||||
)
|
||||
else:
|
||||
return _('deletion of object "%s"') % related_object
|
|
@ -21,7 +21,7 @@ from django.db import models
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from authentic2.a2_rbac.models import Role
|
||||
from authentic2.apps.authenticators.models import BaseAuthenticator
|
||||
from authentic2.apps.authenticators.models import AuthenticatorRelatedObjectBase, BaseAuthenticator
|
||||
from authentic2.manager.utils import label_from_role
|
||||
from authentic2.utils.misc import redirect_to_login
|
||||
|
||||
|
@ -213,24 +213,6 @@ class SAMLAuthenticator(BaseAuthenticator):
|
|||
return views.profile(request, *args, **kwargs)
|
||||
|
||||
|
||||
class AuthenticatorRelatedObjectBase(models.Model):
|
||||
authenticator = models.ForeignKey(BaseAuthenticator, on_delete=models.CASCADE)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def get_journal_text(self):
|
||||
return '%s (%s)' % (self._meta.verbose_name, self.pk)
|
||||
|
||||
@property
|
||||
def model_name(self):
|
||||
return self._meta.model_name
|
||||
|
||||
@property
|
||||
def verbose_name_plural(self):
|
||||
return self._meta.verbose_name_plural
|
||||
|
||||
|
||||
class SAMLAttributeLookup(AuthenticatorRelatedObjectBase):
|
||||
user_field = models.CharField(_('User field'), max_length=256)
|
||||
saml_attribute = models.CharField(_('SAML attribute'), max_length=1024)
|
||||
|
|
|
@ -10,17 +10,3 @@
|
|||
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra-tab-buttons %}
|
||||
{% for model in object.related_models %}
|
||||
<button aria-controls="panel-{{ model.model_name }}" aria-selected="false" id="tab-{{ model.model_name }}" role="tab" tabindex="-1">{{ model.verbose_name_plural }}</button>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra-tab-list %}
|
||||
{% for model, objects in object.related_models.items %}
|
||||
<div aria-labelledby="tab-{{ model.model_name }}" hidden="" id="panel-{{ model.model_name }}" role="tabpanel" tabindex="0">
|
||||
{% include 'authentic2_auth_saml/related_object_list.html' with object_list=objects model_name=model.model_name %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -15,34 +15,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.conf.urls import include, url
|
||||
from django.urls import path
|
||||
|
||||
from authentic2.apps.authenticators.manager_urls import superuser_login_required
|
||||
from authentic2.decorators import required
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^accounts/saml/', include('mellon.urls'), kwargs={'template_base': 'authentic2/base.html'})
|
||||
]
|
||||
|
||||
urlpatterns += required(
|
||||
superuser_login_required,
|
||||
[
|
||||
path(
|
||||
'authenticators/<int:authenticator_pk>/<slug:model_name>/add/',
|
||||
views.add_related_object,
|
||||
name='a2-manager-authenticators-add-related-object',
|
||||
),
|
||||
path(
|
||||
'authenticators/<int:authenticator_pk>/<slug:model_name>/<int:pk>/edit/',
|
||||
views.edit_related_object,
|
||||
name='a2-manager-authenticators-edit-related-object',
|
||||
),
|
||||
path(
|
||||
'authenticators/<int:authenticator_pk>/<slug:model_name>/<int:pk>/delete/',
|
||||
views.delete_related_object,
|
||||
name='a2-manager-authenticators-delete-related-object',
|
||||
),
|
||||
],
|
||||
)
|
||||
|
|
|
@ -1,14 +1,7 @@
|
|||
from django.apps import apps
|
||||
from django.forms.models import modelform_factory
|
||||
from django.http import Http404
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.shortcuts import render
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse
|
||||
from django.views.generic import CreateView, DeleteView, UpdateView
|
||||
from mellon.utils import get_idp
|
||||
|
||||
from authentic2.apps.authenticators.models import BaseAuthenticator
|
||||
from authentic2.manager.views import MediaMixin, TitleMixin
|
||||
from authentic2.utils.misc import redirect_to_login
|
||||
|
||||
|
||||
|
@ -41,73 +34,3 @@ def profile(request, *args, **kwargs):
|
|||
user_saml_identifier.idp = get_idp(user_saml_identifier.issuer.entity_id)
|
||||
context['user_saml_identifiers'] = user_saml_identifiers
|
||||
return render_to_string('authentic2_auth_saml/profile.html', context, request=request)
|
||||
|
||||
|
||||
class AuthenticatorRelatedObjectMixin(MediaMixin, TitleMixin):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.authenticator = get_object_or_404(
|
||||
BaseAuthenticator.authenticators.all(), pk=kwargs.get('authenticator_pk')
|
||||
)
|
||||
|
||||
model_name = kwargs.get('model_name')
|
||||
if model_name not in (x._meta.model_name for x in self.authenticator.related_models):
|
||||
raise Http404()
|
||||
self.model = apps.get_model(self.authenticator._meta.app_label, model_name)
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_form_class(self):
|
||||
return modelform_factory(self.model, self.authenticator.related_object_form_class)
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
if not kwargs.get('instance'):
|
||||
kwargs['instance'] = self.model()
|
||||
kwargs['instance'].authenticator = self.authenticator
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
return (
|
||||
reverse('a2-manager-authenticator-detail', kwargs={'pk': self.authenticator.pk})
|
||||
+ '#open:%s' % self.model._meta.model_name
|
||||
)
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
return self.model._meta.verbose_name
|
||||
|
||||
|
||||
class RelatedObjectAddView(AuthenticatorRelatedObjectMixin, CreateView):
|
||||
template_name = 'authentic2/manager/form.html'
|
||||
|
||||
def form_valid(self, form):
|
||||
resp = super().form_valid(form)
|
||||
self.request.journal.record('authenticator.related_object.creation', related_object=form.instance)
|
||||
return resp
|
||||
|
||||
|
||||
add_related_object = RelatedObjectAddView.as_view()
|
||||
|
||||
|
||||
class RelatedObjectEditView(AuthenticatorRelatedObjectMixin, UpdateView):
|
||||
template_name = 'authentic2/manager/form.html'
|
||||
|
||||
def form_valid(self, form):
|
||||
resp = super().form_valid(form)
|
||||
self.request.journal.record('authenticator.related_object.edit', form=form)
|
||||
return resp
|
||||
|
||||
|
||||
edit_related_object = RelatedObjectEditView.as_view()
|
||||
|
||||
|
||||
class RelatedObjectDeleteView(AuthenticatorRelatedObjectMixin, DeleteView):
|
||||
template_name = 'authentic2/authenticators/authenticator_delete_form.html'
|
||||
title = ''
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
self.request.journal.record('authenticator.related_object.deletion', related_object=self.get_object())
|
||||
return super().delete(*args, **kwargs)
|
||||
|
||||
|
||||
delete_related_object = RelatedObjectDeleteView.as_view()
|
||||
|
|
Loading…
Reference in New Issue