8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-22 19:33:11 +00:00

Merge branch '213-helpful-acl-messages' into 'dev'

Resolve "When refusing access due to missing permission, also indicate which groups would have granted the required permission"

See merge request federez/re2o!432
This commit is contained in:
Maxime Bombar 2019-09-09 16:21:57 +02:00
commit 9f800a6c02
17 changed files with 698 additions and 339 deletions

View file

@ -70,6 +70,7 @@ def can_view(user):
'app_label': settings.API_CONTENT_TYPE_APP_LABEL, 'app_label': settings.API_CONTENT_TYPE_APP_LABEL,
'codename': settings.API_PERMISSION_CODENAME 'codename': settings.API_PERMISSION_CODENAME
} }
can = user.has_perm('%(app_label)s.%(codename)s' % kwargs) permission = '%(app_label)s.%(codename)s' % kwargs
can = user.has_perm(permission)
return can, None if can else _("You don't have the right to see this" return can, None if can else _("You don't have the right to see this"
" application.") " application."), (permission,)

View file

@ -40,7 +40,11 @@ def can_view(user):
""" """
can = user.has_module_perms('cotisations') can = user.has_module_perms('cotisations')
if can: if can:
return can, None return can, None, ('cotisations',)
else: else:
return can, _("You don't have the right to view this application.") return (
can,
_("You don't have the right to view this application."),
('cotisations',)
)

View file

@ -169,44 +169,78 @@ class Facture(BaseInvoice):
return self.vente_set.all() return self.vente_set.all()
def can_edit(self, user_request, *args, **kwargs): def can_edit(self, user_request, *args, **kwargs):
user_can, _, permissions = self.user.can_edit(
user_request, *args, **kwargs)
if not user_request.has_perm('cotisations.change_facture'): if not user_request.has_perm('cotisations.change_facture'):
return False, _("You don't have the right to edit an invoice.") return (
False,
_("You don't have the right to edit an invoice."),
('cotisations.change_facture',)
)
elif not user_request.has_perm('cotisations.change_all_facture') and \ elif not user_request.has_perm('cotisations.change_all_facture') and \
not self.user.can_edit(user_request, *args, **kwargs)[0]: not user_can:
return False, _("You don't have the right to edit this user's " return (
"invoices.") False,
_("You don't have the right to edit this user's invoices."),
('cotisations.change_all_facture',) + permissions
)
elif not user_request.has_perm('cotisations.change_all_facture') and \ elif not user_request.has_perm('cotisations.change_all_facture') and \
(self.control or not self.valid): (self.control or not self.valid):
return False, _("You don't have the right to edit an invoice " return (
"already controlled or invalidated.") False,
_("You don't have the right to edit an invoice "
"already controlled or invalidated."),
('cotisations.change_all_facture',)
)
else: else:
return True, None return True, None, None
def can_delete(self, user_request, *args, **kwargs): def can_delete(self, user_request, *args, **kwargs):
user_can, _, permissions = self.user.can_edit(
user_request, *args, **kwargs)
if not user_request.has_perm('cotisations.delete_facture'): if not user_request.has_perm('cotisations.delete_facture'):
return False, _("You don't have the right to delete an invoice.") return (
False,
_("You don't have the right to delete an invoice."),
('cotisations.delete_facture',)
)
elif not user_request.has_perm('cotisations.change_all_facture') and \ elif not user_request.has_perm('cotisations.change_all_facture') and \
not self.user.can_edit(user_request, *args, **kwargs)[0]: not user_can:
return False, _("You don't have the right to delete this user's " return (
"invoices.") False,
_("You don't have the right to delete this user's invoices."),
('cotisations.change_all_facture',) + permissions
)
elif not user_request.has_perm('cotisations.change_all_facture') and \ elif not user_request.has_perm('cotisations.change_all_facture') and \
(self.control or not self.valid): (self.control or not self.valid):
return False, _("You don't have the right to delete an invoice " return (
"already controlled or invalidated.") False,
_("You don't have the right to delete an invoice "
"already controlled or invalidated."),
('cotisations.change_all_facture',)
)
else: else:
return True, None return True, None, None
def can_view(self, user_request, *_args, **_kwargs): def can_view(self, user_request, *_args, **_kwargs):
if not user_request.has_perm('cotisations.view_facture'): if not user_request.has_perm('cotisations.view_facture'):
if self.user != user_request: if self.user != user_request:
return False, _("You don't have the right to view someone else's " return (
"invoices history.") False,
_("You don't have the right to view someone else's "
"invoices history."),
('cotisations.view_facture',)
)
elif not self.valid: elif not self.valid:
return False, _("The invoice has been invalidated.") return (
False,
_("The invoice has been invalidated."),
('cotisations.view_facture',)
)
else: else:
return True, None return True, None, None
else: else:
return True, None return True, None, None
@staticmethod @staticmethod
def can_change_control(user_request, *_args, **_kwargs): def can_change_control(user_request, *_args, **_kwargs):
@ -214,7 +248,8 @@ class Facture(BaseInvoice):
this invoice """ this invoice """
return ( return (
user_request.has_perm('cotisations.change_facture_control'), user_request.has_perm('cotisations.change_facture_control'),
_("You don't have the right to edit the \"controlled\" state.") _("You don't have the right to edit the \"controlled\" state."),
('cotisations.change_facture_control',)
) )
@staticmethod @staticmethod
@ -226,12 +261,12 @@ class Facture(BaseInvoice):
an invoice or if the `options.allow_self_subscription` is set. an invoice or if the `options.allow_self_subscription` is set.
""" """
if user_request.has_perm('cotisations.add_facture'): if user_request.has_perm('cotisations.add_facture'):
return True, None return True, None, None
if len(Paiement.find_allowed_payments(user_request)) <= 0: if len(Paiement.find_allowed_payments(user_request)) <= 0:
return False, _("There are no payment method which you can use.") return False, _("There are no payment method which you can use."), ('cotisations.add_facture',)
if len(Article.find_allowed_articles(user_request, user_request)) <= 0: if len(Article.find_allowed_articles(user_request, user_request)) <= 0:
return False, _("There are no article that you can buy.") return False, _("There are no article that you can buy."), ('cotisations.add_facture',)
return True, None return True, None, None
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(Facture, self).__init__(*args, **kwargs) super(Facture, self).__init__(*args, **kwargs)
@ -360,12 +395,18 @@ class CostEstimate(CustomInvoice):
def can_delete(self, user_request, *args, **kwargs): def can_delete(self, user_request, *args, **kwargs):
if not user_request.has_perm('cotisations.delete_costestimate'): if not user_request.has_perm('cotisations.delete_costestimate'):
return False, _("You don't have the right " return (
"to delete a cost estimate.") False,
_("You don't have the right to delete a cost estimate."),
('cotisations.delete_costestimate',)
)
if self.final_invoice is not None: if self.final_invoice is not None:
return False, _("The cost estimate has an " return (
"invoice and can't be deleted.") False,
return True, None _("The cost estimate has an invoice and can't be deleted."),
None
)
return True, None, None
# TODO : change Vente to Purchase # TODO : change Vente to Purchase
@ -505,40 +546,66 @@ class Vente(RevMixin, AclMixin, models.Model):
super(Vente, self).save(*args, **kwargs) super(Vente, self).save(*args, **kwargs)
def can_edit(self, user_request, *args, **kwargs): def can_edit(self, user_request, *args, **kwargs):
user_can, _, permissions = self.facture.user.can_edit(
user_request, *args, **kwargs
)
if not user_request.has_perm('cotisations.change_vente'): if not user_request.has_perm('cotisations.change_vente'):
return False, _("You don't have the right to edit the purchases.") return (
elif (not user_request.has_perm('cotisations.change_all_facture') and False,
not self.facture.user.can_edit( _("You don't have the right to edit the purchases."),
user_request, *args, **kwargs ('cotisations.change_vente',)
)[0]): )
return False, _("You don't have the right to edit this user's " elif not (
"purchases.") user_request.has_perm('cotisations.change_all_facture') or
user_can):
return (
False,
_("You don't have the right to edit this user's purchases."),
('cotisations.change_all_facture',) + permissions
)
elif (not user_request.has_perm('cotisations.change_all_vente') and elif (not user_request.has_perm('cotisations.change_all_vente') and
(self.facture.control or not self.facture.valid)): (self.facture.control or not self.facture.valid)):
return False, _("You don't have the right to edit a purchase " return (
"already controlled or invalidated.") False,
_("You don't have the right to edit a purchase "
"already controlled or invalidated."),
('cotisations.change_all_vente',)
)
else: else:
return True, None return True, None, None
def can_delete(self, user_request, *args, **kwargs): def can_delete(self, user_request, *args, **kwargs):
user_can, _, permissions = self.facture.user.can_edit(
user_request, *args, **kwargs)
if not user_request.has_perm('cotisations.delete_vente'): if not user_request.has_perm('cotisations.delete_vente'):
return False, _("You don't have the right to delete a purchase.") return (
if not self.facture.user.can_edit(user_request, *args, **kwargs)[0]: False,
return False, _("You don't have the right to delete this user's " _("You don't have the right to delete a purchase."),
"purchases.") ('cotisations.delete_vente',)
)
if not user_can:
return (
False,
_("You don't have the right to delete this user's purchases."),
permissions
)
if self.facture.control or not self.facture.valid: if self.facture.control or not self.facture.valid:
return False, _("You don't have the right to delete a purchase " return False, _("You don't have the right to delete a purchase "
"already controlled or invalidated.") "already controlled or invalidated."), None
else: else:
return True, None return True, None, None
def can_view(self, user_request, *_args, **_kwargs): def can_view(self, user_request, *_args, **_kwargs):
if (not user_request.has_perm('cotisations.view_vente') and if (not user_request.has_perm('cotisations.view_vente') and
self.facture.user != user_request): self.facture.user != user_request):
return False, _("You don't have the right to view someone " return (
"else's purchase history.") False,
_("You don't have the right to view someone "
"else's purchase history."),
('cotisations.view_vente',)
)
else: else:
return True, None return True, None, None
def __str__(self): def __str__(self):
return str(self.name) + ' ' + str(self.facture) return str(self.name) + ' ' + str(self.facture)
@ -683,7 +750,8 @@ class Article(RevMixin, AclMixin, models.Model):
self.available_for_everyone self.available_for_everyone
or user.has_perm('cotisations.buy_every_article') or user.has_perm('cotisations.buy_every_article')
or user.has_perm('cotisations.add_facture'), or user.has_perm('cotisations.add_facture'),
_("You can't buy this article.") _("You can't buy this article."),
('cotisations.buy_every_article', 'cotisations.add_facture')
) )
@classmethod @classmethod
@ -838,7 +906,8 @@ class Paiement(RevMixin, AclMixin, models.Model):
self.available_for_everyone self.available_for_everyone
or user.has_perm('cotisations.use_every_payment') or user.has_perm('cotisations.use_every_payment')
or user.has_perm('cotisations.add_facture'), or user.has_perm('cotisations.add_facture'),
_("You can't use this payment method.") _("You can't use this payment method."),
('cotisations.use_every_payment', 'cotisations.add_facture')
) )
@classmethod @classmethod
@ -907,32 +976,51 @@ class Cotisation(RevMixin, AclMixin, models.Model):
def can_edit(self, user_request, *_args, **_kwargs): def can_edit(self, user_request, *_args, **_kwargs):
if not user_request.has_perm('cotisations.change_cotisation'): if not user_request.has_perm('cotisations.change_cotisation'):
return False, _("You don't have the right to edit a subscription.") return (
False,
_("You don't have the right to edit a subscription."),
('cotisations.change_cotisation',)
)
elif not user_request.has_perm('cotisations.change_all_cotisation') \ elif not user_request.has_perm('cotisations.change_all_cotisation') \
and (self.vente.facture.control or and (self.vente.facture.control or
not self.vente.facture.valid): not self.vente.facture.valid):
return False, _("You don't have the right to edit a subscription " return (
"already controlled or invalidated.") False,
_("You don't have the right to edit a subscription "
"already controlled or invalidated."),
('cotisations.change_all_cotisation',)
)
else: else:
return True, None return True, None, None
def can_delete(self, user_request, *_args, **_kwargs): def can_delete(self, user_request, *_args, **_kwargs):
if not user_request.has_perm('cotisations.delete_cotisation'): if not user_request.has_perm('cotisations.delete_cotisation'):
return False, _("You don't have the right to delete a " return (
"subscription.") False,
_("You don't have the right to delete a subscription."),
('cotisations.delete_cotisation',)
)
if self.vente.facture.control or not self.vente.facture.valid: if self.vente.facture.control or not self.vente.facture.valid:
return False, _("You don't have the right to delete a subscription " return (
"already controlled or invalidated.") False,
_("You don't have the right to delete a subscription "
"already controlled or invalidated."),
None
)
else: else:
return True, None return True, None, None
def can_view(self, user_request, *_args, **_kwargs): def can_view(self, user_request, *_args, **_kwargs):
if not user_request.has_perm('cotisations.view_cotisation') and\ if not user_request.has_perm('cotisations.view_cotisation') and\
self.vente.facture.user != user_request: self.vente.facture.user != user_request:
return False, _("You don't have the right to view someone else's " return (
"subscription history.") False,
_("You don't have the right to view someone else's "
"subscription history."),
('cotisations.view_cotisation',)
)
else: else:
return True, None return True, None, None
def __str__(self): def __str__(self):
return str(self.vente) return str(self.vente)

View file

@ -39,6 +39,10 @@ def can_view(user):
viewing is granted and msg is a message (can be None). viewing is granted and msg is a message (can be None).
""" """
can = user.has_module_perms('admin') can = user.has_module_perms('admin')
return can, None if can else _("You don't have the right to view this" return (
" application.") can,
None if can else _("You don't have the right to view this"
" application."),
'admin'
)

View file

@ -39,5 +39,10 @@ def can_view(user):
viewing is granted and msg is a message (can be None). viewing is granted and msg is a message (can be None).
""" """
can = user.has_module_perms('machines') can = user.has_module_perms('machines')
return can, None if can else _("You don't have the right to view this" return (
" application.") can,
None if can else _("You don't have the right to view this"
" application."),
('machines',)
)

View file

@ -105,8 +105,11 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model):
A tuple with a boolean stating if edition is allowed and an A tuple with a boolean stating if edition is allowed and an
explanation message. explanation message.
""" """
return (user_request.has_perm('machines.change_machine_user'), return (
_("You don't have the right to change the machine's user.")) user_request.has_perm('machines.change_machine_user'),
_("You don't have the right to change the machine's user."),
('machines.change_machine_user',)
)
@staticmethod @staticmethod
def can_view_all(user_request, *_args, **_kwargs): def can_view_all(user_request, *_args, **_kwargs):
@ -115,9 +118,12 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model):
:param user_request: instance user qui fait l'edition :param user_request: instance user qui fait l'edition
:return: True ou False avec la raison de l'échec le cas échéant""" :return: True ou False avec la raison de l'échec le cas échéant"""
if not user_request.has_perm('machines.view_machine'): if not user_request.has_perm('machines.view_machine'):
return False, _("You don't have the right to view all the" return (
" machines.") False,
return True, None _("You don't have the right to view all the machines."),
('machines.view_machine',)
)
return True, None, None
@staticmethod @staticmethod
def can_create(user_request, userid, *_args, **_kwargs): def can_create(user_request, userid, *_args, **_kwargs):
@ -129,7 +135,7 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model):
try: try:
user = users.models.User.objects.get(pk=userid) user = users.models.User.objects.get(pk=userid)
except users.models.User.DoesNotExist: except users.models.User.DoesNotExist:
return False, _("Nonexistent user.") return False, _("Nonexistent user."), None
max_lambdauser_interfaces = (preferences.models.OptionalMachine max_lambdauser_interfaces = (preferences.models.OptionalMachine
.get_cached_value( .get_cached_value(
'max_lambdauser_interfaces' 'max_lambdauser_interfaces'
@ -137,15 +143,23 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model):
if not user_request.has_perm('machines.add_machine'): if not user_request.has_perm('machines.add_machine'):
if not (preferences.models.OptionalMachine if not (preferences.models.OptionalMachine
.get_cached_value('create_machine')): .get_cached_value('create_machine')):
return False, (_("You don't have the right to add a machine.")) return (
False,
_("You don't have the right to add a machine."),
('machines.add_machine',)
)
if user != user_request: if user != user_request:
return False, (_("You don't have the right to add a machine" return (
" to another user.")) False,
_("You don't have the right to add a machine"
" to another user."),
('machines.add_machine',)
)
if user.user_interfaces().count() >= max_lambdauser_interfaces: if user.user_interfaces().count() >= max_lambdauser_interfaces:
return False, (_("You reached the maximum number of interfaces" return False, _("You reached the maximum number of interfaces"
" that you are allowed to create yourself" " that you are allowed to create yourself"
" (%s)." % max_lambdauser_interfaces)) " (%s)." % max_lambdauser_interfaces), None
return True, None return True, None, None
def can_edit(self, user_request, *args, **kwargs): def can_edit(self, user_request, *args, **kwargs):
"""Vérifie qu'on peut bien éditer cette instance particulière (soit """Vérifie qu'on peut bien éditer cette instance particulière (soit
@ -154,16 +168,22 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model):
:param user_request: instance user qui fait l'edition :param user_request: instance user qui fait l'edition
:return: True ou False avec la raison le cas échéant""" :return: True ou False avec la raison le cas échéant"""
if self.user != user_request: if self.user != user_request:
if (not user_request.has_perm('machines.change_interface') or can_user, _, permissions = self.user.can_edit(
not self.user.can_edit( self.user,
self.user, user_request,
user_request, *args,
*args, **kwargs
**kwargs )
)[0]): if not (
return False, (_("You don't have the right to edit a machine" user_request.has_perm('machines.change_interface') and
" of another user.")) can_user):
return True, None return (
False,
_("You don't have the right to edit a machine"
" of another user."),
('machines.change_interface',) + permissions
)
return True, None, None
def can_delete(self, user_request, *args, **kwargs): def can_delete(self, user_request, *args, **kwargs):
"""Vérifie qu'on peut bien supprimer cette instance particulière (soit """Vérifie qu'on peut bien supprimer cette instance particulière (soit
@ -172,16 +192,22 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model):
:param user_request: instance user qui fait l'edition :param user_request: instance user qui fait l'edition
:return: True ou False avec la raison de l'échec le cas échéant""" :return: True ou False avec la raison de l'échec le cas échéant"""
if self.user != user_request: if self.user != user_request:
if (not user_request.has_perm('machines.change_interface') or can_user, _, permissions = self.user.can_edit(
not self.user.can_edit( self.user,
self.user, user_request,
user_request, *args,
*args, **kwargs
**kwargs )
)[0]): if not (
return False, _("You don't have the right to delete a machine" user_request.has_perm('machines.change_interface') and
" of another user.") can_user):
return True, None return (
False,
_("You don't have the right to delete a machine"
" of another user."),
('machines.change_interface',) + permissions
)
return True, None, None
def can_view(self, user_request, *_args, **_kwargs): def can_view(self, user_request, *_args, **_kwargs):
"""Vérifie qu'on peut bien voir cette instance particulière (soit """Vérifie qu'on peut bien voir cette instance particulière (soit
@ -191,9 +217,13 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model):
:return: True ou False avec la raison de l'échec le cas échéant""" :return: True ou False avec la raison de l'échec le cas échéant"""
if (not user_request.has_perm('machines.view_machine') and if (not user_request.has_perm('machines.view_machine') and
self.user != user_request): self.user != user_request):
return False, _("You don't have the right to view other machines" return (
" than yours.") False,
return True, None _("You don't have the right to view other machines"
" than yours."),
('machines.view_machine',)
)
return True, None, None
@cached_property @cached_property
def short_name(self): def short_name(self):
@ -285,9 +315,12 @@ class MachineType(RevMixin, AclMixin, models.Model):
message is acces is not allowed. message is acces is not allowed.
""" """
if not user_request.has_perm('machines.use_all_machinetype'): if not user_request.has_perm('machines.use_all_machinetype'):
return False, (_("You don't have the right to use all machine" return (
" types.")) False,
return True, None _("You don't have the right to use all machine types."),
('machines.use_all_machinetype',)
)
return True, None, None
def __str__(self): def __str__(self):
return self.name return self.name
@ -528,7 +561,11 @@ class IpType(RevMixin, AclMixin, models.Model):
restrictions restrictions
:param user_request: instance user qui fait l'edition :param user_request: instance user qui fait l'edition
:return: True ou False avec la raison de l'échec le cas échéant""" :return: True ou False avec la raison de l'échec le cas échéant"""
return user_request.has_perm('machines.use_all_iptype'), None return (
user_request.has_perm('machines.use_all_iptype'),
None,
('machines.use_all_iptype',)
)
def __str__(self): def __str__(self):
return self.name return self.name
@ -766,7 +803,11 @@ class Extension(RevMixin, AclMixin, models.Model):
restrictions restrictions
:param user_request: instance user qui fait l'edition :param user_request: instance user qui fait l'edition
:return: True ou False avec la raison de l'échec le cas échéant""" :return: True ou False avec la raison de l'échec le cas échéant"""
return user_request.has_perm('machines.use_all_extension'), None return (
user_request.has_perm('machines.use_all_extension'),
_("You cannot use all extensions."),
('machines.use_all_extension',)
)
def __str__(self): def __str__(self):
return self.name return self.name
@ -1222,31 +1263,42 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
try: try:
machine = Machine.objects.get(pk=machineid) machine = Machine.objects.get(pk=machineid)
except Machine.DoesNotExist: except Machine.DoesNotExist:
return False, _("Nonexistent machine.") return False, _("Nonexistent machine."), None
if not user_request.has_perm('machines.add_interface'): if not user_request.has_perm('machines.add_interface'):
if not (preferences.models.OptionalMachine if not (preferences.models.OptionalMachine
.get_cached_value('create_machine')): .get_cached_value('create_machine')):
return False, _("You can't add a machine.") return False, _("You can't add a machine."), ('machines.add_interface',)
max_lambdauser_interfaces = (preferences.models.OptionalMachine max_lambdauser_interfaces = (preferences.models.OptionalMachine
.get_cached_value( .get_cached_value(
'max_lambdauser_interfaces' 'max_lambdauser_interfaces'
)) ))
if machine.user != user_request: if machine.user != user_request:
return False, _("You don't have the right to add an interface" return (
" to a machine of another user.") False,
_("You don't have the right to add an interface"
" to a machine of another user."),
('machines.add_interface',)
)
if (machine.user.user_interfaces().count() >= if (machine.user.user_interfaces().count() >=
max_lambdauser_interfaces): max_lambdauser_interfaces):
return False, (_("You reached the maximum number of interfaces" return (
" that you are allowed to create yourself" False,
" (%s)." % max_lambdauser_interfaces)) _("You reached the maximum number of interfaces"
return True, None " that you are allowed to create yourself"
" (%s)." % max_lambdauser_interfaces),
('machines.add_interface',)
)
return True, None, None
@staticmethod @staticmethod
def can_change_machine(user_request, *_args, **_kwargs): def can_change_machine(user_request, *_args, **_kwargs):
"""Check if a user can change the machine associated with an """Check if a user can change the machine associated with an
Interface object """ Interface object """
return (user_request.has_perm('machines.change_interface_machine'), return (
_("Permission required to edit the machine.")) user_request.has_perm('machines.change_interface_machine'),
_("Permission required to edit the machine."),
('machines.change_interface_machine',)
)
def can_edit(self, user_request, *args, **kwargs): def can_edit(self, user_request, *args, **kwargs):
"""Verifie que l'user a les bons droits infra pour editer """Verifie que l'user a les bons droits infra pour editer
@ -1255,15 +1307,21 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
:param user_request: Utilisateur qui fait la requête :param user_request: Utilisateur qui fait la requête
:return: soit True, soit False avec la raison de l'échec""" :return: soit True, soit False avec la raison de l'échec"""
if self.machine.user != user_request: if self.machine.user != user_request:
if (not user_request.has_perm('machines.change_interface') or can_user, _, permissions = self.machine.user.can_edit(
not self.machine.user.can_edit( user_request,
user_request, *args,
*args, **kwargs
**kwargs )
)[0]): if not (
return False, _("You don't have the right to edit a machine of" user_request.has_perm('machines.change_interface') and
" another user.") can_user ):
return True, None return (
False,
_("You don't have the right to edit a machine of"
" another user."),
('machines.change_interface',) + permissions
)
return True, None, None
def can_delete(self, user_request, *args, **kwargs): def can_delete(self, user_request, *args, **kwargs):
"""Verifie que l'user a les bons droits delete object pour del """Verifie que l'user a les bons droits delete object pour del
@ -1272,15 +1330,21 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
:param user_request: Utilisateur qui fait la requête :param user_request: Utilisateur qui fait la requête
:return: soit True, soit False avec la raison de l'échec""" :return: soit True, soit False avec la raison de l'échec"""
if self.machine.user != user_request: if self.machine.user != user_request:
if (not user_request.has_perm('machines.change_interface') or can_user, _, permissions = self.machine.user.can_edit(
not self.machine.user.can_edit( user_request,
user_request, *args,
*args, **kwargs
**kwargs )
)[0]): if not (
return False, _("You don't have the right to edit a machine of" user_request.has_perm('machines.change_interface') and
" another user.") can_user):
return True, None return (
False,
_("You don't have the right to edit a machine of"
" another user."),
('machines.change_interface',) + permissions
)
return True, None, None
def can_view(self, user_request, *_args, **_kwargs): def can_view(self, user_request, *_args, **_kwargs):
"""Vérifie qu'on peut bien voir cette instance particulière avec """Vérifie qu'on peut bien voir cette instance particulière avec
@ -1290,9 +1354,12 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
:return: True ou False avec la raison de l'échec le cas échéant""" :return: True ou False avec la raison de l'échec le cas échéant"""
if (not user_request.has_perm('machines.view_interface') and if (not user_request.has_perm('machines.view_interface') and
self.machine.user != user_request): self.machine.user != user_request):
return False, _("You don't have the right to view machines other" return (
" than yours.") False,
return True, None _("You don't have the right to view machines other than yours."),
('machines.view_interface',)
)
return True, None, None
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(Interface, self).__init__(*args, **kwargs) super(Interface, self).__init__(*args, **kwargs)
@ -1340,19 +1407,26 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
try: try:
interface = Interface.objects.get(pk=interfaceid) interface = Interface.objects.get(pk=interfaceid)
except Interface.DoesNotExist: except Interface.DoesNotExist:
return False, _("Nonexistent interface.") return False, _("Nonexistent interface."), None
if not user_request.has_perm('machines.add_ipv6list'): if not user_request.has_perm('machines.add_ipv6list'):
if interface.machine.user != user_request: if interface.machine.user != user_request:
return False, _("You don't have the right to add an alias to a" return (
" machine of another user.") False,
return True, None _("You don't have the right to add an alias to a"
" machine of another user."),
('machines.add_ipv6list',)
)
return True, None, None
@staticmethod @staticmethod
def can_change_slaac_ip(user_request, *_args, **_kwargs): def can_change_slaac_ip(user_request, *_args, **_kwargs):
""" Check if a user can change the slaac value """ """ Check if a user can change the slaac value """
return (user_request.has_perm('machines.change_ipv6list_slaac_ip'), return (
_("Permission required to change the SLAAC value of an IPv6" user_request.has_perm('machines.change_ipv6list_slaac_ip'),
" address")) _("Permission required to change the SLAAC value of an IPv6"
" address"),
('machines.change_ipv6list_slaac_ip',)
)
def can_edit(self, user_request, *args, **kwargs): def can_edit(self, user_request, *args, **kwargs):
"""Verifie que l'user a les bons droits infra pour editer """Verifie que l'user a les bons droits infra pour editer
@ -1361,15 +1435,21 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
:param user_request: Utilisateur qui fait la requête :param user_request: Utilisateur qui fait la requête
:return: soit True, soit False avec la raison de l'échec""" :return: soit True, soit False avec la raison de l'échec"""
if self.interface.machine.user != user_request: if self.interface.machine.user != user_request:
if (not user_request.has_perm('machines.change_ipv6list') or can_user, _, permissions = self.interface.machine.user.can_edit(
not self.interface.machine.user.can_edit( user_request,
user_request, *args,
*args, **kwargs
**kwargs )
)[0]): if not (
return False, _("You don't have the right to edit a machine of" user_request.has_perm('machines.change_ipv6list') and
" another user.") can_user):
return True, None return (
False,
_("You don't have the right to edit a machine of"
" another user."),
('machines.change_ipv6list',)
)
return True, None, None
def can_delete(self, user_request, *args, **kwargs): def can_delete(self, user_request, *args, **kwargs):
"""Verifie que l'user a les bons droits delete object pour del """Verifie que l'user a les bons droits delete object pour del
@ -1378,15 +1458,20 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
:param user_request: Utilisateur qui fait la requête :param user_request: Utilisateur qui fait la requête
:return: soit True, soit False avec la raison de l'échec""" :return: soit True, soit False avec la raison de l'échec"""
if self.interface.machine.user != user_request: if self.interface.machine.user != user_request:
if (not user_request.has_perm('machines.change_ipv6list') or can_user, _, permissions = self.interface.machine.user.can_edit(
not self.interface.machine.user.can_edit( user_request,
user_request, *args,
*args, **kwargs
**kwargs )
)[0]): if not (user_request.has_perm('machines.change_ipv6list') and
return False, _("You don't have the right to edit a machine of" can_user):
" another user.") return (
return True, None False,
_("You don't have the right to edit a machine of"
" another user."),
('machines.change_ipv6list',) + permissions
)
return True, None, None
def can_view(self, user_request, *_args, **_kwargs): def can_view(self, user_request, *_args, **_kwargs):
"""Vérifie qu'on peut bien voir cette instance particulière avec """Vérifie qu'on peut bien voir cette instance particulière avec
@ -1396,9 +1481,12 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
:return: True ou False avec la raison de l'échec le cas échéant""" :return: True ou False avec la raison de l'échec le cas échéant"""
if (not user_request.has_perm('machines.view_ipv6list') and if (not user_request.has_perm('machines.view_ipv6list') and
self.interface.machine.user != user_request): self.interface.machine.user != user_request):
return False, _("You don't have the right to view machines other" return (
" than yours.") False,
return True, None _("You don't have the right to view machines other than yours."),
('machines.view_ipv6list',)
)
return True, None, None
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(Ipv6List, self).__init__(*args, **kwargs) super(Ipv6List, self).__init__(*args, **kwargs)
@ -1554,25 +1642,33 @@ class Domain(RevMixin, AclMixin, models.Model):
try: try:
interface = Interface.objects.get(pk=interfaceid) interface = Interface.objects.get(pk=interfaceid)
except Interface.DoesNotExist: except Interface.DoesNotExist:
return False, _("Nonexistent interface.") return False, _("Nonexistent interface."), None
if not user_request.has_perm('machines.add_domain'): if not user_request.has_perm('machines.add_domain'):
max_lambdauser_aliases = (preferences.models.OptionalMachine max_lambdauser_aliases = (preferences.models.OptionalMachine
.get_cached_value( .get_cached_value(
'max_lambdauser_aliases' 'max_lambdauser_aliases'
)) ))
if interface.machine.user != user_request: if interface.machine.user != user_request:
return False, _("You don't have the right to add an alias to a" return (
" machine of another user.") False,
_("You don't have the right to add an alias to a"
" machine of another user."),
('machines.add_domain',)
)
if Domain.objects.filter( if Domain.objects.filter(
cname__in=Domain.objects.filter( cname__in=Domain.objects.filter(
interface_parent__in=(interface.machine.user interface_parent__in=(interface.machine.user
.user_interfaces()) .user_interfaces())
) )
).count() >= max_lambdauser_aliases: ).count() >= max_lambdauser_aliases:
return False, _("You reached the maximum number of alias that" return (
" you are allowed to create yourself (%s). " False,
% max_lambdauser_aliases) _("You reached the maximum number of alias that"
return True, None " you are allowed to create yourself (%s). "
% max_lambdauser_aliases),
('machines.add_domain',)
)
return True, None, None
def can_edit(self, user_request, *_args, **_kwargs): def can_edit(self, user_request, *_args, **_kwargs):
"""Verifie que l'user a les bons droits pour editer """Verifie que l'user a les bons droits pour editer
@ -1582,9 +1678,13 @@ class Domain(RevMixin, AclMixin, models.Model):
:return: soit True, soit False avec la raison de l'échec""" :return: soit True, soit False avec la raison de l'échec"""
if (not user_request.has_perm('machines.change_domain') and if (not user_request.has_perm('machines.change_domain') and
self.get_source_interface.machine.user != user_request): self.get_source_interface.machine.user != user_request):
return False, _("You don't have the right to edit an alias of a" return (
" machine of another user.") False,
return True, None _("You don't have the right to edit an alias of a"
" machine of another user."),
('machines.change_domain',)
)
return True, None, None
def can_delete(self, user_request, *_args, **_kwargs): def can_delete(self, user_request, *_args, **_kwargs):
"""Verifie que l'user a les bons droits delete object pour del """Verifie que l'user a les bons droits delete object pour del
@ -1594,9 +1694,13 @@ class Domain(RevMixin, AclMixin, models.Model):
:return: soit True, soit False avec la raison de l'échec""" :return: soit True, soit False avec la raison de l'échec"""
if (not user_request.has_perm('machines.delete_domain') and if (not user_request.has_perm('machines.delete_domain') and
self.get_source_interface.machine.user != user_request): self.get_source_interface.machine.user != user_request):
return False, _("You don't have the right to delete an alias of a" return (
" machine of another user.") False,
return True, None _("You don't have the right to delete an alias of a"
" machine of another user."),
('machines.delete_domain',)
)
return True, None, None
def can_view(self, user_request, *_args, **_kwargs): def can_view(self, user_request, *_args, **_kwargs):
"""Vérifie qu'on peut bien voir cette instance particulière avec """Vérifie qu'on peut bien voir cette instance particulière avec
@ -1606,9 +1710,12 @@ class Domain(RevMixin, AclMixin, models.Model):
:return: True ou False avec la raison de l'échec le cas échéant""" :return: True ou False avec la raison de l'échec le cas échéant"""
if (not user_request.has_perm('machines.view_domain') and if (not user_request.has_perm('machines.view_domain') and
self.get_source_interface.machine.user != user_request): self.get_source_interface.machine.user != user_request):
return False, _("You don't have the right to view machines other" return (
" than yours.") False,
return True, None _("You don't have the right to view machines other than yours."),
('machines.view_domain',)
)
return True, None, None
def __str__(self): def __str__(self):
return str(self.name) + str(self.extension) return str(self.name) + str(self.extension)
@ -1840,11 +1947,14 @@ class OuverturePortList(RevMixin, AclMixin, models.Model):
:param user_request: Utilisateur qui fait la requête :param user_request: Utilisateur qui fait la requête
:return: soit True, soit False avec la raison de l'échec""" :return: soit True, soit False avec la raison de l'échec"""
if not user_request.has_perm('machines.delete_ouvertureportlist'): if not user_request.has_perm('machines.delete_ouvertureportlist'):
return False, _("You don't have the right to delete a ports" return (
" opening list.") False,
_("You don't have the right to delete a ports opening list."),
('machines.delete_ouvertureportlist',)
)
if self.interface_set.all(): if self.interface_set.all():
return False, _("This ports opening list is used.") return False, _("This ports opening list is used."), None
return True, None return True, None, None
def __str__(self): def __str__(self):
return self.name return self.name

