diff --git a/cotisations/__init__.py b/cotisations/__init__.py index fc1be5d7..df6e4256 100644 --- a/cotisations/__init__.py +++ b/cotisations/__init__.py @@ -21,3 +21,4 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +from .acl import * diff --git a/cotisations/acl.py b/cotisations/acl.py new file mode 100644 index 00000000..1928bca3 --- /dev/null +++ b/cotisations/acl.py @@ -0,0 +1,39 @@ +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017 Gabriel Détraz +# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Augustin Lemesle +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""".acl + +Here are defined some functions to check acl on the application. +""" + +def can_view(user): + """Check if an user can view the application. + + Args: + user: The user who wants to view the application. + + Returns: + A couple (allowed, msg) where allowed is a boolean which is True if + viewing is granted and msg is a message (can be None). + """ + can = user.has_perms(('cableur',)) + return can, None if can else "Vous ne pouvez pas voir cette application." diff --git a/cotisations/views.py b/cotisations/views.py index 876c7eb3..d7d953c9 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -43,8 +43,8 @@ from users.models import User from re2o.settings import LOGO_PATH from re2o import settings from re2o.views import form -from re2o.utils import ( - SortTable, +from re2o.utils import SortTable +from re2o.acl import ( can_create, can_edit, can_delete, diff --git a/logs/__init__.py b/logs/__init__.py index fc1be5d7..df6e4256 100644 --- a/logs/__init__.py +++ b/logs/__init__.py @@ -21,3 +21,4 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +from .acl import * diff --git a/logs/acl.py b/logs/acl.py new file mode 100644 index 00000000..5f6c768a --- /dev/null +++ b/logs/acl.py @@ -0,0 +1,39 @@ +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017 Gabriel Détraz +# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Augustin Lemesle +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""logs.acl + +Here are defined some functions to check acl on the application. +""" + +def can_view(user): + """Check if an user can view the application. + + Args: + user: The user who wants to view the application. + + Returns: + A couple (allowed, msg) where allowed is a boolean which is True if + viewing is granted and msg is a message (can be None). + """ + can = user.has_perms(('cableur',)) + return can, None if can else "Vous ne pouvez pas voir cette application." diff --git a/logs/views.py b/logs/views.py index 92ce6dad..b2d36d2b 100644 --- a/logs/views.py +++ b/logs/views.py @@ -98,6 +98,8 @@ from re2o.utils import ( all_baned, all_has_access, all_adherent, +) +from re2o.acl import ( can_view_all, can_view_app, can_edit_history, diff --git a/machines/__init__.py b/machines/__init__.py index fc1be5d7..df6e4256 100644 --- a/machines/__init__.py +++ b/machines/__init__.py @@ -21,3 +21,4 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +from .acl import * diff --git a/machines/acl.py b/machines/acl.py new file mode 100644 index 00000000..565dfdcd --- /dev/null +++ b/machines/acl.py @@ -0,0 +1,39 @@ +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017 Gabriel Détraz +# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Augustin Lemesle +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""machines.acl + +Here are defined some functions to check acl on the application. +""" + +def can_view(user): + """Check if an user can view the application. + + Args: + user: The user who wants to view the application. + + Returns: + A couple (allowed, msg) where allowed is a boolean which is True if + viewing is granted and msg is a message (can be None). + """ + can = user.has_perms(('cableur',)) + return can, None if can else "Vous ne pouvez pas voir cette application." diff --git a/machines/views.py b/machines/views.py index be19d1d0..2581fb01 100644 --- a/machines/views.py +++ b/machines/views.py @@ -124,6 +124,8 @@ from re2o.utils import ( all_has_access, filter_active_interfaces, SortTable, +) +from re2o.acl import ( can_create, can_edit, can_delete, @@ -219,7 +221,7 @@ def generate_ipv4_mbf_param( form, is_type_tt ): @can_create(Machine) @can_edit(User) def new_machine(request, user, userid): - """ Fonction de creation d'une machine. Cree l'objet machine, + """ Fonction de creation d'une machine. Cree l'objet machine, le sous objet interface et l'objet domain à partir de model forms. Trop complexe, devrait être simplifié""" @@ -568,7 +570,7 @@ def add_mx(request): @login_required @can_edit(Mx) def edit_mx(request, mx_instance, mxid): - + mx = MxForm(request.POST or None, instance=mx_instance) if mx.is_valid(): with transaction.atomic(), reversion.create_revision(): @@ -746,7 +748,7 @@ def add_alias(request, interface, interfaceid): reversion.set_comment("Création") messages.success(request, "Cet alias a été ajouté") return redirect(reverse( - 'machines:index-alias', + 'machines:index-alias', kwargs={'interfaceid':str(interfaceid)} )) return form({'aliasform': alias}, 'machines/machine.html', request) @@ -763,7 +765,7 @@ def edit_alias(request, domain_instance, domainid): reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in alias.changed_data)) messages.success(request, "Alias modifié") return redirect(reverse( - 'machines:index-alias', + 'machines:index-alias', kwargs={'interfaceid':str(domain_instance.cname.interface_parent.id)} )) return form({'aliasform': alias}, 'machines/machine.html', request) @@ -783,7 +785,7 @@ def del_alias(request, interface, interfaceid): except ProtectedError: messages.error(request, "Erreur l'alias suivant %s ne peut être supprimé" % alias_del) return redirect(reverse( - 'machines:index-alias', + 'machines:index-alias', kwargs={'interfaceid':str(interfaceid)} )) return form({'aliasform': alias}, 'machines/machine.html', request) diff --git a/preferences/__init__.py b/preferences/__init__.py index e69de29b..e895e295 100644 --- a/preferences/__init__.py +++ b/preferences/__init__.py @@ -0,0 +1,2 @@ + +from .acl import * diff --git a/preferences/acl.py b/preferences/acl.py new file mode 100644 index 00000000..f4f11ba1 --- /dev/null +++ b/preferences/acl.py @@ -0,0 +1,39 @@ +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017 Gabriel Détraz +# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Augustin Lemesle +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""preferences.acl + +Here are defined some functions to check acl on the application. +""" + +def can_view(user): + """Check if an user can view the application. + + Args: + user: The user who wants to view the application. + + Returns: + A couple (allowed, msg) where allowed is a boolean which is True if + viewing is granted and msg is a message (can be None). + """ + can = user.has_perms(('cableur',)) + return can, None if can else "Vous ne pouvez pas voir cette application." diff --git a/preferences/views.py b/preferences/views.py index 03a5f547..45324f53 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -42,7 +42,7 @@ from reversion.models import Version from reversion import revisions as reversion from re2o.views import form -from re2o.utils import can_create, can_edit, can_delete_set, can_view_all +from re2o.acl import can_create, can_edit, can_delete_set, can_view_all from .forms import ServiceForm, DelServiceForm from .models import Service, OptionalUser, OptionalMachine, AssoOption from .models import MailMessageOption, GeneralOption, OptionalTopologie diff --git a/re2o/acl.py b/re2o/acl.py new file mode 100644 index 00000000..3226ec7c --- /dev/null +++ b/re2o/acl.py @@ -0,0 +1,235 @@ +# -*- mode: python; coding: utf-8 -*- +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017 Gabriel Détraz +# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Augustin Lemesle +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""Handles ACL for re2o. + +Here are defined some decorators that can be used in views to handle ACL. +""" +from __future__ import unicode_literals + +import sys + +from django.contrib import messages +from django.shortcuts import redirect +from django.urls import reverse + +import cotisations, logs, machines, preferences, search, topologie, users + + +def can_create(model): + """Decorator to check if an user can create a model. + It assumes that a valid user exists in the request and that the model has a + method can_create(user) which returns true if the user can create this kind + of models. + """ + def decorator(view): + def wrapper(request, *args, **kwargs): + can, msg = model.can_create(request.user, *args, **kwargs) + if not can: + messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") + return redirect(reverse('users:profil', + kwargs={'userid':str(request.user.id)} + )) + return view(request, *args, **kwargs) + return wrapper + return decorator + + +def can_edit(model, *field_list): + """Decorator to check if an user can edit a model. + It tries to get an instance of the model, using + `model.get_instance(*args, **kwargs)` and assumes that the model has a + method `can_edit(user)` which returns `true` if the user can edit this + kind of models. + """ + def decorator(view): + def wrapper(request, *args, **kwargs): + try: + instance = model.get_instance(*args, **kwargs) + except model.DoesNotExist: + messages.error(request, u"Entrée inexistante") + return redirect(reverse('users:profil', + kwargs={'userid':str(request.user.id)} + )) + can, msg = instance.can_edit(request.user) + if not can: + messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") + return redirect(reverse('users:profil', + kwargs={'userid':str(request.user.id)} + )) + for field in field_list: + can_create = getattr(model, 'can_change_' + field) + can, msg = can_create(instance, request.user, *args, **kwargs) + if not can: + messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") + return redirect(reverse('users:profil', + kwargs={'userid':str(request.user.id)} + )) + return view(request, instance, *args, **kwargs) + return wrapper + return decorator + + +def can_change(model, *field_list): + """Decorator to check if an user can edit a field of a model class. + Difference with can_edit : take a class and not an instance + """ + def decorator(view): + def wrapper(request, *args, **kwargs): + for field in field_list: + can_create = getattr(model, 'can_change_' + field) + can, msg = can_create(request.user, *args, **kwargs) + if not can: + messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") + return redirect(reverse('users:profil', + kwargs={'userid':str(request.user.id)} + )) + return view(request, *args, **kwargs) + return wrapper + return decorator + + +def can_delete(model): + """Decorator to check if an user can delete a model. + It tries to get an instance of the model, using + `model.get_instance(*args, **kwargs)` and assumes that the model has a + method `can_delete(user)` which returns `true` if the user can delete this + kind of models. + """ + def decorator(view): + def wrapper(request, *args, **kwargs): + try: + instance = model.get_instance(*args, **kwargs) + except model.DoesNotExist: + messages.error(request, u"Entrée inexistante") + return redirect(reverse('users:profil', + kwargs={'userid':str(request.user.id)} + )) + can, msg = instance.can_delete(request.user) + if not can: + messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") + return redirect(reverse('users:profil', + kwargs={'userid':str(request.user.id)} + )) + return view(request, instance, *args, **kwargs) + return wrapper + return decorator + + +def can_delete_set(model): + """Decorator which returns a list of detable models by request user. + If none of them, return an error""" + def decorator(view): + def wrapper(request, *args, **kwargs): + all_objects = model.objects.all() + instances_id = [] + for instance in all_objects: + can, msg = instance.can_delete(request.user) + if can: + instances_id.append(instance.id) + instances = model.objects.filter(id__in=instances_id) + if not instances: + messages.error(request, "Vous ne pouvez pas accéder à ce menu") + return redirect(reverse('users:profil', + kwargs={'userid':str(request.user.id)} + )) + return view(request, instances, *args, **kwargs) + return wrapper + return decorator + + +def can_view(model): + """Decorator to check if an user can view a model. + It tries to get an instance of the model, using + `model.get_instance(*args, **kwargs)` and assumes that the model has a + method `can_view(user)` which returns `true` if the user can view this + kind of models. + """ + def decorator(view): + def wrapper(request, *args, **kwargs): + try: + instance = model.get_instance(*args, **kwargs) + except model.DoesNotExist: + messages.error(request, u"Entrée inexistante") + return redirect(reverse('users:profil', + kwargs={'userid':str(request.user.id)} + )) + can, msg = instance.can_view(request.user) + if not can: + messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") + return redirect(reverse('users:profil', + kwargs={'userid':str(request.user.id)} + )) + return view(request, instance, *args, **kwargs) + return wrapper + return decorator + + +def can_view_all(model): + """Decorator to check if an user can view a class of model. + """ + def decorator(view): + def wrapper(request, *args, **kwargs): + can, msg = model.can_view_all(request.user) + if not can: + messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") + return redirect(reverse('users:profil', + kwargs={'userid':str(request.user.id)} + )) + return view(request, *args, **kwargs) + return wrapper + return decorator + + +def can_view_app(app_name): + """Decorator to check if an user can view an application. + """ + assert app_name in sys.modules.keys() + def decorator(view): + def wrapper(request, *args, **kwargs): + app = sys.modules[app_name] + can,msg = app.can_view(request.user) + if can: + return view(request, *args, **kwargs) + messages.error(request, msg) + return redirect(reverse('users:profil', + kwargs={'userid':str(request.user.id)} + )) + return wrapper + return decorator + + +def can_edit_history(view): + """Decorator to check if an user can edit history.""" + def wrapper(request, *args, **kwargs): + if request.user.has_perms(('admin',)): + return view(request, *args, **kwargs) + messages.error( + request, + "Vous ne pouvez pas éditer l'historique." + ) + return redirect(reverse('users:profil', + kwargs={'userid':str(request.user.id)} + )) + return wrapper + diff --git a/re2o/templatetags/__init__.py b/re2o/templatetags/__init__.py index d9561b4b..5be3ef33 100644 --- a/re2o/templatetags/__init__.py +++ b/re2o/templatetags/__init__.py @@ -2,19 +2,19 @@ # Re2o est un logiciel d'administration développé initiallement au rezometz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. -# +# # Copyright © 2017 Maël Kervella -# +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. diff --git a/re2o/templatetags/acl.py b/re2o/templatetags/acl.py index e57988d4..e09fcef7 100644 --- a/re2o/templatetags/acl.py +++ b/re2o/templatetags/acl.py @@ -48,7 +48,7 @@ an instance of a model (either Model.can_xxx or instance.can_xxx) **Example**: {% can_create Machine targeted_user %} -

I'm authorized to create new machines for this guy \\o/

+

I'm authorized to create new machines.models.for this guy \\o/

{% acl_else %}

Why can't I create a little machine for this guy ? :(

{% acl_end %} @@ -70,72 +70,71 @@ an instance of a model (either Model.can_xxx or instance.can_xxx) the acl function exists in the model definition """ +import sys from django import template from django.template.base import Node, NodeList -from re2o.utils import APP_VIEWING_RIGHT - -import cotisations.models as cotisations -import machines.models as machines -import preferences.models as preferences -import topologie.models as topologie -import users.models as users +import cotisations +import machines +import preferences +import topologie +import users register = template.Library() MODEL_NAME = { # cotisations - 'Facture' : cotisations.Facture, - 'Vente' : cotisations.Vente, - 'Article' : cotisations.Article, - 'Banque' : cotisations.Banque, - 'Paiement' : cotisations.Paiement, - 'Cotisation' : cotisations.Cotisation, + 'Facture' : cotisations.models.Facture, + 'Vente' : cotisations.models.Vente, + 'Article' : cotisations.models.Article, + 'Banque' : cotisations.models.Banque, + 'Paiement' : cotisations.models.Paiement, + 'Cotisation' : cotisations.models.Cotisation, # machines - 'Machine' : machines.Machine, - 'MachineType' : machines.MachineType, - 'IpType' : machines.IpType, - 'Vlan' : machines.Vlan, - 'Nas' : machines.Nas, - 'SOA' : machines.SOA, - 'Extension' : machines.Extension, - 'Mx' : machines.Mx, - 'Ns' : machines.Ns, - 'Txt' : machines.Txt, - 'Srv' : machines.Srv, - 'Interface' : machines.Interface, - 'Domain' : machines.Domain, - 'IpList' : machines.IpList, - 'Service' : machines.Service, - 'Service_link' : machines.Service_link, - 'OuverturePortList' : machines.OuverturePortList, - 'OuverturePort' : machines.OuverturePort, + 'Machine' : machines.models.Machine, + 'MachineType' : machines.models.MachineType, + 'IpType' : machines.models.IpType, + 'Vlan' : machines.models.Vlan, + 'Nas' : machines.models.Nas, + 'SOA' : machines.models.SOA, + 'Extension' : machines.models.Extension, + 'Mx' : machines.models.Mx, + 'Ns' : machines.models.Ns, + 'Txt' : machines.models.Txt, + 'Srv' : machines.models.Srv, + 'Interface' : machines.models.Interface, + 'Domain' : machines.models.Domain, + 'IpList' : machines.models.IpList, + 'Service' : machines.models.Service, + 'Service_link' : machines.models.Service_link, + 'OuverturePortList' : machines.models.OuverturePortList, + 'OuverturePort' : machines.models.OuverturePort, # preferences - 'OptionalUser': preferences.OptionalUser, - 'OptionalMachine': preferences.OptionalMachine, - 'OptionalTopologie': preferences.OptionalTopologie, - 'GeneralOption': preferences.GeneralOption, - 'Service': preferences.Service, - 'AssoOption': preferences.AssoOption, - 'MailMessageOption': preferences.MailMessageOption, + 'OptionalUser': preferences.models.OptionalUser, + 'OptionalMachine': preferences.models.OptionalMachine, + 'OptionalTopologie': preferences.models.OptionalTopologie, + 'GeneralOption': preferences.models.GeneralOption, + 'Service': preferences.models.Service, + 'AssoOption': preferences.models.AssoOption, + 'MailMessageOption': preferences.models.MailMessageOption, # topologie - 'Stack' : topologie.Stack, - 'Switch' : topologie.Switch, - 'ModelSwitch' : topologie.ModelSwitch, - 'ConstructorSwitch' : topologie.ConstructorSwitch, - 'Port' : topologie.Port, - 'Room' : topologie.Room, + 'Stack' : topologie.models.Stack, + 'Switch' : topologie.models.Switch, + 'ModelSwitch' : topologie.models.ModelSwitch, + 'ConstructorSwitch' : topologie.models.ConstructorSwitch, + 'Port' : topologie.models.Port, + 'Room' : topologie.models.Room, # users - 'User' : users.User, - 'Adherent' : users.Adherent, - 'Club' : users.Club, - 'ServiceUser' : users.ServiceUser, - 'Right' : users.Right, - 'School' : users.School, - 'ListRight' : users.ListRight, - 'Ban' : users.Ban, - 'Whitelist' : users.Whitelist, + 'User' : users.models.User, + 'Adherent' : users.models.Adherent, + 'Club' : users.models.Club, + 'ServiceUser' : users.models.ServiceUser, + 'Right' : users.models.Right, + 'School' : users.models.School, + 'ListRight' : users.models.ListRight, + 'Ban' : users.models.Ban, + 'Whitelist' : users.models.Whitelist, } @@ -181,21 +180,9 @@ def get_callback(tag_name, obj=None): if tag_name == 'cannot_view_all': return acl_fct(obj.can_view_all, True) if tag_name == 'can_view_app': - return acl_fct( - lambda user:( - user.has_perms((APP_VIEWING_RIGHT[obj],)), - "Vous ne pouvez pas voir cette application." - ), - False - ) + return acl_fct(sys.modules[obj].can_view, False) if tag_name == 'cannot_view_app': - return acl_fct( - lambda user:( - user.has_perms((APP_VIEWING_RIGHT[obj],)), - "Vous ne pouvez pas voir cette application." - ), - True - ) + return acl_fct(sys.modules[obj].can_view, True) if tag_name == 'can_edit_history': return acl_fct(lambda user:(user.has_perms(('admin',)),None),False) if tag_name == 'cannot_edit_history': @@ -252,7 +239,7 @@ def acl_app_filter(parser, token): "%r tag require 1 argument : the application" % token.contents.split()[0] ) - if not app_name in APP_VIEWING_RIGHT.keys(): + if not app_name in sys.modules.keys(): raise template.TemplateSyntaxError( "%r is not a registered application for acl." % app_name diff --git a/re2o/utils.py b/re2o/utils.py index 1ccc8962..23c6e052 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -50,214 +50,6 @@ from preferences.models import Service DT_NOW = timezone.now() -def can_create(model): - """Decorator to check if an user can create a model. - It assumes that a valid user exists in the request and that the model has a - method can_create(user) which returns true if the user can create this kind - of models. - """ - def decorator(view): - def wrapper(request, *args, **kwargs): - can, msg = model.can_create(request.user, *args, **kwargs) - if not can: - messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") - return redirect(reverse('users:profil', - kwargs={'userid':str(request.user.id)} - )) - return view(request, *args, **kwargs) - return wrapper - return decorator - - -def can_edit(model, *field_list): - """Decorator to check if an user can edit a model. - It tries to get an instance of the model, using - `model.get_instance(*args, **kwargs)` and assumes that the model has a - method `can_edit(user)` which returns `true` if the user can edit this - kind of models. - """ - def decorator(view): - def wrapper(request, *args, **kwargs): - try: - instance = model.get_instance(*args, **kwargs) - except model.DoesNotExist: - messages.error(request, u"Entrée inexistante") - return redirect(reverse('users:profil', - kwargs={'userid':str(request.user.id)} - )) - can, msg = instance.can_edit(request.user) - if not can: - messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") - return redirect(reverse('users:profil', - kwargs={'userid':str(request.user.id)} - )) - for field in field_list: - can_create = getattr(model, 'can_change_' + field) - can, msg = can_create(instance, request.user, *args, **kwargs) - if not can: - messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") - return redirect(reverse('users:profil', - kwargs={'userid':str(request.user.id)} - )) - return view(request, instance, *args, **kwargs) - return wrapper - return decorator - - -def can_change(model, *field_list): - """Decorator to check if an user can edit a field of a model class. - Difference with can_edit : take a class and not an instance - """ - def decorator(view): - def wrapper(request, *args, **kwargs): - for field in field_list: - can_create = getattr(model, 'can_change_' + field) - can, msg = can_create(None, request.user, *args, **kwargs) - if not can: - messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") - return redirect(reverse('users:profil', - kwargs={'userid':str(request.user.id)} - )) - return view(request, *args, **kwargs) - return wrapper - return decorator - - -def can_delete(model): - """Decorator to check if an user can delete a model. - It tries to get an instance of the model, using - `model.get_instance(*args, **kwargs)` and assumes that the model has a - method `can_delete(user)` which returns `true` if the user can delete this - kind of models. - """ - def decorator(view): - def wrapper(request, *args, **kwargs): - try: - instance = model.get_instance(*args, **kwargs) - except model.DoesNotExist: - messages.error(request, u"Entrée inexistante") - return redirect(reverse('users:profil', - kwargs={'userid':str(request.user.id)} - )) - can, msg = instance.can_delete(request.user) - if not can: - messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") - return redirect(reverse('users:profil', - kwargs={'userid':str(request.user.id)} - )) - return view(request, instance, *args, **kwargs) - return wrapper - return decorator - - -def can_delete_set(model): - """Decorator which returns a list of detable models by request user. - If none of them, return an error""" - def decorator(view): - def wrapper(request, *args, **kwargs): - all_objects = model.objects.all() - instances_id = [] - for instance in all_objects: - can, msg = instance.can_delete(request.user) - if can: - instances_id.append(instance.id) - instances = model.objects.filter(id__in=instances_id) - if not instances: - messages.error(request, "Vous ne pouvez pas accéder à ce menu") - return redirect(reverse('users:profil', - kwargs={'userid':str(request.user.id)} - )) - return view(request, instances, *args, **kwargs) - return wrapper - return decorator - - -def can_view(model): - """Decorator to check if an user can view a model. - It tries to get an instance of the model, using - `model.get_instance(*args, **kwargs)` and assumes that the model has a - method `can_view(user)` which returns `true` if the user can view this - kind of models. - """ - def decorator(view): - def wrapper(request, *args, **kwargs): - try: - instance = model.get_instance(*args, **kwargs) - except model.DoesNotExist: - messages.error(request, u"Entrée inexistante") - return redirect(reverse('users:profil', - kwargs={'userid':str(request.user.id)} - )) - can, msg = instance.can_view(request.user) - if not can: - messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") - return redirect(reverse('users:profil', - kwargs={'userid':str(request.user.id)} - )) - return view(request, instance, *args, **kwargs) - return wrapper - return decorator - - -def can_view_all(model): - """Decorator to check if an user can view a class of model. - """ - def decorator(view): - def wrapper(request, *args, **kwargs): - can, msg = model.can_view_all(request.user) - if not can: - messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") - return redirect(reverse('users:profil', - kwargs={'userid':str(request.user.id)} - )) - return view(request, *args, **kwargs) - return wrapper - return decorator - - -APP_VIEWING_RIGHT = { - 'cotisations' : 'cableur', - 'logs' : 'cableur', - 'machines' : 'cableur', - 'preferences' : 'cableur', - 'search' : 'cableur', - 'topologie' : 'cableur', - 'users' : 'cableur', -} - -def can_view_app(app_name): - """Decorator to check if an user can view an application. - """ - assert app_name in APP_VIEWING_RIGHT.keys() - def decorator(view): - def wrapper(request, *args, **kwargs): - if request.user.has_perms((APP_VIEWING_RIGHT[app_name],)): - return view(request, *args, **kwargs) - messages.error( - request, - msg or "Vous ne pouvez pas accéder à l'application " + app_name - ) - return redirect(reverse('users:profil', - kwargs={'userid':str(request.user.id)} - )) - return wrapper - return decorator - - -def can_edit_history(view): - """Decorator to check if an user can edit history.""" - def wrapper(request, *args, **kwargs): - if request.user.has_perms(('admin',)): - return view(request, *args, **kwargs) - messages.error( - request, - "Vous ne pouvez pas éditer l'historique." - ) - return redirect(reverse('users:profil', - kwargs={'userid':str(request.user.id)} - )) - return wrapper - def all_adherent(search_time=DT_NOW): """ Fonction renvoyant tous les users adherents. Optimisee pour n'est diff --git a/search/__init__.py b/search/__init__.py index fc1be5d7..df6e4256 100644 --- a/search/__init__.py +++ b/search/__init__.py @@ -21,3 +21,4 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +from .acl import * diff --git a/search/acl.py b/search/acl.py new file mode 100644 index 00000000..99d6603f --- /dev/null +++ b/search/acl.py @@ -0,0 +1,39 @@ +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017 Gabriel Détraz +# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Augustin Lemesle +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""search.acl + +Here are defined some functions to check acl on the application. +""" + +def can_view(user): + """Check if an user can view the application. + + Args: + user: The user who wants to view the application. + + Returns: + A couple (allowed, msg) where allowed is a boolean which is True if + viewing is granted and msg is a message (can be None). + """ + can = user.has_perms(('cableur',)) + return can, None if can else "Vous ne pouvez pas voir cette application." diff --git a/topologie/__init__.py b/topologie/__init__.py index fc1be5d7..df6e4256 100644 --- a/topologie/__init__.py +++ b/topologie/__init__.py @@ -21,3 +21,4 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +from .acl import * diff --git a/topologie/acl.py b/topologie/acl.py new file mode 100644 index 00000000..4a0a98c0 --- /dev/null +++ b/topologie/acl.py @@ -0,0 +1,39 @@ +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017 Gabriel Détraz +# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Augustin Lemesle +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""topologie.acl + +Here are defined some functions to check acl on the application. +""" + +def can_view(user): + """Check if an user can view the application. + + Args: + user: The user who wants to view the application. + + Returns: + A couple (allowed, msg) where allowed is a boolean which is True if + viewing is granted and msg is a message (can be None). + """ + can = user.has_perms(('cableur',)) + return can, None if can else "Vous ne pouvez pas voir cette application." diff --git a/topologie/views.py b/topologie/views.py index 0e24ebcd..84ee9fb7 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -65,8 +65,8 @@ from topologie.forms import ( CreatePortsForm ) from users.views import form -from re2o.utils import ( - SortTable, +from re2o.utils import SortTable +from re2o.acl import ( can_create, can_edit, can_delete, diff --git a/users/__init__.py b/users/__init__.py index fc1be5d7..df6e4256 100644 --- a/users/__init__.py +++ b/users/__init__.py @@ -21,3 +21,4 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +from .acl import * diff --git a/users/acl.py b/users/acl.py new file mode 100644 index 00000000..85c53e43 --- /dev/null +++ b/users/acl.py @@ -0,0 +1,39 @@ +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017 Gabriel Détraz +# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Augustin Lemesle +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +"""users.acl + +Here are defined some functions to check acl on the application. +""" + +def can_view(user): + """Check if an user can view the application. + + Args: + user: The user who wants to view the application. + + Returns: + A couple (allowed, msg) where allowed is a boolean which is True if + viewing is granted and msg is a message (can be None). + """ + can = user.has_perms(('cableur',)) + return can, None if can else "Vous ne pouvez pas voir cette application." diff --git a/users/views.py b/users/views.py index 27f0f873..e52ba095 100644 --- a/users/views.py +++ b/users/views.py @@ -93,6 +93,8 @@ from re2o.views import form from re2o.utils import ( all_has_access, SortTable, +) +from re2o.acl import ( can_create, can_edit, can_delete_set,