"""
Sistema Estadístico Integral de Venezuela - (SEIVEN)
Copyleft (@) 2015 CENDITEL nodo Mérida - https://mpv.cenditel.gob.ve/seiven
"""
## @namespace usuario.views
#
# Contiene las clases, atributos, métodos y/o funciones a implementar para las vistas del módulo de usuario
# @author Ing. Roldan Vargas (rvargas at cenditel.gob.ve)
# @author Centro Nacional de Desarrollo e Investigación en Tecnologías Libres
# (CENDITEL) nodo Mérida - Venezuela
# @copyright GNU Public License versión 2 (GPLv2)
from __future__ import unicode_literals
import base64
import hashlib
from datetime import datetime
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import logout, login, authenticate
from django.contrib.auth.models import User
from django.contrib.messages.views import SuccessMessageMixin
from django.core import urlresolvers, signing
from django.core.urlresolvers import reverse_lazy, reverse
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.template import RequestContext
from django.views.generic import CreateView, UpdateView, ListView
from django.utils.translation import ugettext_lazy as _
from base.constant import REGISTRO_MESSAGE, UPDATE_MESSAGE, EMAIL_SUBJECT_REGISTRO, CADUCIDAD_LINK_REGISTRO
from base.functions import enviar_correo, calcular_diferencia_fechas
from base.models import Institucion
from .forms import AutenticarForm, RegistroForm, OlvidoClaveForm, ModificarClaveForm, PerfilForm
import logging
from .models import UserProfile
date_now = datetime.now()
logger = logging.getLogger("usuario")
def hash_user(user, is_new_user=False, is_reset=False):
"""!
Función que permite encriptar los datos del usuario
@author Ing. Roldan Vargas (rvargas at cenditel.gob.ve)
@copyright GNU Public License versión 2 (GPLv2)
@date 22-08-2016
@param user {object} Objeto que obtiene los datos del usuario
@param is_new_user {boolean} Indica si es un nuevo usuario
@param is_reset {boolean} Indica si se reinician los datos del usuario
@return Devuelve un enlace cifrado
"""
if is_new_user or not user.last_login:
date_to_hash = user.date_joined.isoformat()
else:
date_to_hash = user.last_login.isoformat()
username = user.username
password = user.password
date_to_hash = date_to_hash + ("", "|reset")[is_reset]
cadena = username + "|" + password + "|" + date_to_hash
hash = hashlib.sha1(cadena.encode("utf-8")).hexdigest()
return base64.urlsafe_b64encode(bytes(hash, "utf-8"))
def acceso(request):
"""!
Funcion que gestiona el acceso al sistema
@author Ing. Roldan Vargas (rvargas at cenditel.gob.ve)
@copyright GNU Public License versión 2 (GPLv2)
@date 23-04-2016
@param request {object} Objeto que obtiene la petición
@return Redirecciona al usuario a la pagina correspondiente en caso de que se haya autenticado o no
"""
form = AutenticarForm()
alert = None
if request.method == "POST":
form = AutenticarForm(data=request.POST)
if form.is_valid():
username = "%s%s" % (
request.POST['tipo_documento_0'], request.POST['tipo_documento_1']
)
if User.objects.filter(username=username, is_active=True):
usuario = authenticate(username=username, password=str(request.POST['clave']))
if usuario is not None:
login(request, usuario)
usr = User.objects.get(username=username)
usr.last_login = datetime.now()
usr.save()
else:
logger.error(str(_("Error al autenticar el usuario [%s]") % username))
logger.info(str(_("Acceso al sistema por el usuario [%s]") % username))
return HttpResponseRedirect(urlresolvers.reverse("inicio"))
else:
alert = str(_("Su usuario se encuentra inactivo. Intente más tarde..."))
return render(request, 'base.template.html', {'form': form, 'alert': alert})
def salir(request):
"""!
Funcion que gestiona la salida del sistema
@author Ing. Roldan Vargas (rvargas at cenditel.gob.ve)
@copyright GNU Public License versión 2 (GPLv2)
@date 23-04-2016
@param request {object} Objeto que contiene la petición
@return Redirecciona al usuario a la pagina de inicio, si fue desautenticado lo envia a la pagina de acceso
"""
user = request.user
if user.is_authenticated():
logout(request)
logger.info("El usuario [%s] salio del sistema" % user)
return HttpResponseRedirect(urlresolvers.reverse("inicio"))
def olvido_clave(request):
"""!
Funcion que gestiona el envío de enlace para la modificación de la contraseña
@author Ing. Roldan Vargas (rvargas at cenditel.gob.ve)
@copyright GNU Public License versión 2 (GPLv2)
@date 06-05-2016
@param request {object} Objeto que obtiene la petición
@return Redirecciona al usuario a la pagina de acceso al sistema
"""
form = OlvidoClaveForm()
alert = None
if request.method == "POST":
form = OlvidoClaveForm(data=request.POST)
if form.is_valid():
username = "%s%s" % (
request.POST['tipo_documento_0'], request.POST['tipo_documento_1']
)
correo = request.POST['correo']
usr = User.objects.get(username=username)
## Fecha (cifrada) en la que se genero el enlace para la modificacion de contraseña
date_link_signed = signing.dumps("%s-%s-%s" % (date_now.year, date_now.month, date_now.day))
link = request.build_absolute_uri("%s?userid=%s&key=%s&date=%s" % (
urlresolvers.reverse('confirmar_modificar_clave'),
username, hash_user(usr, is_reset=True).decode(),
date_link_signed
))
administrador, admin_email = '', ''
if settings.ADMINS:
administrador = settings.ADMINS[0][0]
admin_email = settings.ADMINS[0][1]
## Indica si el correo electrónico fue enviado
enviado = enviar_correo(usr.email, 'usuario.olvido.clave.mail', EMAIL_SUBJECT_REGISTRO, {
'link': link, 'emailapp': settings.EMAIL_FROM, 'administrador': administrador,
'admin_email': admin_email
})
if not enviado:
logger.warning(
str(_("Ocurrió un inconveniente al enviar el correo de recuperación de clave al usuario [%s]")
% username)
)
else:
form = OlvidoClaveForm()
alert = _("Se le ha enviado, al correo electrónico indicado, la información necesaria para la "
"modificación de la contraseña")
messages.info(request, _("Se le ha enviado, al correo electrónico indicado, la información necesaria "
"para la modificación de la contraseña"))
return render(request, 'usuario.recuperar.clave.html', {'form': form, 'alert': alert})
def confirmar_modificar_clave(request):
"""!
Función que permite confirmar el enlace enviado al usuario para la modificación de contraseña
@author Ing. Roldan Vargas (rvargas at cenditel.gob.ve)
@copyright GNU Public License versión 2 (GPLv2)
@date 06-05-2016
@param request {object} Objeto que contiene la petición
@return Devuelve un mensje al usuario indicando el estatus de la validación del enlace
"""
userid = request.GET.get('userid', None)
key = request.GET.get('key', None)
date_link_generate = request.GET.get('date', None)
verificado = False
mensaje = str(_("El usuario ha sido verificado"))
modificar_clave_url = None
if userid and key and date_link_generate and User.objects.filter(username=userid):
user = User.objects.get(username=userid)
## Fecha (descifrada) en la que se genero el enlace para la modificacion de contraseña
link_date = datetime.strptime(signing.loads(date_link_generate), "%Y-%m-%d")
if calcular_diferencia_fechas(link_date) <= CADUCIDAD_LINK_REGISTRO:
if key.strip() == hash_user(user, is_reset=True).decode():
modificar_clave_url = "%s?userid=%s&key=%s" % (
urlresolvers.reverse('modificar_clave'), user.username, hash_user(user, is_reset=True)
)
verificado = True
else:
mensaje = str(_("El usuario no puede ser verificado"))
else:
mensaje = str(_("El enlace utilizado expiró. Contacte al administrador del sistema."))
return render(request, 'usuario.validar.olvido.clave.html', {
'verificado': verificado, 'emailapp': settings.EMAIL_FROM, 'mensaje': mensaje,
'modificar_clave_url': modificar_clave_url
})
def modificar_clave(request):
"""!
Funcion que gestiona la modificación de contraseña
@author Ing. Roldan Vargas (rvargas at cenditel.gob.ve)
@copyright GNU Public License versión 2 (GPLv2)
@date 06-05-2016
@param request {object} Objeto que obtiene la petición
@return Redirecciona al usuario a la pagina de autenticación del sistema
"""
form = ModificarClaveForm()
username = request.GET.get('userid', None)
if request.method == "POST":
form = ModificarClaveForm(data=request.POST)
if form.is_valid():
username = username if username else request.POST['username']
user = User.objects.get(username=username)
user.set_password(request.POST['clave'])
user.save()
if UserProfile.objects.filter(user=user):
perfil = UserProfile.objects.get(user=user)
perfil.fecha_modpass = datetime.now()
perfil.save()
messages.info(request, _("Su contraseña ha sido modificada correctamente"))
alert = str(_("La contraseña fue modificada satisfactoriamente"))
logger.info(str(_("El usuario [%s] modificó su contraseña por olvido") % username))
form = AutenticarForm()
return render(request, 'base.template.html', {'form': form, 'alert': alert})
#return HttpResponseRedirect(urlresolvers.reverse("acceso"))
return render(request, 'usuario.modificar.clave.html', {'form': form, 'fortaleza_clave': True, 'username': username})
def confirmar_registro(request):
"""!
Función que permite confirmar el enlace enviado al usuario durante el registro
@author Ing. Roldan Vargas (rvargas at cenditel.gob.ve)
@copyright GNU Public License versión 2 (GPLv2)
@date 02-05-2016
@param request {object} Objeto que contiene la petición
@return Devuelve un mensje al usuario indicando el estatus de la validación del enlace
"""
userid = request.GET.get('userid', None)
key = request.GET.get('key', None)
verificado = False
mensaje = str(_("El usuario ha sido verificado"))
login_url = None
if userid and key and User.objects.filter(username=userid):
user = User.objects.get(username=userid)
if calcular_diferencia_fechas(user.date_joined) <= CADUCIDAD_LINK_REGISTRO:
if key.strip() == hash_user(user, is_new_user=True).decode():
if UserProfile.objects.filter(user=user, ocupacion='ES'):
# Si es estudiante el sistema activa automáticamente el usuario,
# en caso contrario debe esperar por la confirmación del administrador
user.is_active = True
user.save()
user_profile = UserProfile.objects.get(user=user)
user_profile.nivel_acceso = 3
else:
mensaje = str(_("El enlace fue verificado y el administrador esta evaluando sus credenciales para "
"otorgarle un nivel de acceso al sistema"))
login_url = "%s?userid=%s&key=%s" % (
urlresolvers.reverse('acceso'), user.username, hash_user(user, is_new_user=True)
)
verificado = True
else:
mensaje = str(_("El usuario no puede ser verificado"))
else:
mensaje = str(_("El enlace utilizado expiró. Contacte al administrador del sistema."))
return render(request, 'usuario.validar.cuenta.html', {
'verificado': verificado, 'emailapp': settings.EMAIL_FROM, 'mensaje': mensaje, 'login_url': login_url
})
class RegistroCreate(SuccessMessageMixin, CreateView):
"""!
Clase que registra usuarios en el sistema
@author Ing. Roldan Vargas (rvargas at cenditel.gob.ve)
@copyright GNU Public License versión 2 (GPLv2)
@date 25-04-2016
@version 2.0.0
"""
model = User
form_class = RegistroForm
template_name = 'usuario.registro.html'
success_url = reverse_lazy('acceso')
success_message = REGISTRO_MESSAGE
def form_valid(self, form):
"""!
Metodo que valida si el formulario es valido, en cuyo caso se procede a registrar los datos del usuario
@author Ing. Roldan Vargas (rvargas at cenditel.gob.ve)
@copyright GNU/GPLv2
@date 22-08-2016
@param self {object} Objeto que instancia la clase
@param form {object} Objeto que contiene el formulario de registro
@return Retorna el formulario validado
"""
self.object = form.save(commit=False)
self.object.username = form.cleaned_data['tipo_documento']
self.object.first_name = form.cleaned_data['nombre']
self.object.last_name = form.cleaned_data['apellido']
self.object.set_password(form.cleaned_data['password'])
self.object.email = form.cleaned_data['correo']
self.object.save()
## Crea el perfil del usuario
UserProfile.objects.create(
tipo_documento=form.cleaned_data['tipo_documento'],
institucion=form.cleaned_data['institucion'],
ocupacion=form.cleaned_data['ocupacion'],
user=self.object
)
## Asigna un enlace de verificación en el registro de usuarios
link = self.request.build_absolute_uri("%s?userid=%s&key=%s" % (
urlresolvers.reverse(confirmar_registro),
self.object.username, hash_user(self.object, is_new_user=True).decode()
))
administrador, admin_email = '', ''
if settings.ADMINS:
administrador = settings.ADMINS[0][0]
admin_email = settings.ADMINS[0][1]
## Indica si el correo electrónico fue enviado
enviado = enviar_correo(self.object.email, 'usuario.bienvenida.mail', EMAIL_SUBJECT_REGISTRO, {
'link': link, 'emailapp': settings.EMAIL_FROM, 'administrador': administrador, 'admin_email': admin_email
})
if not enviado:
logger.warning(
str(_("Ocurrió un inconveniente al enviar el correo de registro al usuario [%s]")
% self.object.username)
)
return super(RegistroCreate, self).form_valid(form)
class ModificarPerfilView(SuccessMessageMixin, UpdateView):
"""!
Clase que muestra el formulario de modificación de datos del perfil de usuario
@author Ing. Roldan Vargas (rvargas at cenditel.gob.ve)
@copyright GNU Public License versión 2 (GPLv2)
@date 02-12-2016
@version 1.0.0
"""
model = User #UserProfile
form_class = PerfilForm
template_name = 'usuario.update.html'
success_url = reverse_lazy('inicio')
success_message = UPDATE_MESSAGE
def get_initial(self, **kwargs):
"""!
Metodo que asigna los valores iniciales del formulario del perfil de usuario con los datos actualmente registrados
@author Ing. Roldan Vargas (rvargas at cenditel.gob.ve)
@copyright GNU/GPLv2
@date @date 02-12-2016
@return Retorna los valores iniciales del formulario
"""
usr = User.objects.get(username=self.request.user)
return {
'nombre': usr.first_name, 'apellido': usr.last_name, 'correo': usr.email, 'tipo_documento': usr.username,
'institucion': self.request.user.profile.institucion.pk, 'ocupacion': self.request.user.profile.ocupacion,
}
def form_valid(self, form):
"""!
Metodo que valida si el formulario es valido, en cuyo caso se procede a modificar los datos del perfil del usuario
@author Ing. Roldan Vargas (rvargas at cenditel.gob.ve)
@copyright GNU/GPLv2
@date @date 02-12-2016
@return Retorna el formulario validado y modifica los datos de perfil del usuario
"""
self.object = form.save(commit=False)
usr = User.objects.get(username=self.object.username)
self.object.password = usr.password
if self.object.first_name != form.cleaned_data['nombre']:
self.object.first_name = form.cleaned_data['nombre']
if self.object.last_name != form.cleaned_data['apellido']:
self.object.last_name = form.cleaned_data['apellido']
if self.object.email != form.cleaned_data['correo']:
self.object.email = form.cleaned_data['correo']
if form.cleaned_data.get('password', None):
self.object.set_password(form.cleaned_data['password'])
self.object.save()
if UserProfile.objects.filter(user__username=str(self.object.username)):
perfil = UserProfile.objects.get(user__username=str(self.object.username))
perfil.ocupacion = form.cleaned_data['ocupacion']
perfil.institucion = Institucion.objects.get(nombre=form.cleaned_data['institucion'])
perfil.save()
messages.info(self.request, UPDATE_MESSAGE)
return HttpResponseRedirect(self.get_success_url())