allow overriding User.can_reset_password by hooks (fixes #25534)

This commit introduce the concept of an user flag, this flag can be
defined in many places:
* globally trough a setting named A2_USER_<FLAG>
* on the user object itself if there is a property user.<flag> which is
  not None
* by any hook returning a not None result and named a2_hook_user_<flag>
* for all users of an OU if the ou.<flag> is not None
This commit is contained in:
Benjamin Dauvergne 2018-07-30 15:45:01 +02:00
parent eeb93e79d3
commit d7a2af17c3
8 changed files with 60 additions and 6 deletions

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('a2_rbac', '0016_auto_20171208_1429'),
]
operations = [
migrations.AddField(
model_name='organizationalunit',
name='user_can_reset_password',
field=models.NullBooleanField(verbose_name='Users can reset password'),
),
]

View File

@ -44,6 +44,9 @@ class OrganizationalUnit(OrganizationalUnitAbstractBase):
content_type_field='target_ct',
object_id_field='target_id')
user_can_reset_password = models.NullBooleanField(
verbose_name=_('Users can reset password'))
objects = managers.OrganizationalUnitManager()
class Meta:

View File

@ -49,6 +49,14 @@ class AppSettings(object):
add_realms(self.A2_REALMS)
return realms.items()
@property
def A2_USER_CAN_RESET_PASSWORD(self):
if hasattr(self.settings, 'A2_USER_CAN_RESET_PASSWORD'):
return self.settings.A2_USER_CAN_RESET_PASSWORD
if hasattr(self.settings, 'A2_CAN_RESET_PASSWORD'):
return self.settings.A2_CAN_RESET_PASSWORD
return self.defaults['A2_USER_CAN_RESET_PASSWORD'].default
def __getattr__(self, key):
if key not in self.defaults:
raise AttributeError('unknown key %s' % key)
@ -107,7 +115,7 @@ default_settings = dict(
definition='Include empty fields in profile view'),
A2_HOMEPAGE_URL = Setting(default=None, definition='IdP has no homepage, '
'redirect to this one.'),
A2_CAN_RESET_PASSWORD = Setting(default=True, definition='Allow online reset of passwords'),
A2_USER_CAN_RESET_PASSWORD = Setting(default=None, definition='Allow online reset of passwords'),
A2_EMAIL_IS_UNIQUE = Setting(default=False,
definition='Email of users must be unique'),
A2_REGISTRATION_EMAIL_IS_UNIQUE = Setting(default=False,

View File

@ -216,6 +216,7 @@ class LDAPUser(get_user_model()):
if hasattr(self, 'keep_pk'):
self.pk = pk
@property
def can_reset_password(self):
return self.block['can_reset_password']

View File

@ -250,8 +250,5 @@ class User(AbstractBaseUser, PermissionMixin):
attribute.set_value(self, getattr(self, attr_name, None))
return rc
def can_reset_password(self):
return self.has_usable_password()
def can_change_password(self):
return app_settings.A2_REGISTRATION_CAN_CHANGE_PASSWORD

View File

@ -81,7 +81,10 @@ class PasswordResetConfirmView(cbv.RedirectToNextURLViewMixin, FormView):
'or has expired'))
if not validlink:
return utils.redirect(request, self.get_success_url())
if not self.user.can_reset_password():
can_reset_password = utils.get_user_flag(user=self.user,
name='can_reset_password',
default=self.user.has_usable_password())
if not can_reset_password:
messages.warning(request, _('It\'s not possible to reset your password. Please '
'contact an administrator.'))
return utils.redirect(request, self.get_success_url())

View File

@ -1054,3 +1054,26 @@ def send_email_change_email(user, email, request=None, context=None, template_na
def update_model(obj, d):
for attr, value in d.items():
setattr(obj, attr, value)
def get_user_flag(user, name, default=None):
'''Get a boolean flag settable at user, by a hook, globally or ou wide'''
from . import hooks
setting_value = getattr(app_settings, 'A2_USER_' + name.upper(), None)
if setting_value is not None:
return bool(setting_value)
user_value = getattr(user, name, None)
if user_value is not None:
return user_value
hook_value = hooks.call_hooks_first_result('user_' + name, user=user)
if hook_value is not None:
return bool(hook_value)
if user.ou and hasattr(user.ou, 'user_' + name):
ou_value = getattr(user.ou, 'user_' + name, None)
if ou_value is not None:
return ou_value
return default

View File

@ -292,7 +292,7 @@ def login(request, template_name='authentic2/login.html',
context_instance = RequestContext(request, {
'cancel': nonce is not None,
'can_reset_password': app_settings.A2_CAN_RESET_PASSWORD,
'can_reset_password': app_settings.A2_USER_CAN_RESET_PASSWORD is not False,
'registration_authorized': getattr(settings, 'REGISTRATION_OPEN', True),
'registration_url': registration_url,
})