Commit a9d26778 authored by Simon Moreno's avatar Simon Moreno
Browse files

Restricciones para editar o borrar usuarios de mayor jerarquía

parent 43421e5f
Pipeline #197532023 passed with stage
in 12 minutes and 16 seconds
......@@ -19,9 +19,9 @@ generar sus propios Roles personalizados para la operación.
A continuación se listan los roles predefinidos:
* **Referente**: los usuarios generados a partir de este rol podrán solo listar todas las campañas, consultar reportes generales de agentes y llamadas, acceder a la supervisión de agentes y campañas.
* **Supervisor**: los usuarios generados a partir de este rol podrán trabajar con todas las campañas (crear, modificar, eliminar y listar), acceder a la supervisión y reportes generales de llamadas y agentes. También podrán crear usuarios y grupos de agentes. Buscar grabaciones de sus campañas, listar y subir bases de contactos y acceder al módulo de telefonía donde podrán trabajar con algunas secciones.
* **Supervisor**: los usuarios generados a partir de este rol podrán trabajar con todas las campañas a las que estén asignados (crear, modificar, eliminar y listar), acceder a la supervisión y reportes generales de llamadas y agentes. También podrán crear usuarios y grupos de agentes, pero sólo podrán editar o eliminar agentes que esten asignados a alguna campaña a la que ellos mismos estén asignados. Buscar grabaciones de sus campañas, listar y subir bases de contactos y acceder al módulo de telefonía donde podrán trabajar con algunas secciones.
* **Gerente**: los usuarios generados a partir de este rol podrán realizar todas las acciones de un supervisor y además puede visualizar el módulo de Auditorías.
* **Administrador**: los usuarios generados a partir de este rol tienen acceso a todo el sistema.
* **Administrador**: los usuarios generados a partir de este rol tienen acceso a todo el sistema. Solamente los administradores pueden editar o borrar usuarios con perfil Administrador.
Para generar un *Rol personalizado*, se debe acceder al módulo de *Usuarios y grupos*, sección *Roles y permisos*, luego allí indicar *Crear rol*.
Al nuevo rol se le asigna un nombre y luego se marcan los permisos que tendrá. Para no iniciar de cero, el usuario puede partir de una base de permisos de cierto perfil y luego seguir personalizando
......@@ -47,7 +47,7 @@ suma los permisos a los que ya han sido checkeados en el nuevo rol.
Usuarios
*********
Vamos a diferenciar entre usuarios del tipo Agente y Administrativos. Los usuarios *Agente* son quienes gestionan las comunicaciones y los usuarios *Administrivos* gestionan la aplicación.
Vamos a diferenciar entre usuarios del tipo Agente y Administrativos. Los usuarios *Agente* son quienes gestionan las comunicaciones y los usuarios *Administrativos* gestionan la aplicación.
.. important::
Antes de crear un usuario *Agente* al menos debe existir un *Grupo de agentes*
......
......@@ -352,11 +352,15 @@ class OminicontactoAppConfig(AppConfig):
{'nombre': 'user_list',
'roles': ['Administrador', 'Gerente', 'Supervisor', ]},
{'nombre': 'user_delete',
'roles': ['Administrador', 'Gerente', 'Supervisor', ]},
'roles': ['Administrador', 'Gerente', ]},
{'nombre': 'user_update',
'roles': ['Administrador', 'Gerente', 'Supervisor', 'Referente']},
{'nombre': 'user_change_password',
'roles': ['Administrador', 'Gerente', ]},
{'nombre': 'agent_delete',
'roles': ['Administrador', 'Gerente', 'Supervisor', ]},
{'nombre': 'agent_update',
'roles': ['Administrador', 'Gerente', 'Supervisor', ]},
{'nombre': 'user_change_password',
'roles': ['Administrador', 'Gerente', 'Supervisor', 'Referente']},
{'nombre': 'agente_list',
'roles': ['Administrador', 'Gerente', 'Supervisor', ]},
{'nombre': 'agenteprofile_update',
......@@ -709,9 +713,13 @@ class OminicontactoAppConfig(AppConfig):
'user_list':
{'descripcion': _('Ver lista de Usuarios'), 'version': '1.7.0'},
'user_delete':
{'descripcion': _('Borrar Usuario'), 'version': '1.7.0'},
{'descripcion': _('Borrar Usuario (no agente)'), 'version': '1.7.0'},
'user_update':
{'descripcion': _('Editar Usuario'), 'version': '1.7.0'},
{'descripcion': _('Editar Usuario (no agente)'), 'version': '1.7.0'},
'agent_delete':
{'descripcion': _('Borrar Usuario Agente'), 'version': '1.7.0'},
'agent_update':
{'descripcion': _('Editar Usuario Agente'), 'version': '1.7.0'},
'user_change_password':
{'descripcion': _('Forzar cambio de password'), 'version': '1.9.0'},
'agente_list':
......
......@@ -194,6 +194,17 @@ class User(AbstractUser):
except Session.DoesNotExist:
pass
def tiene_agente_asignado(self, agente_profile):
"""
Verifica si el agente pasado como parametro esta asignado a alguna de las campañas en las
que el usuario esta asignado como supervisor.
"""
campanas_supervisor = set(self.campanasupervisors.values_list('id', flat=True))
campanas_agent = set(agente_profile.campana_member.values_list(
'queue_name__campana_id', flat=True))
# Si las campañas son disjuntas es porque no esta asignado
return not campanas_supervisor.isdisjoint(campanas_agent)
class Grupo(models.Model):
nombre = models.CharField(max_length=20, unique=True, verbose_name=_('Nombre'))
......
......@@ -45,7 +45,7 @@ along with this program. If not, see http://www.gnu.org/licenses/.
<th>{% trans 'Nombre' %}</th>
<th>{% trans 'Apellido' %}</th>
<th>{% trans 'Perfil' %}</th>
{% if edita_user or elimina_user %}
{% if edita_user or elimina_user or edita_agente or elimina_agente %}
<th>{% trans 'Acciones' %}</th>
{% endif %}
</tr>
......@@ -77,27 +77,52 @@ along with this program. If not, see http://www.gnu.org/licenses/.
{% endif %}
</td>
{% if edita_user or elimina_user %}
<td>
<div class="dropdown">
<button class="btn btn-light dropdown-toggle" type="button" id="dropdownActions" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% trans 'Opciones' %}
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownActions">
{% if edita_user or elimina_user or edita_agente or elimina_agente %}
<td>
{% if usuario.is_agente %}
{% if edita_agente or elimina_agente %}
<div class="dropdown">
<button class="btn btn-light dropdown-toggle" type="button" id="dropdownActions" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% trans 'Opciones' %}
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownActions">
{% if edita_agente %}
<a class="dropdown-item" href="{% url 'agent_update' usuario.id %}">
<span class="icon icon-pencil"></span>{% trans 'Editar' %}
</a>
{% endif %}
{% if elimina_agente %}
<a class="dropdown-item" href="{% url 'agent_delete' usuario.id %}">
<span class="icon icon-trash"></span>{% trans 'Eliminar' %}
</a>
{% endif %}
</div>
</div>
{% endif %}
{% else %}
{% if edita_user or elimina_user %}
<div class="dropdown">
<button class="btn btn-light dropdown-toggle" type="button" id="dropdownActions" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% trans 'Opciones' %}
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownActions">
{% if edita_user %}
<a class="dropdown-item" href="{% url 'user_update' usuario.id %}">
<span class="icon icon-pencil"></span>{% trans 'Editar' %}
</a>
{% endif %}
{% if elimina_user and usuario.id != 1 %}
<a class="dropdown-item" href="{% url 'user_delete' usuario.id %}">
<span class="icon icon-trash"></span>{% trans 'Eliminar' %}
</a>
<a class="dropdown-item" href="{% url 'user_delete' usuario.id %}">
<span class="icon icon-trash"></span>{% trans 'Eliminar' %}
</a>
{% endif %}
</div>
</div>
</div>
</td>
{% endif %}
{% endif %}
</td>
{% endif %}
</tr>
{% empty %}
<tr>
......
......@@ -32,8 +32,9 @@ from django.utils.translation import ugettext as _
from django.contrib.auth.models import Group
from ominicontacto_app.tests.utiles import OMLBaseTest, PASSWORD
from ominicontacto_app.tests.factories import GrupoFactory
from ominicontacto_app.models import Grupo, User
from ominicontacto_app.tests.factories import (
GrupoFactory, CampanaFactory, QueueMemberFactory, QueueFactory)
from ominicontacto_app.models import Grupo, User, Campana
from ominicontacto_app.services.asterisk_service import ActivacionAgenteService
logger = _logging.getLogger(__name__)
......@@ -41,20 +42,31 @@ logger = _logging.getLogger(__name__)
COMPLEX_PASSWORD = '*FTS*OML1*'
class CreacionUsuariosTest(OMLBaseTest):
class ABMUsuariosTest(OMLBaseTest):
def setUp(self):
super(CreacionUsuariosTest, self).setUp()
super(ABMUsuariosTest, self).setUp()
self.admin = self.crear_administrador(username='admin1')
self.gerente = self.crear_supervisor_profile(rol=User.GERENTE, user=None)
self.supervisor = self.crear_supervisor_profile(rol=User.SUPERVISOR, user=None)
self.grupo1 = GrupoFactory(nombre='grupo1')
self.rol_gerente = Group.objects.get(name=User.GERENTE)
self.rol_supervisor = Group.objects.get(name=User.SUPERVISOR)
self.rol_agente = Group.objects.get(name=User.AGENTE)
# self.supervisor1
self.client.login(username=self.admin.username, password=PASSWORD)
self.agente = self.crear_agente_profile()
self.campana = CampanaFactory(estado=Campana.ESTADO_ACTIVA, type=Campana.TYPE_MANUAL)
QueueFactory(campana=self.campana)
QueueMemberFactory.create(member=self.agente, queue_name=self.campana.queue_campana)
class CreacionUsuariosTest(ABMUsuariosTest):
@patch.object(ActivacionAgenteService, 'activar')
@patch('ominicontacto_app.services.kamailio_service.KamailioService.generar_sip_password')
def test_crear_supervisor(self, generar_secret_key, activar):
self.client.login(username=self.admin.username, password=PASSWORD)
url = reverse('user_nuevo')
response = self.client.get(url, follow=True)
......@@ -66,7 +78,7 @@ class CreacionUsuariosTest(OMLBaseTest):
'0-email': 'asd@asd.com',
'0-password1': COMPLEX_PASSWORD,
'0-password2': COMPLEX_PASSWORD,
'0-rol': self.rol_gerente.id
'0-rol': self.rol_supervisor.id
}
response = self.client.post(url, data, follow=True)
self.assertEqual(response.status_code, 200)
......@@ -81,7 +93,6 @@ class CreacionUsuariosTest(OMLBaseTest):
@patch.object(ActivacionAgenteService, 'activar_agente')
def test_crear_agente(self, activar_agente):
self.client.login(username=self.admin.username, password=PASSWORD)
url = reverse('user_nuevo')
response = self.client.get(url, follow=True)
......@@ -114,8 +125,6 @@ class CreacionUsuariosTest(OMLBaseTest):
self.assertEqual(agente.grupo, self.grupo1)
def test_deshabilitar_is_agente_si_no_hay_grupo(self):
self.client.login(username=self.admin.username, password=PASSWORD)
url = reverse('user_nuevo')
# Sin Grupo
......@@ -148,3 +157,167 @@ def filtrar_linea(lineas, texto):
# def test_gerente puede crear supervisores y clientes o agentes (no gerentes)
# def test_gerente solo puede asignar grupos que él mismo creó
# def test_Supervisor solo puede asignar grupos propios
class ListaUsuariosTest(ABMUsuariosTest):
def setUp(self):
super(ListaUsuariosTest, self).setUp()
self.admin2 = self.crear_administrador(username='admin2')
def test_admin_ve_links_para_editar_y_borrar_todos(self):
url = reverse('user_list', kwargs={"page": 1})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, reverse('user_delete', kwargs={'pk': self.admin2.id}))
self.assertContains(
response, reverse('user_delete', kwargs={'pk': self.gerente.user.id}))
self.assertContains(
response, reverse('user_delete', kwargs={'pk': self.supervisor.user.id}))
self.assertContains(response, reverse('agent_delete', kwargs={'pk': self.agente.user.id}))
self.assertContains(response, reverse('user_update', kwargs={'pk': self.admin2.id}))
self.assertContains(
response, reverse('user_update', kwargs={'pk': self.gerente.user.id}))
self.assertContains(
response, reverse('user_update', kwargs={'pk': self.supervisor.user.id}))
self.assertContains(response, reverse('agent_update', kwargs={'pk': self.agente.user.id}))
def test_supervisor_solo_ve_links_para_editar_o_borrar_agentes(self):
self.client.login(username=self.supervisor.user.username, password=PASSWORD)
url = reverse('user_list', kwargs={"page": 1})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, reverse('user_delete', kwargs={'pk': self.admin.id}))
self.assertNotContains(
response, reverse('user_delete', kwargs={'pk': self.gerente.user.id}))
self.assertNotContains(
response, reverse('user_delete', kwargs={'pk': self.supervisor.user.id}))
self.assertContains(
response, reverse('agent_delete', kwargs={'pk': self.agente.user.id}))
self.assertNotContains(response, reverse('user_update', kwargs={'pk': self.admin.id}))
self.assertNotContains(
response, reverse('user_update', kwargs={'pk': self.gerente.user.id}))
self.assertNotContains(
response, reverse('user_update', kwargs={'pk': self.supervisor.user.id}))
self.assertContains(
response, reverse('agent_update', kwargs={'pk': self.agente.user.id}))
class BorrarUsuariosTest(ABMUsuariosTest):
def setUp(self):
super(BorrarUsuariosTest, self).setUp()
def test_admin_puede_borrar_admin(self):
admin2 = self.crear_administrador(username='admin2')
url = reverse('user_delete', kwargs={'pk': admin2.id})
response = self.client.post(url, follow=True)
self.assertEqual(response.status_code, 200)
self.assertRedirects(response, reverse('user_list', kwargs={"page": 1}))
admin2.refresh_from_db()
self.assertTrue(admin2.borrado)
def test_gerente_no_puede_borrar_admin(self):
self.client.login(username=self.gerente.user.username, password=PASSWORD)
admin2 = self.crear_administrador(username='admin2')
url = reverse('user_delete', kwargs={'pk': admin2.id})
response = self.client.post(url, follow=True)
self.assertEqual(response.status_code, 200)
self.assertRedirects(response, reverse('user_list', kwargs={"page": 1}))
admin2.refresh_from_db()
self.assertFalse(admin2.borrado)
message = _('No tiene permiso para eliminar al usuario {}'.format(
admin2.get_full_name()))
self.assertContains(response, message)
def test_supevisor_no_puede_borrar_users(self):
self.client.login(username=self.supervisor.user.username, password=PASSWORD)
admin2 = self.crear_administrador(username='admin2')
url = reverse('user_delete', kwargs={'pk': admin2.id})
response = self.client.post(url, follow=True)
self.assertEqual(response.status_code, 403)
def test_supevisor_no_puede_borrar_agentes_no_asignados_a_sus_campanas(self):
self.client.login(username=self.supervisor.user.username, password=PASSWORD)
user_agente = self.agente.user
url = reverse('agent_delete', kwargs={'pk': user_agente.id})
response = self.client.post(url, follow=True)
self.assertEqual(response.status_code, 200)
self.assertRedirects(response, reverse('user_list', kwargs={"page": 1}))
user_agente.refresh_from_db()
self.assertFalse(user_agente.borrado)
message = _('No tiene permiso para eliminar al usuario {}'.format(
user_agente.get_full_name()))
self.assertContains(response, message)
@patch('ominicontacto_app.services.creacion_queue.ActivacionQueueService.activar')
@patch('ominicontacto_app.views_queue_member.remover_agente_cola_asterisk')
def test_supevisor_puede_borrar_agentes_asignados_a_sus_campanas(
self, remover_agente_cola_asterisk, activar):
self.client.login(username=self.supervisor.user.username, password=PASSWORD)
self.campana.supervisors.add(self.supervisor.user)
user_agente = self.agente.user
url = reverse('agent_delete', kwargs={'pk': user_agente.id})
response = self.client.post(url, follow=True)
self.assertEqual(response.status_code, 200)
self.assertRedirects(response, reverse('user_list', kwargs={"page": 1}))
user_agente.refresh_from_db()
self.assertTrue(user_agente.borrado)
class EditarUsuariosTest(ABMUsuariosTest):
def setUp(self):
super(EditarUsuariosTest, self).setUp()
def test_admin_puede_editar_admin(self):
admin2 = self.crear_administrador(username='admin2')
url = reverse('user_update', kwargs={'pk': admin2.id})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
def test_gerente_no_puede_editar_admin(self):
self.client.login(username=self.gerente.user.username, password=PASSWORD)
admin2 = self.crear_administrador(username='admin2')
url = reverse('user_update', kwargs={'pk': admin2.id})
response = self.client.get(url, follow=True)
self.assertEqual(response.status_code, 200)
self.assertRedirects(response, reverse('user_list', kwargs={"page": 1}))
message = _('No tiene permiso para editar al usuario {}'.format(
admin2.get_full_name()))
self.assertContains(response, message)
def test_supevisor_no_puede_editar_users(self):
self.client.login(username=self.supervisor.user.username, password=PASSWORD)
admin2 = self.crear_administrador(username='admin2')
url = reverse('user_update', kwargs={'pk': admin2.id})
response = self.client.get(url, follow=True)
self.assertEqual(response.status_code, 403)
def test_supevisor_no_puede_editar_agentes_no_asignados_a_sus_campanas(self):
self.client.login(username=self.supervisor.user.username, password=PASSWORD)
user_agente = self.agente.user
url = reverse('agent_update', kwargs={'pk': user_agente.id})
response = self.client.get(url, follow=True)
self.assertEqual(response.status_code, 200)
self.assertRedirects(response, reverse('user_list', kwargs={"page": 1}))
message = _('No tiene permiso para editar al usuario {}'.format(
user_agente.get_full_name()))
self.assertContains(response, message)
def test_supevisor_puede_editar_agentes_asignados_a_sus_campanas(self):
self.client.login(username=self.supervisor.user.username, password=PASSWORD)
self.campana.supervisors.add(self.supervisor.user)
user_agente = self.agente.user
url = reverse('agent_update', kwargs={'pk': user_agente.id})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
......@@ -90,6 +90,14 @@ urlpatterns = [
login_required(views_user_profiles.CustomerUserUpdateView.as_view()),
name='user_update',
),
url(r'^user/agent/delete/(?P<pk>\d+)/$',
login_required(views_user_profiles.UserDeleteView.as_view()),
name='agent_delete', kwargs={'for_agent': ''}
),
url(r'^user/agent/update/(?P<pk>\d+)/$',
login_required(views_user_profiles.CustomerUserUpdateView.as_view()),
name='agent_update', kwargs={'for_agent': ''}
),
url(r'^user/password/$',
login_required(views_user_profiles.CustomerUserUpdateView.as_view()),
name='user_change_password', kwargs={'change_password': ''}
......
......@@ -202,10 +202,34 @@ class CustomerUserUpdateView(UpdateView):
def dispatch(self, *args, **kwargs):
self.force_password_change = False
self.for_agent = False
if 'change_password' in kwargs:
self.force_password_change = True
else:
user = self.get_object()
if 'for_agent' in kwargs:
self.for_agent = True
if not user.is_agente:
raise ValueError(_('URL incorrecta'))
else:
if user.is_agente:
raise ValueError(_('URL incorrecta'))
if not self._can_edit_user(user):
message = _('No tiene permiso para editar al usuario {}'.format(
user.get_full_name()))
messages.warning(self.request, message)
return HttpResponseRedirect(reverse('user_list', kwargs={"page": 1}))
return super(CustomerUserUpdateView, self).dispatch(*args, **kwargs)
def _can_edit_user(self, user):
# Solo un administrador puede editar otro administrador
if user.get_is_administrador() and not self.request.user.get_is_administrador():
return False
if self.for_agent:
if self.request.user.is_supervisor:
return self.request.user.tiene_agente_asignado(user.get_agente_profile())
return True
def get_object(self, *args, **kwargs):
if self.force_password_change:
return self.request.user
......@@ -263,8 +287,31 @@ class UserDeleteView(DeleteView):
if usuario.id == 1:
return HttpResponseRedirect(
reverse('user_list', kwargs={"page": 1}))
self.for_agent = False
user = self.get_object()
if 'for_agent' in kwargs:
self.for_agent = True
if not user.is_agente:
raise ValueError(_('URL incorrecta'))
else:
if user.is_agente:
raise ValueError(_('URL incorrecta'))
if not self._can_delete_user(user):
message = _('No tiene permiso para eliminar al usuario {}'.format(
user.get_full_name()))
messages.warning(self.request, message)
return HttpResponseRedirect(reverse('user_list', kwargs={"page": 1}))
return super(UserDeleteView, self).dispatch(request, *args, **kwargs)
def _can_delete_user(self, user):
# Solo un administrador puede eliminar otro administrador
if user.get_is_administrador() and not self.request.user.get_is_administrador():
return False
if self.for_agent:
if self.request.user.is_supervisor:
return self.request.user.tiene_agente_asignado(user.get_agente_profile())
return True
def get_context_data(self, **kwargs):
context = super(UserDeleteView, self).get_context_data(**kwargs)
context['user'] = self.request.user
......@@ -308,6 +355,8 @@ class UserListView(ListView):
context['modifica_perfil_supervisor'] = user.tiene_permiso_oml('supervisor_update')
context['edita_user'] = user.tiene_permiso_oml('user_update')
context['elimina_user'] = user.tiene_permiso_oml('user_delete')
context['edita_agente'] = user.tiene_permiso_oml('agent_update')
context['elimina_agente'] = user.tiene_permiso_oml('agent_delete')
context['numero_usuarios_activos'] = User.numero_usuarios_activos()
if 'search' in self.request.GET:
context['search'] = self.request.GET.get('search')
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment