views: add a delete_account view on /accounts/delete

It desactivate then logout the requesting user. A model DeletedUser is
created. The cleanup() method of the DeletedUser manager will delete the
user account and the deleted user object.
This commit is contained in:
Benjamin Dauvergne 2013-06-17 16:54:09 +02:00
parent 892502e3d5
commit 67ce8619ad
7 changed files with 77 additions and 18 deletions

View File

@ -376,6 +376,15 @@ object is created. By default this new ``User`` has both ``is_staff`` and
The default PAM service used is ``login`` but you can change it by setting the
``PAM_SERVICE`` variable in your ``settings.py`` file.
Cronjobs
========
The following cronjob must be run to clean deleted accounts and temporary objects::
5 0 * * * athentic2-ctl cleanup
It's made to run every day at 00:05.
Roadmap
=======

View File

@ -25,7 +25,8 @@
{% endfor %}
</dl>
{% endif %}
<p> <a href="{% url 'profile_edit' %}">{% trans "Edit profile" %}</a></p>
<p><a href="{% url 'profile_edit' %}">{% trans "Edit profile" %}</a></p>
<p><a href="{% url 'delete_account' %}">{% trans "Delete profile" %}</a></p>
</div>
<h3>{% trans "Credentials" %}</h3>
{% for html_block in frontends_block %}

View File

@ -78,8 +78,12 @@ def profile(request):
# Credentials management
blocks = [ frontend.profile(request, next='/profile') for frontend in frontends \
if hasattr(frontend, 'profile') ]
return render_to_response('idp/account_management.html', { 'frontends_block': blocks, 'profile': profile },
RequestContext(request))
return render_to_response('idp/account_management.html', {
'frontends_block': blocks,
'profile': profile,
'allow_account_deletion': settings.AUTHENTIC2_ALLOW_ACCOUNT_DELETION,
},
RequestContext(request))
def logout_list(request):
'''Return logout links from idp backends'''

View File

