
org.javabeanstack.security.Sessions Maven / Gradle / Ivy
The newest version!
/*
* JavaBeanStack FrameWork
*
* Copyright (C) 2017 Jorge Enciso
* Email: [email protected]
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.javabeanstack.security;
import org.javabeanstack.security.model.IUserSession;
import org.javabeanstack.security.model.UserSession;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import javax.annotation.PostConstruct;
import javax.crypto.BadPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.ejb.EJB;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.Startup;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import org.apache.log4j.Logger;
import org.javabeanstack.crypto.CipherUtil;
import org.javabeanstack.crypto.DigestUtil;
import org.javabeanstack.data.DBLinkInfo;
import org.javabeanstack.data.IDBLinkInfo;
import org.javabeanstack.error.ErrorManager;
import org.javabeanstack.error.ErrorReg;
import org.javabeanstack.data.IGenericDAO;
import org.javabeanstack.model.IAppCompany;
import org.javabeanstack.model.IAppCompanyAllowed;
import org.javabeanstack.model.IAppUser;
import org.javabeanstack.util.Fn;
import org.javabeanstack.util.Strings;
import org.javabeanstack.model.IAppAuthConsumerToken;
import org.javabeanstack.security.model.IClientAuthRequestInfo;
import org.javabeanstack.util.LocalDates;
/**
* Es la clase encargada de todas las sesiones de usuarios. Guarda información
* de cada sesión y gestión la creación y expiración de la sesión.
*
* El test unitario se encuentra en TestProjects clase
* py.com.oym.test.data.TestSesiones
*
* @author Jorge Enciso
*/
//@Singleton
@Startup
@Lock(LockType.READ)
public class Sessions implements ISessions {
private static final Logger LOGGER = Logger.getLogger(Sessions.class);
protected final Map sessionVar = new HashMap<>();
protected boolean oneSessionPerUser = false;
private SecretKey secretKey;
private final Map tokenCache = new HashMap();
private final Map sessionsInfo = new HashMap();
@EJB
protected IGenericDAO dao;
@EJB
private IOAuthConsumer oAuthConsumer;
/**
* Se ejecuta al instanciarse esta clase.
*
*/
@PostConstruct
private void init() {
try {
secretKey = CipherUtil.getSecureRandomKey(CipherUtil.BLOWFISH, 128);
} catch (NoSuchAlgorithmException ex) {
ErrorManager.showError(ex, LOGGER);
}
}
@Override
public Object getSessionInfo(String sessionId, String key) {
return sessionsInfo.get(new SessionInfo(sessionId, key));
}
@Override
public void addSessionInfo(String sessionId, String key, Object info) {
IUserSession userSession = getUserSession(sessionId);
if (userSession == null || userSession.getUser() == null) {
removeAllSessionInfo(sessionId);
return;
}
sessionsInfo.put(new SessionInfo(sessionId, key), info);
}
@Override
public void removeSessionInfo(String sessionId, String key) {
this.sessionsInfo.remove(new SessionInfo(sessionId, key));
}
private void removeAllSessionInfo(String sessionId) {
for (Iterator> it = sessionsInfo.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = it.next();
if (entry.getKey().sessionId.equals(sessionId)) {
it.remove();
}
}
}
/**
* Crea una sesión de usuario para acceso a la app
*
* @param userLogin usuario
* @param password password
* @param idcompany empresa que esta solicitando ingresar
* @param idleSessionExpireInMinutes minutos sin actividad antes de cerrar
* la sesión.
* @return objeto conteniendo datos del login exitoso o rechazado
*/
@Override
@Lock(LockType.WRITE)
public IUserSession createSession(String userLogin, String password, Object idcompany, Integer idleSessionExpireInMinutes) {
LOGGER.debug("CREATESESSION IN");
try {
// Verifcar si coincide usuario, contraseña y pasar resultado para ser procesado
IUserSession session = login(userLogin, password);
processCreateSession(session, idcompany, idleSessionExpireInMinutes);
return session;
} catch (Exception exp) {
ErrorManager.showError(exp, LOGGER);
}
return null;
}
/**
* Procesa la creación de la sesión del usuario al sistema.
*
* @param session variable creada en el metodo login que va a ser procesada.
* @param idcompany empresa que esta solicitando ingresar
* @param idleSessionExpireInMinutes minutos sin actividad antes de cerrar
* la sesión.
* @return verdadero o falso si pudo o no crear la sesión.
* @throws Exception
*/
@Lock(LockType.WRITE)
protected boolean processCreateSession(IUserSession session, Object idcompany, Integer idleSessionExpireInMinutes) throws Exception {
LOGGER.debug("PROCESSCREATESESSION IN");
if (session == null || session.getUser() == null) {
return false;
}
String sessionId = createSessionId(session);
// Si se permite solo una sesión por usuario
if (oneSessionPerUser) {
// Válidar que el usuario no este ya logueado
IUserSession sessionCtrl = getUserSession(encrypt(sessionId));
if (sessionCtrl != null && sessionCtrl.getUser() != null) {
session.setUser(null);
String mensaje = "Este usuario tiene una sesión activa";
LOGGER.debug(mensaje);
session.setError(new ErrorReg(mensaje, 4, ""));
return false;
}
}
// Verificar si tiene permiso para acceder a los datos de la empresa
if (!session.getUser().getRol().contains(IAppUser.ANALISTA)
&& !checkCompanyAccess(((IAppUser) session.getUser()).getIduser(), (Long) idcompany)) {
session.setUser(null);
String mensaje = "No tiene autorización para acceder a esta empresa";
LOGGER.debug(mensaje);
session.setError(new ErrorReg(mensaje, 4, ""));
return false;
}
Map parameters = new HashMap();
parameters.put("idcompany", idcompany);
IAppCompany company = dao.findByQuery(null,
"select o from AppCompanyLight o "
+ " where idcompany = :idcompany", parameters);
// Agregar atributos adicionales a la sesión
String persistUnit = company.getPersistentUnit().trim();
session.setPersistenceUnit(persistUnit); //Unidad de persistencia
session.setCompany(company); // Empresa logueada
session.setIdCompany(Long.parseLong(idcompany.toString()));
// Id de sesión encriptada por seguridad.
session.setSessionId(encrypt(sessionId));
// Tiempo de expiración en minutos desde ultima actividad
session.setIdleSessionExpireInMinutes(idleSessionExpireInMinutes);
// Metodo que se ejecuta al final del proceso con el fin de que en clases derivadas
// se pueda realizar tareas adicionales como anexar otros atributos a la sesión.
afterCreateSession(session);
// Agregar sesión al pool de sesiones
sessionVar.put(sessionId, session);
LOGGER.debug("Sesión creada: " + sessionId);
return true;
}
protected void afterCreateSession(IUserSession session) throws Exception {
}
/**
* Vuelve a crear la sesión con el acceso a una nueva empresa
*
* @param sessionIdEncrypted identificador de la sesión.
* @param idcompany identificador de la empresa a la que se solicita el
* nuevo acceso.
* @return objeto sesión
*/
@Override
@Lock(LockType.WRITE)
public IUserSession reCreateSession(String sessionIdEncrypted, Object idcompany) {
LOGGER.debug("RECREATESESSION IN");
String sessionId = decrypt(sessionIdEncrypted);
// Verificar válides de la sesión
UserSession session = getUserSession(sessionIdEncrypted);
if (session == null || session.getError() != null) {
if (session == null) {
return null;
}
LOGGER.debug(session.getError().getMessage());
return session;
}
// Eliminar sesión
sessionVar.remove(sessionId);
try {
//Crear nueva sesión
processCreateSession(session, idcompany, null);
return session;
} catch (Exception exp) {
ErrorManager.showError(exp, LOGGER);
}
return null;
}
/**
* Verifica si el iduser proporcionado es válido
*
* @param iduser id del usuario
* @return una variable ErrorReg si es nulo es válido si no hubo algún error
* @throws java.lang.Exception
*/
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public boolean isUserValid(Long iduser) throws Exception {
return (checkUser(iduser) == null);
}
/**
* Verifica si el iduser proporcionado es válido
*
* @param iduser id del usuario
* @return una variable ErrorReg si es nulo es válido si no hubo algún error
* @throws Exception
*/
protected ErrorReg checkUser(Long iduser) throws Exception {
LOGGER.debug("IsUserValid IN");
String mensaje;
Map params = new HashMap<>();
params.put("iduser", iduser);
if (Fn.nvl(iduser, 0L) != 0L) {
// Verificar existencia del usuario
IAppUser usuario = dao.findByQuery(null,
"select o from AppUserLight o where iduser = :iduser",
params);
// Verificar que exista el usuario
if (usuario == null) {
mensaje = "Este usuario " + iduser + " no existe";
LOGGER.debug(mensaje);
return new ErrorReg(mensaje, 1, "");
}
// Verificar que el usuario este activo.
if (usuario.getDisabled()) {
mensaje = "La cuenta " + usuario.getLogin().trim() + " esta inactivo";
LOGGER.info(mensaje);
return new ErrorReg(mensaje, 2, "");
}
// Verificar que no expiro la cuenta
if (usuario.getExpiredDate().isBefore(LocalDates.now())) {
mensaje = "La cuenta " + usuario.getLogin() + " expiro";
LOGGER.debug(mensaje);
return new ErrorReg(mensaje, 2, "");
}
return null;
}
mensaje = "Este usuario " + iduser + " no existe";
LOGGER.debug(mensaje);
return new ErrorReg(mensaje, 1, "");
}
/**
* Devuelve verdadero si sus credenciales para el logeo son válidas o falso
* si no
*
* @param userLogin usuario
* @param password contraseña.
* @return userSession conteniendo información de la sesión del usuario
* @throws Exception
*/
@Override
public IUserSession login(String userLogin, String password) throws Exception {
LOGGER.debug("LOGIN IN");
String mensaje;
Map params = new HashMap<>();
params.put("userLogin", userLogin);
UserSession userSession;
if (userLogin != null) {
// Verificar existencia del usuario
IAppUser usuario = dao.findByQuery(null,
"select o from AppUserLight o where code = :userLogin",
params);
userSession = new UserSession();
// Verificar que exista el usuario
if (usuario == null) {
mensaje = "Este usuario " + userLogin + " no existe";
LOGGER.debug(mensaje);
userSession.setError(new ErrorReg(mensaje, 1, ""));
return userSession;
}
// Verificar que el usuario este activo.
if (usuario.getDisabled()) {
mensaje = "La cuenta " + usuario.getLogin().trim() + " esta inactivo";
LOGGER.info(mensaje);
userSession.setError(new ErrorReg(mensaje, 2, ""));
return userSession;
}
// Verificar que no expiro la cuenta
if (usuario.getExpiredDate().isBefore(LocalDates.now())) {
mensaje = "La cuenta " + usuario.getLogin() + " expiro";
LOGGER.debug(mensaje);
userSession.setError(new ErrorReg(mensaje, 2, ""));
return userSession;
}
// Verificar que la contraseña sea correcta
String claveEncriptada = getEncryptedPass(usuario, password);
if (!claveEncriptada.equals(usuario.getPass())) {
userSession.setError(new ErrorReg("Contraseña incorrecta", 3, ""));
return userSession;
}
userSession.setUser(usuario);
return userSession;
}
return null;
}
/**
* Cierra una sesión
*
* @param sessionIdEncrypted identificador de la sesión a cerrar
*/
@Override
@Lock(LockType.WRITE)
public void logout(String sessionIdEncrypted) {
LOGGER.debug("LOGOUT IN");
try {
String sessionId = decrypt(sessionIdEncrypted);
sessionVar.remove(sessionId);
} catch (Exception ex) {
//
}
}
/**
* Chequea si un usuario tiene permiso a acceder a una empresa determinada
*
* @param iduser identificador del usuario
* @param idcompany identificador de la empresa
* @return verdadero si tiene permiso el usuario y falso si no
* @throws Exception
*/
@Override
public Boolean checkCompanyAccess(Long iduser, Long idcompany) throws Exception {
LOGGER.debug("CHECKCOMPANYACCESS IN");
Map params = new HashMap<>();
params.put("iduser", iduser);
params.put("idcompany", idcompany);
IAppCompanyAllowed row = dao.findByQuery(null,
"select o from AppCompanyAllowed o "
+ "where iduser = :iduser and idcompany = :idcompany",
params);
if (row != null) {
return !row.getDeny();
}
return true;
}
/**
* Devuelve un objeto sesión correspondiente a una sesión solicitada
*
* @param sessionIdEncrypted identificador de la sesión encriptada.
* @return objeto con los datos de la sesión solicitada
*/
@Override
public UserSession getUserSession(String sessionIdEncrypted) {
LOGGER.debug("GETUSERSESSION IN ");
if (sessionIdEncrypted == null) {
return null;
}
String sessionId;
try {
sessionId = decrypt(sessionIdEncrypted);
LOGGER.debug("SESSION ENCRYPTADA: " + sessionIdEncrypted);
LOGGER.debug("SESSION : " + sessionId);
} catch (Exception exp) {
return null;
}
UserSession sesion = (UserSession) sessionVar.get(sessionId);
if (sesion != null) {
Integer expireInMinutes = sesion.getIdleSessionExpireInMinutes();
if (expireInMinutes == null) {
expireInMinutes = 30;
}
// Verificar si ya expiro su sesión
Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
cal1.setTime(sesion.getLastReference());
cal2.setTime(new Date());
long time1 = cal1.getTimeInMillis();
long time2 = cal2.getTimeInMillis();
// Diferencias en minutos desde la ultima vez que se hizo referencia a esta sesión.
long idleInMinutes = (time2 - time1) / (60 * 1000);
if (idleInMinutes >= expireInMinutes) {
sessionVar.remove(sessionId);
sesion.setUser(null);
String mensaje = "La sesión expiro";
sesion.setError(new ErrorReg(mensaje, 6, ""));
return sesion;
}
sesion.setLastReference(new Date());
}
return sesion;
}
/**
* Objeto con la información necesaria para acceder a la base de datos.
* (persistunit, session del usuario)
*
* @param sessionId identificador de la sesión o el token
* @return DBLinkInfo
*/
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
@Override
public IDBLinkInfo getDBLinkInfo(String sessionId) {
IDBLinkInfo dbLinkInfo = new DBLinkInfo();
if (!Strings.isNullorEmpty(sessionId)) {
IUserSession userSession = getUserSession(sessionId);
if (userSession != null) {
dbLinkInfo.setUserSession(userSession);
} else {
if (oAuthConsumer.isValidToken(sessionId)) {
IAppAuthConsumerToken token = oAuthConsumer.findAuthToken(sessionId);
if (token != null) {
try {
dbLinkInfo.setToken(token, oAuthConsumer, true);
} catch (Exception exp) {
ErrorManager.showError(exp, LOGGER);
}
}
}
}
}
return dbLinkInfo;
}
/**
* Verifica consistencia de el modelo AuthConsumerData
*
* @param data modelo OAuthConsumerData
* @return verdadero si es válido o falso si no
*/
@Override
public boolean checkAuthConsumerData(IOAuthConsumerData data) {
if (data == null) {
return false;
}
try {
Long iduser = data.getIdAppUser();
Long idcompany = data.getIdCompany();
String userLogin = data.getUserLogin();
String userPass = data.getUserPass();
if (!Fn.nvl(userLogin, "").isEmpty()) {
IUserSession session = login(userLogin, userPass);
if (session == null) {
return false;
}
iduser = session.getUser().getIduser();
} else {
if (checkUser(iduser) != null) {
return false;
}
}
return checkCompanyAccess(iduser, idcompany);
} catch (Exception exp) {
ErrorManager.showError(exp, LOGGER);
}
return false;
}
/**
* Devuelve un texto que identificará a la sesión de forma unica.
*
* @param userSession sesión creada desde el metodo login()
* @return identificador de sesión.
*/
protected String createSessionId(IUserSession userSession) {
if (userSession == null || userSession.getUser() == null) {
return null;
}
// Esto puede ser modificado en clases derivadas.
String sessionId = userSession.getUser().getId() + ":" + userSession.getUser().getPass().toUpperCase().trim();
Date fecha = new Date();
if (!oneSessionPerUser) {
sessionId += ":" + fecha.getTime();
}
return sessionId;
}
/**
* Password cifrado que se utilizará para comparar con el password
* almacenado en la base de datos.
*
* @param user usuario
* @param password password
* @return Password cifrado que se utilizará para comparar con el dato
* almacenado en la base de datos.
*/
protected String getEncryptedPass(IAppUser user, String password) {
// Esto puede ser modificado en clases derivadas
String md5 = user.getLogin().toUpperCase().trim() + ":" + password.trim();
return DigestUtil.md5(md5).toUpperCase();
}
/**
* Encripta un mensaje (en este caso el sesionId)
*
* @param msg mensaje
* @return mensaje cifrado
*/
protected final String encrypt(String msg) {
Cipher cipher;
try {
cipher = Cipher.getInstance(CipherUtil.BLOWFISH);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encrypted = cipher.doFinal(msg.getBytes());
return Fn.bytesToHex(encrypted);
} catch (InvalidKeyException | NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException | NoSuchPaddingException ex) {
ErrorManager.showError(ex, LOGGER);
}
return null;
}
/**
* Desencripta un mensaje (en este caso el sessionId)
*
* @param msgEncrypted mensaje encriptado.
* @return mensaje descifrado
*/
protected final String decrypt(String msgEncrypted) {
try {
byte[] encrypted = Fn.hexToByte(msgEncrypted);
Cipher cipher = Cipher.getInstance(CipherUtil.BLOWFISH);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(encrypted));
} catch (InvalidKeyException | NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException | NoSuchPaddingException ex) {
ErrorManager.showError(ex, LOGGER);
}
return null;
}
@Override
public IClientAuthRequestInfo getClientAuthCache(String authHeader) {
IClientAuthRequestInfo info = tokenCache.get(authHeader);
//Si no existe
if (info == null) {
return null;
}
//Si se registro en el cache hace más de un día eliminar del cache
if (LocalDates.daysInterval(info.getLogDate(), LocalDates.now()) > 1) {
tokenCache.remove(authHeader);
return null;
}
return info;
}
@Override
public void addClientAuthCache(String authHeader, IClientAuthRequestInfo authRequestInfo) {
tokenCache.put(authHeader, authRequestInfo);
}
public class SessionInfo {
String sessionId;
String key;
public SessionInfo() {
}
public SessionInfo(String sessionId, String key) {
this.sessionId = sessionId;
this.key = key;
}
@Override
public int hashCode() {
int hash = 5;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final SessionInfo other = (SessionInfo) obj;
if (!Objects.equals(this.sessionId, other.sessionId)) {
return false;
}
if (!Objects.equals(this.key, other.key)) {
return false;
}
return true;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy