diff --git a/cotisations/models.py b/cotisations/models.py index 32326983..006e8d69 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -246,9 +246,10 @@ class Facture(BaseInvoice): def can_change_control(user_request, *_args, **_kwargs): """ Returns True if the user can change the 'controlled' status of this invoice """ + can = user_request.has_perm('cotisations.change_facture_control') return ( - user_request.has_perm('cotisations.change_facture_control'), - _("You don't have the right to edit the \"controlled\" state."), + can, + _("You don't have the right to edit the \"controlled\" state.") if not can else None, ('cotisations.change_facture_control',) ) @@ -746,11 +747,12 @@ class Article(RevMixin, AclMixin, models.Model): A boolean stating if usage is granted and an explanation message if the boolean is `False`. """ + can = self.available_for_everyone \ + or user.has_perm('cotisations.buy_every_article') \ + or user.has_perm('cotisations.add_facture') return ( - self.available_for_everyone - or user.has_perm('cotisations.buy_every_article') - or user.has_perm('cotisations.add_facture'), - _("You can't buy this article."), + can, + _("You can't buy this article.") if not can else None, ('cotisations.buy_every_article', 'cotisations.add_facture') ) @@ -902,11 +904,12 @@ class Paiement(RevMixin, AclMixin, models.Model): A boolean stating if usage is granted and an explanation message if the boolean is `False`. """ + can = self.available_for_everyone \ + or user.has_perm('cotisations.use_every_payment') \ + or user.has_perm('cotisations.add_facture') return ( - self.available_for_everyone - or user.has_perm('cotisations.use_every_payment') - or user.has_perm('cotisations.add_facture'), - _("You can't use this payment method."), + can, + _("You can't use this payment method.") if not can else None, ('cotisations.use_every_payment', 'cotisations.add_facture') ) diff --git a/machines/models.py b/machines/models.py index 11a4498b..68c7c58b 100644 --- a/machines/models.py +++ b/machines/models.py @@ -105,9 +105,10 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): A tuple with a boolean stating if edition is allowed and an explanation message. """ + can = user_request.has_perm('machines.change_machine_user') return ( - user_request.has_perm('machines.change_machine_user'), - _("You don't have the right to change the machine's user."), + can, + _("You don't have the right to change the machine's user.") if not can else None, ('machines.change_machine_user',) ) @@ -803,9 +804,10 @@ class Extension(RevMixin, AclMixin, models.Model): restrictions :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" + can = user_request.has_perm('machines.use_all_extension') return ( - user_request.has_perm('machines.use_all_extension'), - _("You cannot use all extensions."), + can, + _("You cannot use all extensions.") if not can else None, ('machines.use_all_extension',) ) @@ -1294,9 +1296,10 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): def can_change_machine(user_request, *_args, **_kwargs): """Check if a user can change the machine associated with an Interface object """ + can = user_request.has_perm('machines.change_interface_machine') return ( - user_request.has_perm('machines.change_interface_machine'), - _("Permission required to edit the machine."), + can, + _("Permission required to edit the machine.") if not can else None, ('machines.change_interface_machine',) ) @@ -1421,10 +1424,11 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): @staticmethod def can_change_slaac_ip(user_request, *_args, **_kwargs): """ Check if a user can change the slaac value """ + can = user_request.has_perm('machines.change_ipv6list_slaac_ip') return ( - user_request.has_perm('machines.change_ipv6list_slaac_ip'), + can, _("Permission required to change the SLAAC value of an IPv6" - " address"), + " address") if not can else None, ('machines.change_ipv6list_slaac_ip',) ) diff --git a/preferences/migrations/0061_optionaluser_allow_archived_connexion.py b/preferences/migrations/0061_optionaluser_allow_archived_connexion.py new file mode 100644 index 00000000..505f940b --- /dev/null +++ b/preferences/migrations/0061_optionaluser_allow_archived_connexion.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-09-09 09:50 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0060_auto_20190712_1821'), + ] + + operations = [ + migrations.AddField( + model_name='optionaluser', + name='allow_archived_connexion', + field=models.BooleanField(default=False, help_text='If True, archived users are allowed to connect.'), + ), + ] diff --git a/preferences/models.py b/preferences/models.py index be8b8ec2..98374e77 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -123,6 +123,10 @@ class OptionalUser(AclMixin, PreferencesModel): help_text=_("If True, all new created and connected users are active." " If False, only when a valid registration has been paid.") ) + allow_archived_connexion = models.BooleanField( + default=False, + help_text=_("If True, archived users are allowed to connect.") + ) class Meta: permissions = ( diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 3e71c817..626cb1a3 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -125,6 +125,8 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "All users are active by default" %} {{ useroptions.all_users_active|tick }} + {% trans "Allow archived users to log-in" %} + {{ useroptions.allow_archived_connexion|tick }} diff --git a/re2o/acl.py b/re2o/acl.py index e445a28c..75857dbf 100644 --- a/re2o/acl.py +++ b/re2o/acl.py @@ -41,6 +41,8 @@ from re2o.utils import get_group_having_permission def acl_error_message(msg, permissions): """Create an error message for msg and permissions.""" + if permissions is None: + return msg groups = ", ".join([ g.name for g in get_group_having_permission(*permissions) ]) @@ -76,9 +78,11 @@ def acl_base_decorator(method_name, *targets, on_instance=True): permission was granted. This is to allow you to run ACL tests on fields only. If the method exists, it has to return a 2-tuple `(can, reason, permissions)` with `can` being a boolean stating - whether the access is granted, `reason` a message to be + whether the access is granted, `reason` an arror message to be displayed if `can` equals `False` (can be `None`) and `permissions` - a list of permissions needed for access (can be `None`). + a list of permissions needed for access (can be `None`). If can is + True and permission is not `None`, a warning message will be + displayed. *targets: The targets. Targets are specified like a sequence of models and fields names. As an example ``` @@ -172,10 +176,17 @@ ModelC) yield can_change_fct(request.user, *args, **kwargs) error_messages = [] + warning_messages = [] for target, fields in group_targets(): for can, msg, permissions in process_target(target, fields): if not can: error_messages.append(acl_error_message(msg, permissions)) + elif msg: + warning_messages.append(acl_error_message(msg, permissions)) + + if warning_messages: + for msg in warning_messages: + messages.warning(request, msg) if error_messages: for msg in error_messages: diff --git a/re2o/mixins.py b/re2o/mixins.py index 9d7f7b2f..dfa6e987 100644 --- a/re2o/mixins.py +++ b/re2o/mixins.py @@ -105,10 +105,11 @@ class AclMixin(object): :param user_request: instance utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" permission = cls.get_modulename() + '.add_' + cls.get_classname() + can = user_request.has_perm(permission) return ( - user_request.has_perm(permission), + can, _("You don't have the right to create a %s object.") - % cls.get_classname(), + % cls.get_classname() if not can else None, (permission,) ) @@ -119,10 +120,11 @@ class AclMixin(object): :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" permission = self.get_modulename() + '.change_' + self.get_classname() + can = user_request.has_perm(permission) return ( - user_request.has_perm(permission), + can, _("You don't have the right to edit a %s object.") - % self.get_classname(), + % self.get_classname() if not can else None, (permission,) ) @@ -133,10 +135,11 @@ class AclMixin(object): :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" permission = self.get_modulename() + '.delete_' + self.get_classname() + can = user_request.has_perm(permission) return ( - user_request.has_perm(permission), + can, _("You don't have the right to delete a %s object.") - % self.get_classname(), + % self.get_classname() if not can else None, (permission,) ) @@ -147,10 +150,11 @@ class AclMixin(object): :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" permission = cls.get_modulename() + '.view_' + cls.get_classname() + can = user_request.has_perm(permission) return ( - user_request.has_perm(permission), + can, _("You don't have the right to view every %s object.") - % cls.get_classname(), + % cls.get_classname() if not can else None, (permission,) ) @@ -161,10 +165,11 @@ class AclMixin(object): :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" permission = self.get_modulename() + '.view_' + self.get_classname() + can = user_request.has_perm(permission) return ( - user_request.has_perm(permission), + can, _("You don't have the right to view a %s object.") - % self.get_classname(), + % self.get_classname() if not can else None, (permission,) ) diff --git a/tickets/models.py b/tickets/models.py index a3fe5e7a..e9acaf9b 100644 --- a/tickets/models.py +++ b/tickets/models.py @@ -86,9 +86,10 @@ class Ticket(AclMixin, models.Model): @staticmethod def can_view_all(user_request, *_args, **_kwargs): """ Check that the user has access to the list of all tickets""" + can = user_request.has_perm('tickets.view_tickets') return( - user_request.has_perm('tickets.view_tickets'), - _("You don't have the right to view the list of tickets."), + can, + _("You don't have the right to view the list of tickets.") if not can else None, ('tickets.view_tickets',) ) diff --git a/users/models.py b/users/models.py index 68719e0f..66d53a96 100755 --- a/users/models.py +++ b/users/models.py @@ -333,7 +333,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, @property def is_active(self): """ Renvoie si l'user est à l'état actif""" - return self.state == self.STATE_ACTIVE or self.state == self.STATE_NOT_YET_ACTIVE + allow_archived = OptionalUser.get_cached_value('allow_archived_connexion') + return self.state == self.STATE_ACTIVE or self.state == self.STATE_NOT_YET_ACTIVE or (allow_archived and self.state in (self.STATE_ARCHIVE, self.STATE_FULL_ARCHIVE)) def set_active(self): """Enable this user if he subscribed successfully one time before @@ -858,18 +859,23 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, user_request one of its member, or if user_request is self, or if user_request has the 'cableur' right. """ + if self.state in (self.STATE_ARCHIVE, self.STATE_FULL_ARCHIVE): + warning_message = _("This user is archived.") + else: + warning_message = None + if self.is_class_club and user_request.is_class_adherent: if (self == user_request or user_request.has_perm('users.change_user') or user_request.adherent in self.club.administrators.all()): - return True, None, None + return True, warning_message, None else: return False, _("You don't have the right to edit this club."), ('users.change_user',) else: if self == user_request: - return True, None, None + return True, warning_message, None elif user_request.has_perm('users.change_all_users'): - return True, None, None + return True, warning_message, None elif user_request.has_perm('users.change_user'): if self.groups.filter(listright__critical=True): return ( @@ -885,9 +891,9 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, ('users.change_all_users', ) ) else: - return True, None, None + return True, warning_message, None elif user_request.has_perm('users.change_all_users'): - return True, None, None + return True, warning_message, None else: return ( False, @@ -962,9 +968,10 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :returns: a message and a boolean which is True if the user has the right to change a state """ + can = user_request.has_perm('users.change_user_state') return ( - user_request.has_perm('users.change_user_state'), - _("Permission required to change the state."), + can, + _("Permission required to change the state.") if not can else None, ('users.change_user_state',) ) @@ -993,9 +1000,10 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :returns: a message and a boolean which is True if the user has the right to change a redirection """ + can = OptionalUser.get_cached_value('local_email_accounts_enabled') return ( - OptionalUser.get_cached_value('local_email_accounts_enabled'), - _("Local email accounts must be enabled."), + can, + _("Local email accounts must be enabled.") if not can else None, None ) @@ -1007,9 +1015,10 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :returns: a message and a boolean which is True if the user has the right to change internal address """ + can = OptionalUser.get_cached_value('local_email_accounts_enabled') return ( - OptionalUser.get_cached_value('local_email_accounts_enabled'), - _("Local email accounts must be enabled."), + can, + _("Local email accounts must be enabled.") if not can else None, None ) @@ -1021,9 +1030,10 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :returns: a message and a boolean which is True if the user has the right to change a force """ + can = user_request.has_perm('users.change_user_force') return ( - user_request.has_perm('users.change_user_force'), - _("Permission required to force the move."), + can, + _("Permission required to force the move.") if not can else None, ('users.change_user_force',) ) @@ -1035,9 +1045,10 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :returns: a message and a boolean which is True if the user has the right to change a group """ + can = user_request.has_perm('users.change_user_grou') return ( - user_request.has_perm('users.change_user_groups'), - _("Permission required to edit the user's groups of rights."), + can, + _("Permission required to edit the user's groups of rights.") if not can else None, ('users.change_user_groups') ) @@ -1048,9 +1059,10 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :param user_request: The user who request :returns: a message and a boolean which is True if permission is granted. """ + can = user_request.is_superuser return ( - user_request.is_superuser, - _("'superuser' right required to edit the superuser flag."), + can, + _("'superuser' right required to edit the superuser flag.") if not can else None, [] ) @@ -1093,9 +1105,10 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :return: True if the user can view the list and an explanation message. """ + can = user_request.has_perm('use.view_user') return ( - user_request.has_perm('users.view_user'), - _("You don't have the right to view the list of users."), + can, + _("You don't have the right to view the list of users.") if not can else None, ('users.view_user',) ) @@ -1107,9 +1120,10 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, :return: True if user_request has the right 'bureau', and a message. """ + can = user_request.has_perm('users.delete_user') return ( - user_request.has_perm('users.delete_user'), - _("You don't have the right to delete this user."), + can, + _("You don't have the right to delete this user.") if not can else None, ('users.delete_user',) ) @@ -1203,9 +1217,10 @@ class Adherent(User): OptionalUser.get_cached_value('self_adhesion')): return True, None, None else: + can = user_request.has_perm('users.add_user') return ( - user_request.has_perm('users.add_user'), - _("You don't have the right to create a user."), + can, + _("You don't have the right to create a user.") if not can else None, ('users.add_user',) ) @@ -1259,9 +1274,10 @@ class Club(User): if OptionalUser.get_cached_value('all_can_create_club'): return True, None, None else: + can = user_request.has_perm('users.add_user') return ( - user_request.has_perm('users.add_user'), - _("You don't have the right to create a club."), + can, + _("You don't have the right to create a club.") if not can else None, ('users.add_user',) )