@ -14,6 +14,9 @@ from django.utils.http import urlquote
from django.conf import settings
import managers
class UserManager(BaseUserManager):
def create_user(self, username, email=None, password=None, **extra_fields):
"""
@ -163,6 +166,19 @@ class AbstractUser(AbstractBaseUser, PermissionsMixin):
roles = property(get_roles)
class DeletedUser(models.Model):
'''Record users to delete'''
objects = managers.DeletedUserManager()
user = models.ForeignKey(settings.AUTH_USER_MODEL)
creation = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = _('user to delete')
verbose_name_plural = _('users to delete')
if settings.AUTH_USER_MODEL == 'authentic2.User':
class User(AbstractUser):
first_name = models.CharField(_('first name'), max_length=30, blank=True)

View File

@ -116,6 +116,7 @@ MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
# Registration settings
ACCOUNT_ACTIVATION_DAYS = int(os.environ.get('ACCOUNT_ACTIVATION_DAYS', 3))
PASSWORD_RESET_TIMEOUT_DAYS = int(os.environ.get('PASSWORD_RESET_TIMEOUT_DAYS', 3))
AUTHENTIC2_ALLOW_ACCOUNT_DELETION = True
# authentication
AUTHENTICATION_BACKENDS = (

View File

@ -17,8 +17,9 @@ urlpatterns = patterns('',
(r'^$', login_required(authentic2.idp.views.homepage), {}, 'index'))
not_homepage_patterns = patterns('',
(r'^', include('authentic2.auth2_auth.urls')),
(r'^redirect/(.*)', 'authentic2.views.redirect'),
url(r'^', include('authentic2.auth2_auth.urls')),
url(r'^redirect/(.*)', 'authentic2.views.redirect'),
url(r'^accounts/delete/', 'authentic2.views.delete_account', name='delete_account'),
url(r'^accounts/password/change/done/', 'authentic2.views.password_change_done'),
url(r'^accounts/register/complete/', 'authentic2.views.registration_success', name='registration_complete'),
url(r'^accounts/register/',
@ -28,18 +29,16 @@ not_homepage_patterns = patterns('',
'form_class': AuthenticRegistrationForm },
name='registration_register',
),
(r'^accounts/', include('registration.backends.simple.urls')),
url(r'^logout$', 'authentic2.idp.views.logout', name='auth_logout'),
(r'^admin/admin_log_view/log/', 'authentic2.admin_log_view.views.admin_view'),
(r'^admin/', include(admin.site.urls)),
url(r'^accounts/', include('registration.backends.simple.urls')),
url(r'^admin/', include(admin.site.urls)),
url(r'^admin_tools/', include('admin_tools.urls')),
(r'^idp/', include('authentic2.idp.urls')),
(r'^logout$', 'authentic2.idp.views.logout'),
(r'^profile/$',
prevent_access_to_transient_users(authentic2.idp.views.profile), {},
'account_management'),
url(r'^idp/', include('authentic2.idp.urls')),
url(r'^logout/$', 'authentic2.idp.views.logout', name='auth_logout'),
url(r'^profile/edit/$', 'authentic2.views.edit_profile',
name='profile_edit'),
url(r'^profile/$',
prevent_access_to_transient_users(authentic2.idp.views.profile), {},
'account_management'),
)
urlpatterns += not_homepage_patterns

View File

@ -2,11 +2,13 @@ import logging
import lasso
import thread
import requests
from django.conf import settings
from django.utils.translation import ugettext as _
from django.shortcuts import render_to_response, redirect as shortcuts_redirect
from django.shortcuts import render_to_response, redirect as shortcuts_redirect, render
from django.template import RequestContext
from django.views.generic.edit import UpdateView
from django.contrib import messages
@ -14,15 +16,21 @@ from django.contrib.auth import get_user_model
from django.shortcuts import get_object_or_404
from django.contrib.auth import SESSION_KEY
from django import http
from django.contrib.auth.decorators import login_required
from authentic2.idp.decorators import prevent_access_to_transient_users
from authentic2.idp.saml import saml2_endpoints
from authentic2.saml import models
from authentic2.saml import models as saml_models
import forms
import models
logger = logging.getLogger(__name__)
def redirect(request, next, template_name='redirect.html'):
'''Show a simple page which does a javascript redirect, closing any popup
enclosing us'''
@ -31,6 +39,7 @@ def redirect(request, next, template_name='redirect.html'):
logging.info('Redirect to %r' % next)
return render_to_response(template_name, { 'next': next })
def server_error(request, template_name='500.html'):
"""
500 error handler.
@ -42,6 +51,7 @@ def server_error(request, template_name='500.html'):
context_instance = RequestContext(request)
)
def registration_success(request, template_name='registration/registration_complete.html'):
"""
Return page after a successful registration.
@ -50,6 +60,7 @@ def registration_success(request, template_name='registration/registration_compl
context_instance = RequestContext(request)
)
class EditProfile(UpdateView):
model = get_user_model()
form_class = forms.UserProfileForm
@ -63,7 +74,7 @@ class EditProfile(UpdateView):
# Push attributes to SP
# Policy must not require user consent
federations = \
models.LibertyFederation.objects.filter(user=self.request.user)
saml_models.LibertyFederation.objects.filter(user=self.request.user)
for federation in federations:
sp_id = federation.sp_id
login = saml2_endpoints.idp_sso(self.request,
@ -88,16 +99,19 @@ class EditProfile(UpdateView):
thread.start_new_thread(self.push_attributes, ())
return super(EditProfile, self).form_valid(form)
edit_profile = prevent_access_to_transient_users(EditProfile.as_view())
def password_change_done(request):
'''Redirect user to homepage and display a success message'''
messages.info(request, _('Your password has been changed'))
return shortcuts_redirect('account_management')
def su(request, username, redirect_url='/'):
'''To use this view add:
url(r'^su/(?P<username>.*)/$', 'authentic2.views.su', {'redirect_url': '/'}),
'''
if request.user.is_superuser or request.session.get('has_superuser_power'):
@ -108,3 +122,18 @@ def su(request, username, redirect_url='/'):
return http.HttpResponseRedirect(redirect_url)
else:
return http.HttpResponseRedirect('/')
@login_required
def delete_account(request, next_url='/'):
next_url = request.build_absolute_uri(request.META.get('HTTP_REFERER') or next_url)
if not settings.AUTHENTIC2_ALLOW_ACCOUNT_DELETION:
return shortcuts_redirect(next_url)
if request.method == 'POST':
if 'submit' in request.POST:
models.DeletedUser.objects.delete_user(request.user)
logger.info(u'deletion of account %s requested' % request.user)
return shortcuts_redirect('auth_logout')
else:
return shortcuts_redirect(next_url)
return render(request, 'registration/delete_account.html')