View file

@ -39,6 +39,10 @@ def can_view(user):
viewing is granted and msg is a message (can be None). viewing is granted and msg is a message (can be None).
""" """
can = user.has_module_perms('preferences') can = user.has_module_perms('preferences')
return can, None if can else _("You don't have the right to view this" return (
" application.") can,
None if can else _("You don't have the right to view this"
" application."),
('preferences',)
)

View file

@ -43,7 +43,7 @@ from reversion import revisions as reversion
from importlib import import_module from importlib import import_module
from re2o.settings_local import OPTIONNAL_APPS_RE2O from re2o.settings_local import OPTIONNAL_APPS_RE2O
from re2o.views import form from re2o.views import form
from re2o.acl import can_create, can_edit, can_delete_set, can_view_all, can_delete from re2o.acl import can_create, can_edit, can_delete_set, can_view_all, can_delete, acl_error_message
from .forms import MailContactForm, DelMailContactForm from .forms import MailContactForm, DelMailContactForm
from .forms import ( from .forms import (
@ -130,10 +130,9 @@ def edit_options(request, section):
return redirect(reverse('preferences:display-options')) return redirect(reverse('preferences:display-options'))
options_instance, _created = model.objects.get_or_create() options_instance, _created = model.objects.get_or_create()
can, msg = options_instance.can_edit(request.user) can, msg, permissions = options_instance.can_edit(request.user)
if not can: if not can:
messages.error(request, msg or _("You don't have the right to edit" messages.error(request, acl_error_message(msg, permissions))
" this option."))
return redirect(reverse('index')) return redirect(reverse('index'))
options = form_instance( options = form_instance(
request.POST or None, request.POST or None,

View file

@ -36,6 +36,28 @@ from django.shortcuts import redirect
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from re2o.utils import get_group_having_permission
def acl_error_message(msg, permissions):
"""Create an error message for msg and permissions."""
groups = ", ".join([
g.name for g in get_group_having_permission(*permissions)
])
message = msg or _("You don't have the right to edit"
" this option.")
if groups:
return message + _(
" You need to be a member of one of those"
" groups : %s"
) % groups
else:
return message + " No group have the %s permission(s) !" % " or ".join([
",".join(permissions[:-1]),
permissions[-1]]
if len(permissions) > 2 else permissions
)
def acl_base_decorator(method_name, *targets, on_instance=True): def acl_base_decorator(method_name, *targets, on_instance=True):
"""Base decorator for acl. It checks if the `request.user` has the """Base decorator for acl. It checks if the `request.user` has the
@ -53,9 +75,10 @@ def acl_base_decorator(method_name, *targets, on_instance=True):
then no error will be triggered, the decorator will act as if then no error will be triggered, the decorator will act as if
permission was granted. This is to allow you to run ACL tests on 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 fields only. If the method exists, it has to return a 2-tuple
`(can, reason)` with `can` being a boolean stating whether the `(can, reason, permissions)` with `can` being a boolean stating
access is granted and `reason` a message to be displayed if `can` whether the access is granted, `reason` a message to be
equals `False` (can be `None`) displayed if `can` equals `False` (can be `None`) and `permissions`
a list of permissions needed for access (can be `None`).
*targets: The targets. Targets are specified like a sequence of models *targets: The targets. Targets are specified like a sequence of models
and fields names. As an example and fields names. As an example
``` ```
@ -139,7 +162,7 @@ ModelC)
target = target.get_instance(*args, **kwargs) target = target.get_instance(*args, **kwargs)
instances.append(target) instances.append(target)
except target.DoesNotExist: except target.DoesNotExist:
yield False, _("Nonexistent entry.") yield False, _("Nonexistent entry."), []
return return
if hasattr(target, method_name): if hasattr(target, method_name):
can_fct = getattr(target, method_name) can_fct = getattr(target, method_name)
@ -148,11 +171,12 @@ ModelC)
can_change_fct = getattr(target, 'can_change_' + field) can_change_fct = getattr(target, 'can_change_' + field)
yield can_change_fct(request.user, *args, **kwargs) yield can_change_fct(request.user, *args, **kwargs)
error_messages = [ error_messages = []
x[1] for x in chain.from_iterable( for target, fields in group_targets():
process_target(x[0], x[1]) for x in group_targets() for can, msg, permissions in process_target(target, fields):
) if not x[0] if not can:
] error_messages.append(acl_error_message(msg, permissions))
if error_messages: if error_messages:
for msg in error_messages: for msg in error_messages:
messages.error( messages.error(

View file

@ -69,7 +69,7 @@ class FieldPermissionModelMixin:
# Try to find a user setting that qualifies them for permission. # Try to find a user setting that qualifies them for permission.
for perm in checks: for perm in checks:
if callable(perm): if callable(perm):
result, _reason = perm(user_request=user) result, _reason, _permissions = perm(user_request=user)
if result is not None: if result is not None:
return result return result
else: else:

View file

@ -104,12 +104,12 @@ class AclMixin(object):
un object un object
:param user_request: instance utilisateur qui fait la requête :param user_request: instance utilisateur qui fait la requête
:return: soit True, soit False avec la raison de l'échec""" :return: soit True, soit False avec la raison de l'échec"""
permission = cls.get_modulename() + '.add_' + cls.get_classname()
return ( return (
user_request.has_perm( user_request.has_perm(permission),
cls.get_modulename() + '.add_' + cls.get_classname() _("You don't have the right to create a %s object.")
), % cls.get_classname(),
(_("You don't have the right to create a %s object.") (permission,)
% cls.get_classname())
) )
def can_edit(self, user_request, *_args, **_kwargs): def can_edit(self, user_request, *_args, **_kwargs):
@ -118,12 +118,12 @@ class AclMixin(object):
:param self: Instance à editer :param self: Instance à editer
:param user_request: Utilisateur qui fait la requête :param user_request: Utilisateur qui fait la requête
:return: soit True, soit False avec la raison de l'échec""" :return: soit True, soit False avec la raison de l'échec"""
permission = self.get_modulename() + '.change_' + self.get_classname()
return ( return (
user_request.has_perm( user_request.has_perm(permission),
self.get_modulename() + '.change_' + self.get_classname() _("You don't have the right to edit a %s object.")
), % self.get_classname(),
(_("You don't have the right to edit a %s object.") (permission,)
% self.get_classname())
) )
def can_delete(self, user_request, *_args, **_kwargs): def can_delete(self, user_request, *_args, **_kwargs):
@ -132,12 +132,12 @@ class AclMixin(object):
:param self: Instance à delete :param self: Instance à delete
:param user_request: Utilisateur qui fait la requête :param user_request: Utilisateur qui fait la requête
:return: soit True, soit False avec la raison de l'échec""" :return: soit True, soit False avec la raison de l'échec"""
permission = self.get_modulename() + '.delete_' + self.get_classname()
return ( return (
user_request.has_perm( user_request.has_perm(permission),
self.get_modulename() + '.delete_' + self.get_classname() _("You don't have the right to delete a %s object.")
), % self.get_classname(),
(_("You don't have the right to delete a %s object.") (permission,)
% self.get_classname())
) )
@classmethod @classmethod
@ -146,12 +146,12 @@ class AclMixin(object):
droit particulier view objet correspondant droit particulier view objet correspondant
:param user_request: instance user qui fait l'edition :param user_request: instance user qui fait l'edition
:return: True ou False avec la raison de l'échec le cas échéant""" :return: True ou False avec la raison de l'échec le cas échéant"""
permission = cls.get_modulename() + '.view_' + cls.get_classname()
return ( return (
user_request.has_perm( user_request.has_perm(permission),
cls.get_modulename() + '.view_' + cls.get_classname() _("You don't have the right to view every %s object.")
), % cls.get_classname(),
(_("You don't have the right to view every %s object.") (permission,)
% cls.get_classname())
) )
def can_view(self, user_request, *_args, **_kwargs): def can_view(self, user_request, *_args, **_kwargs):
@ -160,11 +160,11 @@ class AclMixin(object):
:param self: instance à voir :param self: instance à voir
:param user_request: instance user qui fait l'edition :param user_request: instance user qui fait l'edition
:return: True ou False avec la raison de l'échec le cas échéant""" :return: True ou False avec la raison de l'échec le cas échéant"""
permission = self.get_modulename() + '.view_' + self.get_classname()
return ( return (
user_request.has_perm( user_request.has_perm(permission),
self.get_modulename() + '.view_' + self.get_classname() _("You don't have the right to view a %s object.")
), % self.get_classname(),
(_("You don't have the right to view a %s object.") (permission,)
% self.get_classname())
) )

View file

@ -147,6 +147,7 @@ def get_callback(tag_name, obj=None):
return acl_fct( return acl_fct(
lambda x: ( lambda x: (
not any(not sys.modules[o].can_view(x)[0] for o in obj), not any(not sys.modules[o].can_view(x)[0] for o in obj),
None,
None None
), ),
False False
@ -155,28 +156,29 @@ def get_callback(tag_name, obj=None):
return acl_fct( return acl_fct(
lambda x: ( lambda x: (
not any(not sys.modules[o].can_view(x)[0] for o in obj), not any(not sys.modules[o].can_view(x)[0] for o in obj),
None,
None None
), ),
True True
) )
if tag_name == 'can_edit_history': if tag_name == 'can_edit_history':
return acl_fct( return acl_fct(
lambda user: (user.has_perm('admin.change_logentry'), None), lambda user: (user.has_perm('admin.change_logentry'), None, None),
False False
) )
if tag_name == 'cannot_edit_history': if tag_name == 'cannot_edit_history':
return acl_fct( return acl_fct(
lambda user: (user.has_perm('admin.change_logentry'), None), lambda user: (user.has_perm('admin.change_logentry'), None, None),
True True
) )
if tag_name == 'can_view_any_app': if tag_name == 'can_view_any_app':
return acl_fct( return acl_fct(
lambda x: (any(sys.modules[o].can_view(x)[0] for o in obj), None), lambda x: (any(sys.modules[o].can_view(x)[0] for o in obj), None, None),
False False
) )
if tag_name == 'cannot_view_any_app': if tag_name == 'cannot_view_any_app':
return acl_fct( return acl_fct(
lambda x: (any(sys.modules[o].can_view(x)[0] for o in obj), None), lambda x: (any(sys.modules[o].can_view(x)[0] for o in obj), None, None),
True True
) )
@ -194,8 +196,8 @@ def acl_fct(callback, reverse):
def acl_fct_reverse(user, *args, **kwargs): def acl_fct_reverse(user, *args, **kwargs):
"""The cannot_xxx checker callback""" """The cannot_xxx checker callback"""
can, msg = callback(user, *args, **kwargs) can, msg, permissions = callback(user, *args, **kwargs)
return not can, msg return not can, msg, permissions
return acl_fct_reverse if reverse else acl_fct_normal return acl_fct_reverse if reverse else acl_fct_normal
@ -217,7 +219,7 @@ def acl_history_filter(parser, token):
assert token.contents == 'acl_end' assert token.contents == 'acl_end'
return AclNode(callback, oknodes, konodes) return AclNode(tag_name, callback, oknodes, konodes)
@register.tag('can_view_any_app') @register.tag('can_view_any_app')
@ -254,7 +256,7 @@ def acl_app_filter(parser, token):
assert token.contents == 'acl_end' assert token.contents == 'acl_end'
return AclNode(callback, oknodes, konodes) return AclNode(tag_name, callback, oknodes, konodes)
@register.tag('can_change') @register.tag('can_change')
@ -264,7 +266,7 @@ def acl_change_filter(parser, token):
try: try:
tag_content = token.split_contents() tag_content = token.split_contents()
# tag_name = tag_content[0] tag_name = tag_content[0]
model_name = tag_content[1] model_name = tag_content[1]
field_name = tag_content[2] field_name = tag_content[2]
args = tag_content[3:] args = tag_content[3:]
@ -291,7 +293,7 @@ def acl_change_filter(parser, token):
# {% can_create_end %} # {% can_create_end %}
assert token.contents == 'acl_end' assert token.contents == 'acl_end'
return AclNode(callback, oknodes, konodes, *args) return AclNode(tag_name, callback, oknodes, konodes, *args)
@register.tag('can_create') @register.tag('can_create')
@ -333,7 +335,7 @@ def acl_model_filter(parser, token):
# {% can_create_end %} # {% can_create_end %}
assert token.contents == 'acl_end' assert token.contents == 'acl_end'
return AclNode(callback, oknodes, konodes, *args) return AclNode(tag_name, callback, oknodes, konodes, *args)
@register.tag('can_edit') @register.tag('can_edit')
@ -377,7 +379,8 @@ class AclNode(Node):
"""A node for the compiled ACL block when acl callback doesn't require """A node for the compiled ACL block when acl callback doesn't require
context.""" context."""
def __init__(self, callback, oknodes, konodes, *args): def __init__(self, tag_name, callback, oknodes, konodes, *args):
self.tag_name = tag_name
self.callback = callback self.callback = callback
self.oknodes = oknodes self.oknodes = oknodes
self.konodes = konodes self.konodes = konodes
@ -388,11 +391,14 @@ class AclNode(Node):
if context['user'].is_anonymous(): if context['user'].is_anonymous():
can = False can = False
else: else:
can, _ = self.callback(context['user'], *(resolved_args)) can, _, _ = self.callback(context['user'], *(resolved_args))
if can: if can:
return self.oknodes.render(context) return self.oknodes.render(context)
return self.konodes.render(context) return self.konodes.render(context)
def __repr__(self):
return '<AclNode %s>' % self.tag_name
class AclInstanceNode(Node): class AclInstanceNode(Node):
"""A node for the compiled ACL block when acl is based on instance""" """A node for the compiled ACL block when acl is based on instance"""
@ -410,7 +416,10 @@ class AclInstanceNode(Node):
if context['user'].is_anonymous(): if context['user'].is_anonymous():
can = False can = False
else: else:
can, _ = callback(context['user'], *(resolved_args)) can, _, _ = callback(context['user'], *(resolved_args))
if can: if can:
return self.oknodes.render(context) return self.oknodes.render(context)
return self.konodes.render(context) return self.konodes.render(context)
def __repr__(self):
return '<AclInstanceNode %s>' % self.tag_name

View file

@ -38,12 +38,23 @@ from __future__ import unicode_literals
from django.utils import timezone from django.utils import timezone
from django.db.models import Q from django.db.models import Q
from django.contrib.auth.models import Permission
from cotisations.models import Cotisation, Facture, Vente from cotisations.models import Cotisation, Facture, Vente
from machines.models import Interface, Machine from machines.models import Interface, Machine
from users.models import Adherent, User, Ban, Whitelist from users.models import Adherent, User, Ban, Whitelist
from preferences.models import AssoOption from preferences.models import AssoOption
def get_group_having_permission(*permission_name):
"""Returns every group having the permission `permission_name`
"""
groups = set()
for name in permission_name:
app_label, codename = name.split('.')
permission = Permission.objects.get(content_type__app_label=app_label, codename=codename)
groups = groups.union(permission.group_set.all())
return groups
def all_adherent(search_time=None, including_asso=True): def all_adherent(search_time=None, including_asso=True):
""" Fonction renvoyant tous les users adherents. Optimisee pour n'est """ Fonction renvoyant tous les users adherents. Optimisee pour n'est
qu'une seule requete sql qu'une seule requete sql

View file

@ -72,21 +72,26 @@ class Ticket(AclMixin, models.Model):
""" Check that the user has the right to view the ticket """ Check that the user has the right to view the ticket
or that it is the author""" or that it is the author"""
if (not user_request.has_perm('tickets.view_ticket') and self.user != user_request): if (not user_request.has_perm('tickets.view_ticket') and self.user != user_request):
return False, _("You don't have the right to view other tickets than yours.") return (
False,
_("You don't have the right to view other tickets than yours."),
('tickets.view_ticket',)
)
else: else:
return True, None return True, None, None
@staticmethod @staticmethod
def can_view_all(user_request, *_args, **_kwargs): def can_view_all(user_request, *_args, **_kwargs):
""" Check that the user has access to the list of all tickets""" """ Check that the user has access to the list of all tickets"""
return( return(
user_request.has_perm('tickets.view_tickets'), user_request.has_perm('tickets.view_tickets'),
_("You don't have the right to view the list of tickets.") _("You don't have the right to view the list of tickets."),
('tickets.view_tickets',)
) )
def can_create(user_request,*_args, **_kwargs): def can_create(user_request,*_args, **_kwargs):
""" Authorise all users to open tickets """ """ Authorise all users to open tickets """
return True,None return True, None, None
@receiver(post_save, sender=Ticket) @receiver(post_save, sender=Ticket)
def ticket_post_save(**kwargs): def ticket_post_save(**kwargs):

View file

@ -39,6 +39,10 @@ def can_view(user):
viewing is granted and msg is a message (can be None). viewing is granted and msg is a message (can be None).
""" """
can = user.has_module_perms('topologie') can = user.has_module_perms('topologie')
return can, None if can else _("You don't have the right to view this" return (
" application.") can,
None if can else _("You don't have the right to view this"
" application."),
('topologie',)
)

View file

@ -38,6 +38,10 @@ def can_view(user):
viewing is granted and msg is a message (can be None). viewing is granted and msg is a message (can be None).
""" """
can = user.has_module_perms('users') can = user.has_module_perms('users')
return can, None if can else _("You don't have the right to view this" return (
" application.") can,
None if can else _("You don't have the right to view this"
" application."),
('users',)
)

View file

@ -864,29 +864,38 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
if (self == user_request or if (self == user_request or
user_request.has_perm('users.change_user') or user_request.has_perm('users.change_user') or
user_request.adherent in self.club.administrators.all()): user_request.adherent in self.club.administrators.all()):
return True, None return True, None, None
else: else:
return False, _("You don't have the right to edit this club.") return False, _("You don't have the right to edit this club."), ('users.change_user',)
else: else:
if self == user_request: if self == user_request:
return True, None return True, None, None
elif user_request.has_perm('users.change_all_users'): elif user_request.has_perm('users.change_all_users'):
return True, None return True, None, None
elif user_request.has_perm('users.change_user'): elif user_request.has_perm('users.change_user'):
if self.groups.filter(listright__critical=True): if self.groups.filter(listright__critical=True):
return False, (_("User with critical rights, can't be" return (
" edited.")) False,
_("User with critical rights, can't be edited. "),
('users.change_all_users',)
)
elif self == AssoOption.get_cached_value('utilisateur_asso'): elif self == AssoOption.get_cached_value('utilisateur_asso'):
return False, (_("Impossible to edit the organisation's" return (
" user without the 'change_all_users'" False,
" right.")) _("Impossible to edit the organisation's"
" user without the 'change_all_users' right."),
('users.change_all_users', )
)
else: else:
return True, None return True, None, None
elif user_request.has_perm('users.change_all_users'): elif user_request.has_perm('users.change_all_users'):
return True, None return True, None, None
else: else:
return False, (_("You don't have the right to edit another" return (
" user.")) False,
_("You don't have the right to edit another user."),
('users.change_user', 'users.change_all_users')
)
def can_change_password(self, user_request, *_args, **_kwargs): def can_change_password(self, user_request, *_args, **_kwargs):
"""Check if a user can change a user's password """Check if a user can change a user's password
@ -901,27 +910,34 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
if (self == user_request or if (self == user_request or
user_request.has_perm('users.change_user_password') or user_request.has_perm('users.change_user_password') or
user_request.adherent in self.club.administrators.all()): user_request.adherent in self.club.administrators.all()):
return True, None return True, None, None
else: else:
return False, _("You don't have the right to edit this club.") return (
False,
_("You don't have the right to edit this club."),
('users.change_user_password',)
)
else: else:
if (self == user_request or if (self == user_request or
user_request.has_perm('users.change_user_groups')): user_request.has_perm('users.change_user_groups')):
# Peut éditer les groupes d'un user, # Peut éditer les groupes d'un user,
# c'est un privilège élevé, True # c'est un privilège élevé, True
return True, None return True, None, None
elif (user_request.has_perm('users.change_user') and elif (user_request.has_perm('users.change_user') and
not self.groups.all()): not self.groups.all()):
return True, None return True, None, None
else: else:
return False, (_("You don't have the right to edit another" return (
" user.")) False,
_("You don't have the right to edit another user."),
('users.change_user_groups', 'users.change_user')
)
def check_selfpasswd(self, user_request, *_args, **_kwargs): def check_selfpasswd(self, user_request, *_args, **_kwargs):
""" Returns (True, None) if user_request is self, else returns """ Returns (True, None, None) if user_request is self, else returns
(False, None) (False, None, None)
""" """
return user_request == self, None return user_request == self, None, None
def can_change_room(self, user_request, *_args, **_kwargs): def can_change_room(self, user_request, *_args, **_kwargs):
""" Check if a user can change a room """ Check if a user can change a room
@ -932,9 +948,13 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
""" """
if not ((self.pk == user_request.pk and OptionalUser.get_cached_value('self_change_room')) if not ((self.pk == user_request.pk and OptionalUser.get_cached_value('self_change_room'))
or user_request.has_perm('users.change_user')): or user_request.has_perm('users.change_user')):
return False, _("Permission required to change the room.") return (
False,
_("Permission required to change the room."),
('users.change_user',)
)
else: else:
return True, None return True, None, None
@staticmethod @staticmethod
def can_change_state(user_request, *_args, **_kwargs): def can_change_state(user_request, *_args, **_kwargs):
@ -946,7 +966,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
""" """
return ( return (
user_request.has_perm('users.change_user_state'), user_request.has_perm('users.change_user_state'),
_("Permission required to change the state.") _("Permission required to change the state."),
('users.change_user_state',)
) )
def can_change_shell(self, user_request, *_args, **_kwargs): def can_change_shell(self, user_request, *_args, **_kwargs):
@ -958,9 +979,13 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
""" """
if not ((self.pk == user_request.pk and OptionalUser.get_cached_value('self_change_shell')) if not ((self.pk == user_request.pk and OptionalUser.get_cached_value('self_change_shell'))
or user_request.has_perm('users.change_user_shell')): or user_request.has_perm('users.change_user_shell')):
return False, _("Permission required to change the shell.") return (
False,
_("Permission required to change the shell."),
('users.change_user_shell',)
)
else: else:
return True, None return True, None, None
@staticmethod @staticmethod
def can_change_local_email_redirect(user_request, *_args, **_kwargs): def can_change_local_email_redirect(user_request, *_args, **_kwargs):
@ -972,7 +997,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
""" """
return ( return (
OptionalUser.get_cached_value('local_email_accounts_enabled'), OptionalUser.get_cached_value('local_email_accounts_enabled'),
_("Local email accounts must be enabled.") _("Local email accounts must be enabled."),
None
) )
@staticmethod @staticmethod
@ -985,7 +1011,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
""" """
return ( return (
OptionalUser.get_cached_value('local_email_accounts_enabled'), OptionalUser.get_cached_value('local_email_accounts_enabled'),
_("Local email accounts must be enabled.") _("Local email accounts must be enabled."),
None
) )
@staticmethod @staticmethod
@ -998,7 +1025,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
""" """
return ( return (
user_request.has_perm('users.change_user_force'), user_request.has_perm('users.change_user_force'),
_("Permission required to force the move.") _("Permission required to force the move."),
('users.change_user_force',)
) )
@staticmethod @staticmethod
@ -1011,7 +1039,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
""" """
return ( return (
user_request.has_perm('users.change_user_groups'), user_request.has_perm('users.change_user_groups'),
_("Permission required to edit the user's groups of rights.") _("Permission required to edit the user's groups of rights."),
('users.change_user_groups')
) )
@staticmethod @staticmethod
@ -1023,7 +1052,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
""" """
return ( return (
user_request.is_superuser, user_request.is_superuser,
_("'superuser' right required to edit the superuser flag.") _("'superuser' right required to edit the superuser flag."),
[]
) )
def can_view(self, user_request, *_args, **_kwargs): def can_view(self, user_request, *_args, **_kwargs):
@ -1039,16 +1069,23 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
user_request.has_perm('users.view_user') or user_request.has_perm('users.view_user') or
user_request.adherent in self.club.administrators.all() or user_request.adherent in self.club.administrators.all() or
user_request.adherent in self.club.members.all()): user_request.adherent in self.club.members.all()):
return True, None return True, None, None
else: else:
return False, _("You don't have the right to view this club.") return (
False,
_("You don't have the right to view this club."),
('users.view_user',)
)
else: else:
if (self == user_request or if (self == user_request or
user_request.has_perm('users.view_user')): user_request.has_perm('users.view_user')):
return True, None return True, None, None
else: else:
return False, (_("You don't have the right to view another" return (
" user.")) False,
_("You don't have the right to view another user."),
('users.view_user',)
)
@staticmethod @staticmethod
def can_view_all(user_request, *_args, **_kwargs): def can_view_all(user_request, *_args, **_kwargs):
@ -1060,7 +1097,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
""" """
return ( return (
user_request.has_perm('users.view_user'), user_request.has_perm('users.view_user'),
_("You don't have the right to view the list of users.") _("You don't have the right to view the list of users."),
('users.view_user',)
) )
def can_delete(self, user_request, *_args, **_kwargs): def can_delete(self, user_request, *_args, **_kwargs):
@ -1073,7 +1111,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
""" """
return ( return (
user_request.has_perm('users.delete_user'), user_request.has_perm('users.delete_user'),
_("You don't have the right to delete this user.") _("You don't have the right to delete this user."),
('users.delete_user',)
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -1160,15 +1199,16 @@ class Adherent(User):
""" """
if (not user_request.is_authenticated and if (not user_request.is_authenticated and
not OptionalUser.get_cached_value('self_adhesion')): not OptionalUser.get_cached_value('self_adhesion')):
return False, None return False, _("Self adhesion is disabled."), None
else: else:
if (OptionalUser.get_cached_value('all_can_create_adherent') or if (OptionalUser.get_cached_value('all_can_create_adherent') or
OptionalUser.get_cached_value('self_adhesion')): OptionalUser.get_cached_value('self_adhesion')):
return True, None return True, None, None
else: else:
return ( return (
user_request.has_perm('users.add_user'), user_request.has_perm('users.add_user'),
_("You don't have the right to create a user.") _("You don't have the right to create a user."),
('users.add_user',)
) )
def clean(self, *args, **kwargs): def clean(self, *args, **kwargs):
@ -1216,14 +1256,15 @@ class Club(User):
an user or if the `options.all_can_create` is set. an user or if the `options.all_can_create` is set.
""" """
if not user_request.is_authenticated: if not user_request.is_authenticated:
return False, None return False, _("You must be authenticated."), None
else: else:
if OptionalUser.get_cached_value('all_can_create_club'): if OptionalUser.get_cached_value('all_can_create_club'):
return True, None return True, None, None
else: else:
return ( return (
user_request.has_perm('users.add_user'), user_request.has_perm('users.add_user'),
_("You don't have the right to create a club.") _("You don't have the right to create a club."),
('users.add_user',)
) )
@staticmethod @staticmethod
@ -1235,13 +1276,17 @@ class Club(User):
message. message.
""" """
if user_request.has_perm('users.view_user'): if user_request.has_perm('users.view_user'):
return True, None return True, None, None
if (hasattr(user_request, 'is_class_adherent') and if (hasattr(user_request, 'is_class_adherent') and
user_request.is_class_adherent): user_request.is_class_adherent):
if (user_request.adherent.club_administrator.all() or if (user_request.adherent.club_administrator.all() or
user_request.adherent.club_members.all()): user_request.adherent.club_members.all()):
return True, None return True, None, None
return False, _("You don't have the right to view the list of users.") return (
False,
_("You don't have the right to view the list of users."),
('users.view_user',)
)
@classmethod @classmethod
def get_instance(cls, clubid, *_args, **_kwargs): def get_instance(cls, clubid, *_args, **_kwargs):
@ -1553,10 +1598,13 @@ class Ban(RevMixin, AclMixin, models.Model):
""" """
if (not user_request.has_perm('users.view_ban') and if (not user_request.has_perm('users.view_ban') and
self.user != user_request): self.user != user_request):
return False, (_("You don't have the right to view bans other" return (
" than yours.")) False,
_("You don't have the right to view bans other than yours."),
('users.view_ban',)
)
else: else:
return True, None return True, None, None
def __str__(self): def __str__(self):
return str(self.user) + ' ' + str(self.raison) return str(self.user) + ' ' + str(self.raison)
@ -1620,10 +1668,13 @@ class Whitelist(RevMixin, AclMixin, models.Model):
""" """
if (not user_request.has_perm('users.view_whitelist') and if (not user_request.has_perm('users.view_whitelist') and
self.user != user_request): self.user != user_request):
return False, (_("You don't have the right to view whitelists" return (
" other than yours.")) False,
_("You don't have the right to view whitelists other than yours."),
('users.view_whitelist',)
)
else: else:
return True, None return True, None, None
def __str__(self): def __str__(self):
return str(self.user) + ' ' + str(self.raison) return str(self.user) + ' ' + str(self.raison)
@ -1892,17 +1943,29 @@ class EMailAddress(RevMixin, AclMixin, models.Model):
a local email account. a local email account.
""" """
if user_request.has_perm('users.add_emailaddress'): if user_request.has_perm('users.add_emailaddress'):
return True, None return True, None, None
if not OptionalUser.get_cached_value('local_email_accounts_enabled'): if not OptionalUser.get_cached_value('local_email_accounts_enabled'):
return False, _("The local email accounts are not enabled.") return (
if int(user_request.id) != int(userid): False,
return False, _("You don't have the right to add a local email" _("The local email accounts are not enabled."),
" account to another user.") None
elif user_request.email_address.count() >= OptionalUser.get_cached_value('max_email_address'):
return False, _("You reached the limit of {} local email accounts.").format(
OptionalUser.get_cached_value('max_email_address')
) )
return True, None if int(user_request.id) != int(userid):
return (
False,
_("You don't have the right to add a local email"
" account to another user."),
('users.add_emailaddress',)
)
elif user_request.email_address.count() >= OptionalUser.get_cached_value('max_email_address'):
return (
False,
_("You reached the limit of {} local email accounts.").format(
OptionalUser.get_cached_value('max_email_address')
),
None
)
return True, None, None
def can_view(self, user_request, *_args, **_kwargs): def can_view(self, user_request, *_args, **_kwargs):
"""Check if a user can view the local email account """Check if a user can view the local email account
@ -1915,13 +1978,21 @@ class EMailAddress(RevMixin, AclMixin, models.Model):
the local email account. the local email account.
""" """
if user_request.has_perm('users.view_emailaddress'): if user_request.has_perm('users.view_emailaddress'):
return True, None return True, None, None
if not OptionalUser.get_cached_value('local_email_accounts_enabled'): if not OptionalUser.get_cached_value('local_email_accounts_enabled'):
return False, _("The local email accounts are not enabled.") return (
False,
_("The local email accounts are not enabled."),
None
)
if user_request == self.user: if user_request == self.user:
return True, None return True, None, None
return False, _("You don't have the right to edit another user's local" return (
" email account.") False,
_("You don't have the right to edit another user's local"
" email account."),
('users.view_emailaddress',)
)
def can_delete(self, user_request, *_args, **_kwargs): def can_delete(self, user_request, *_args, **_kwargs):
"""Check if a user can delete the alias """Check if a user can delete the alias
@ -1934,16 +2005,24 @@ class EMailAddress(RevMixin, AclMixin, models.Model):
the local email account. the local email account.
""" """
if self.local_part == self.user.pseudo.lower(): if self.local_part == self.user.pseudo.lower():
return False, _("You can't delete a local email account whose" return (
" local part is the same as the username.") False,
_("You can't delete a local email account whose"
" local part is the same as the username."),
None
)
if user_request.has_perm('users.delete_emailaddress'): if user_request.has_perm('users.delete_emailaddress'):
return True, None return True, None, None
if not OptionalUser.get_cached_value('local_email_accounts_enabled'): if not OptionalUser.get_cached_value('local_email_accounts_enabled'):
return False, _("The local email accounts are not enabled.") return False, _("The local email accounts are not enabled."), None
if user_request == self.user: if user_request == self.user:
return True, None return True, None, None
return False, _("You don't have the right to delete another user's" return (
" local email account") False,
_("You don't have the right to delete another user's"
" local email account"),
('users.delete_emailaddress',)
)
def can_edit(self, user_request, *_args, **_kwargs): def can_edit(self, user_request, *_args, **_kwargs):
"""Check if a user can edit the alias """Check if a user can edit the alias
@ -1956,16 +2035,24 @@ class EMailAddress(RevMixin, AclMixin, models.Model):
the local email account. the local email account.
""" """
if self.local_part == self.user.pseudo.lower(): if self.local_part == self.user.pseudo.lower():
return False, _("You can't edit a local email account whose local" return (
" part is the same as the username.") False,
_("You can't edit a local email account whose local"
" part is the same as the username."),
None
)
if user_request.has_perm('users.change_emailaddress'): if user_request.has_perm('users.change_emailaddress'):
return True, None return True, None, None
if not OptionalUser.get_cached_value('local_email_accounts_enabled'): if not OptionalUser.get_cached_value('local_email_accounts_enabled'):
return False, _("The local email accounts are not enabled.") return False, _("The local email accounts are not enabled."), None
if user_request == self.user: if user_request == self.user:
return True, None return True, None, None
return False, _("You don't have the right to edit another user's local" return (
" email account.") False,
_("You don't have the right to edit another user's local"
" email account."),
('users.change_emailaddress',)
)
def clean(self, *args, **kwargs): def clean(self, *args, **kwargs):
self.local_part = self.local_part.lower() self.local_part = self.local_part.lower()