Package com.cosmo.security.auth.impl

Source Code of com.cosmo.security.auth.impl.PostgreSqlAuthenticationImpl

package com.cosmo.security.auth.impl;

import java.security.GeneralSecurityException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;

import javax.servlet.http.HttpServletRequest;

import com.cosmo.Workspace;
import com.cosmo.data.DataAgent;
import com.cosmo.data.DataException;
import com.cosmo.data.DataFactory;
import com.cosmo.security.User;
import com.cosmo.security.UserAlreadyExistsException;
import com.cosmo.security.UserNotFoundException;
import com.cosmo.security.auth.Authentication;
import com.cosmo.security.auth.AuthenticationException;
import com.cosmo.security.auth.AuthenticationFactory;
import com.cosmo.util.CryptoUtils;
import com.cosmo.util.PluginProperties;

/**
* Implementa el proveedor de autenticaci�n nativo de Cosmo (versi�n para JDBC/PostgreSQL).
* <br /><br />
* Esta implementaci�n incorpora adicionalmente la API de administraci�n de autenticaci�n RDBMS.
*
* @version 1.0.0
* @author Gerard Llort
*/
public class PostgreSqlAuthenticationImpl implements Authentication
{
   private static String PARAM_LOCKCONTROL = "lockControl";
   private static String PARAM_ATTEMPTS = "lockAttempts";
   private static String PARAM_TIMEOUT = "lockTimeout";
  
   private static String TABLE_NAME = "cosmo_users";
   private static String TABLE_LOCKS = "cosmo_auth_locks";

   private Workspace workspace;
   private PluginProperties agent;
  
  
   //==============================================
   // Constructors
   //==============================================
  
   /**
    * Constructor de la clase.
    *
    * @param workspace Una instancia de {@link Workspace} que representa el workspace al que est� conectado actualmente.
    */
   public PostgreSqlAuthenticationImpl(Workspace workspace)
   {
      this.workspace = workspace;
      this.agent = this.workspace.getProperties().getSecurityProperties().getAuthenticationAgent();
   }
  
  
   //==============================================
   // Properties
   //==============================================
  
   /**
    * Devuelve un {@code hash} que contiene los par�metros de configuraci�n del agente de seguridad.
    */
   public HashMap<String, String> getParameters()
   {
      return agent.getParams();
   }
  
  
   //==============================================
   // Methods (interface Authentication)
   //==============================================
  
   /**
    * Verifica las credenciales de un usuario.
    *
    * @param login Login del usuario.
    * @param password Contrase�a (sin encriptar) del usuario.
    * @return Una instancia de {@link User} que representa el usuario al que corresponden las credenciales proporcionadas.
    *
    * @throws UserNotFoundException
    * @throws AuthenticationException
    */
   @Override
   public User login(String login, String password) throws UserNotFoundException, AuthenticationException
   {
      String sql;
      User user = null;
      // DataSource ds;
      DataAgent conn = null;
     
      // Comprobaci�n de cuenta bloqueada
      if (agent.getParamBoolean(PARAM_LOCKCONTROL, false))
      {
         // Obtiene la configuraci�n del agente
         int attemps = agent.getParamInteger(PARAM_ATTEMPTS, 5);
         int timeout = agent.getParamInteger(PARAM_TIMEOUT, 30);

         // Determina si la cuenta del usuario est� o no bloqueada
         if (isLocked(login, attemps, timeout))
         {
            throw new AuthenticationException("La cuenta " + login + " est� bloqueada.");
         }
      }

      try
      {
         sql = "SELECT * " +
               "FROM  " + TABLE_NAME + " " +
               "WHERE Lower(usrlogin) = '" + login.trim().toLowerCase() + "' And " +
               "      usrpwd = '" + CryptoUtils.encrypt(password) + "'";

         conn = DataFactory.getInstance(workspace);
         // ds = this.workspace.getProperties().getDataProperties().getDataSource();
         // conn = new DataConnection(ds);
         // conn.connect();
         ResultSet rs = conn.executeSql(sql);
         if (rs.next())
         {
            user = new User();
            user.setLogin(rs.getString("usrlogin"));
            user.setMail(rs.getString("usrmail"));
            user.setName(rs.getString("usrname"));
            user.setCreated(rs.getDate("usrcreated"));
            user.setLastLogin(rs.getDate("usrlastlogin"));
            user.setLogonCount(rs.getInt("usrlogoncount"));
         }
         else
         {
            // Si tiene el control de bloqueo activado, actualiza la informaci�n de bloqueo
            if (agent.getParamBoolean(PARAM_LOCKCONTROL, false))
            {
               loginFail(login);
            }

            throw new UserNotFoundException();
         }

         // Actualiza los datos estadísticos y de control del usuario
         sql = "UPDATE " + TABLE_NAME + " " +
               "SET usrlastlogin  = current_timestamp, " +
               "    usrlogoncount = usrlogoncount + 1 " +
               "WHERE Lower(usrlogin) = '" + login.trim().toLowerCase() + "'";
         conn.execute(sql);

         // Confirma los cambios en la bbdd
         if (!conn.isAutoCommit()) conn.commit();
      }
      catch (SQLException ex)
      {
         throw new AuthenticationException(ex.getMessage(), ex);
      }
      catch (GeneralSecurityException ex)
      {
         throw new AuthenticationException(ex.getMessage(), ex);
      }
      catch (UserNotFoundException ex)
      {
         throw ex;
      }
      catch (Exception ex)
      {
         throw new AuthenticationException(ex.getMessage(), ex);
      }
      finally
      {
         conn.disconnect();
      }             

      return user;
   }

   /**
    * Elimina la informaci�n de autenticaci�n actual.
    */
   @Override
   public void logout()
   {
      // Este driver no informa al SGBD de la salida del usuario
      return;
   }

   /**
    * Revalida la sesi�n de usuario.
    */
   @Override
   public void validate()
   {
      // Este driver no revalida la sesi�n
      return;  
   };

   /**
    * Indica si el servicio usa un gateway para la autenticaci�n de usuarios.
    */
   public boolean isLoginGatewayRequired()
   {
      return false;
   }

   /**
    * Indica si una respuesta corresponde al retorno de la acci�n de login.
    *
    * @param request Una instancia de {@link HttpServletRequest} que cotniene el contexto de la llamada.
    *
    * @return {@code true} si la petici�n corresponde al retorno de la pantalla de login o {@code false} en cualquier otro caso.
    */
   public boolean isLoginGatewayResponse(HttpServletRequest request)
   {
      return false;
   }

   /**
    * Detecta si una autenticaci�n delegada (Login Gateway) ha sido exitosa.<br />
    * Las clases que extiendan a {@link AuthenticationFactory} ser�n responsables de obtener los datos del usuario
    * autenticado en el sistema externo, ya sea mediante servicios REST u otros mecanismos.
    *
    * @param request Una instancia de {@link HttpServletRequest} que cotniene el contexto de la llamada.
    *
    * @return Una instancia de {@link User} que contiene las propiedades del usuario autenticado o {@code null} en cualquier otro caso.
    */
   public User getLoginGatewayUser(HttpServletRequest request)
   {
      return null;
   }

   /**
    * Devuelve la URL usada para la autenticaci�n de usuarios.
    */
   public String getLoginGatewayUrl()
   {
      return null;
   }


   //==============================================
   // Methods
   //==============================================

   /**
    * Comprueba si un determinado login existe.
    *
    * @param login Una cadena que contiene el login a comprobar.
    *
    * @return {@code true} si el login corresponde a una cuenta de usuario o {@code false} en cualquier otro caso.
    *
    * @throws AuthenticationException
    */
   public boolean loginExist(String login) throws AuthenticationException
   {
      String sSQL;
      // DataSource ds;
      // DataConnection conn = null;
      DataAgent conn = null;

      try
      {
         // Comprueba si existe el usuario
         sSQL = "SELECT Count(*) " +
                "FROM " + TABLE_NAME + " " +
                "WHERE Lower(usrlogin) = '" + login.trim().toLowerCase() + "'";

         conn = DataFactory.getInstance(workspace);
         // ds = this.workspace.getProperties().getDataProperties().getDataSource();
         // conn = new DataConnection(ds);
         // conn.connect();

         return (conn.executeScalar(sSQL) > 0);
      }
      catch (Exception ex)
      {
         throw new AuthenticationException(ex.getMessage(), ex);
      }
      finally
      {
         conn.disconnect();
      }
   }

   /**
    * Crea una nueva cuenta de usuario.
    *
    * @param user Una instancia de {@link User} que representa el nuevo usuario.
    *    
    * @throws UserAlreadyExistsException
    * @throws AuthenticationException
    */
   public void add(User user, String password) throws UserAlreadyExistsException, AuthenticationException
   {
      String sSQL;
      ResultSet rs;
      // DataSource ds;
      // DataConnection conn = null;
      DataAgent conn;

      try
      {
         // Comprueba si existe alg�n usuario con mismo LOGIN o CORREO
         sSQL = "SELECT Count(*) " +
                "FROM " + TABLE_NAME + " " +
                "WHERE Lower(usrlogin) = '" + user.getLogin().trim().toLowerCase() + "' Or " +
                "      Lower(usrmail)  = '" + user.getMail().trim().toLowerCase()  + "'";

         conn = DataFactory.getInstance(workspace);
         // ds = this.workspace.getProperties().getDataProperties().getDataSource();
         // conn = new DataConnection(ds);
         // conn.connect();
         rs = conn.executeSql(sSQL);
         if (rs.next() && rs.getInt(1) > 0)
         {
            throw new UserAlreadyExistsException();
         }

         sSQL = "INSERT INTO " + TABLE_NAME + " (usrlogin, usrmail, usrpwd, usrname, usrcreated, usrlastlogin, usrlogoncount) " +
                "VALUES ('" + user.getLogin() + "', " +
                "        '" + user.getMail() + "', " +
                "        '" + CryptoUtils.encrypt(password) + "', " +
                "        '" + user.getName() + "', " +
                "             current_timestamp, " +
                "             null, " +
                "         " + 0 + ")";

         conn.execute(sSQL);

         // Confirma los cambios en la bbdd
         if (!conn.isAutoCommit()) conn.commit();
      }
      catch (Exception ex)
      {
         throw new AuthenticationException(ex.getMessage(), ex);
      }
      finally
      {
         // conn.disconnect();
      }
   }

   /**
    * Actualiza los datos de un usuario (el login no se puede modificar y la contrase�a no se actualiza mediante este m�todo).
    *
    * @param user Una instancia de {@link User} que representa el nuevo usuario.
    *    
    * @throws UserAlreadyExistsException
    * @throws AuthenticationException
    */
   public void update(User user) throws UserNotFoundException, AuthenticationException
   {
      String sSQL;
      DataAgent conn = null;

      // Comprueba si existe el usuario especificado
      if (!loginExist(user.getLogin()))
      {
         throw new UserNotFoundException();
      }

      try
      {
         sSQL = "UPDATE " + TABLE_NAME + " " +
                "SET   usrmail = '" + DataAgent.sqlFormatTextValue(user.getMail()) + "', " +
                "      usrname = '" + DataAgent.sqlFormatTextValue(user.getName()) + "' " +
                "WHERE Lower(usrlogin) = '" + user.getLogin().trim().toLowerCase() + "'";

         conn = DataFactory.getInstance(workspace);
         conn.execute(sSQL);

         // Confirma los cambios en la bbdd
         if (!conn.isAutoCommit()) conn.commit();
      }
      catch (Exception ex)
      {
         throw new AuthenticationException(ex.getMessage(), ex);
      }
      finally
      {
         if (conn != null)
         {
            conn.disconnect();
         }
      }
   }

   /**
    * Elimina una cuenta de usuario.
    *
    * @param login Una cadena que representa el login del usuario.
    *    
    * @throws UserNotFoundException
    * @throws AuthenticationException
    */
   public void delete(String login) throws UserNotFoundException, AuthenticationException
   {
      String sSQL;
      DataAgent conn = null;

      // Comprueba si existe el usuario especificado
      if (!loginExist(login))
      {
         throw new UserNotFoundException();
      }

      try
      {
         // Elimina el usuario
         sSQL = "DELETE FROM " + TABLE_NAME + " " +
                "WHERE Lower(usrlogin) = '" + login.trim().toLowerCase() + "'";

         conn = DataFactory.getInstance(workspace);
         conn.execute(sSQL);

         // Confirma los cambios en la bbdd
         if (!conn.isAutoCommit()) conn.commit();
      }
      catch (Exception ex)
      {
         throw new AuthenticationException(ex.getMessage(), ex);
      }
      finally
      {
         if (conn != null)
         {
            conn.disconnect();
         }
      }
   }

   /**
    * Actualiza el password de un determinado usuario.
    *
    * @param login Login del usuario.
    * @param oldPassword Contrase�a actual.
    * @param newPassword Nueva contrase�a.
    *
    * @throws UserNotFoundException
    * @throws AuthenticationException
    */
   public void setUserPassword(String login, String oldPassword, String newPassword) throws UserNotFoundException, AuthenticationException
   {
      String sSQL;
      DataAgent conn = null;

      try
      {
         // Comprueba que exista el usuario y que el password actual sea el correcto
         sSQL = "SELECT Count(*) " +
                "FROM  " + TABLE_NAME + " " +
                "WHERE Lower(usrlogin) = '" + login.trim().toLowerCase() + "' And " +
                "      usrpwd = '" + CryptoUtils.encrypt(oldPassword) + "'";

         conn = DataFactory.getInstance(workspace);
         if (conn.executeScalar(sSQL) <= 0)
         {
            throw new UserNotFoundException();
         }

         // Actualiza la contrase�a del usuario
         sSQL = "UPDATE " + TABLE_NAME + " " +
                "SET   usrpwd = '" + CryptoUtils.encrypt(newPassword) + "' " +
                "WHERE Lower(usrlogin) = '" + login.trim().toLowerCase() + "'";

         conn.execute(sSQL);

         // Confirma los cambios en la bbdd
         if (!conn.isAutoCommit()) conn.commit();
      }
      catch (Exception ex)
      {
         throw new AuthenticationException(ex.getMessage(), ex);
      }
      finally
      {
         if (conn != null)
         {
            conn.disconnect();
         }
      }
   }

   /**
    * Obtiene un listado con todos los usuarios.
    *
    * @return Una instancia de {@link ResultSet} que contiene los datos del listado.
    *
    * @throws AuthenticationException
    */
   public ArrayList<User> getUsers() throws AuthenticationException
   {
      String sSQL;
      ResultSet rs;
      DataAgent conn = null;

      ArrayList<User> users = new ArrayList<User>();

      try
      {
         sSQL = "SELECT   * " +
                "FROM     " + TABLE_NAME + " " +
                "ORDER BY usrlogin Asc";

         conn = DataFactory.getInstance(workspace);
         rs = conn.executeSql(sSQL);
         while (rs.next())
         {
            users.add(readUser(rs));
         }

         return users;
      }
      catch (Exception ex)
      {
         throw new AuthenticationException(ex.getMessage(), ex);
      }
      finally
      {
         if (conn != null)
         {
            conn.disconnect();
         }
      }
   }

   /**
    * Obtiene una lista de usuario cuyo perfil contiene determinado texto.<br />
    * Los campos d�nde se efect�a la b�squeda son: {@code usrlogin}, {@code usrmail} y {@code usrname}.
    *
    * @param filter La cadena que debe contener al menos uno de los campos (como fragmento o cadena completa).
    *
    * @return Una lista de instancias {@link User} que coinciden con la b�squeda.
    *
    * @throws AuthenticationException
    */
   public ArrayList<User> findUsers(String filter) throws AuthenticationException
   {
      String sSQL;
      ResultSet rs;
      DataAgent conn = null;

      ArrayList<User> users = new ArrayList<User>();

      try
      {
         sSQL = "SELECT   * " +
                "FROM     " + TABLE_NAME + " " +
                "WHERE usrlogin LIKE '%" + DataAgent.sqlFormatTextValue(filter) + "%' Or " +
                "      usrname  LIKE '%" + DataAgent.sqlFormatTextValue(filter) + "%' Or " +
                "      usrmail  LIKE '%" + DataAgent.sqlFormatTextValue(filter) + "%' " +
                "ORDER BY usrlogin Asc";

         conn = DataFactory.getInstance(workspace);
         rs = conn.executeSql(sSQL);
         while (rs.next())
         {
            users.add(readUser(rs));
         }

         return users;
      }
      catch (Exception ex)
      {
         throw new AuthenticationException(ex.getMessage(), ex);
      }
      finally
      {
         if (conn != null)
         {
            conn.disconnect();
         }
      }
   }

   /**
    * Obtiene un listado con todos los usuarios.
    *
    * @return Una instancia de {@link ResultSet} que contiene los datos del listado.
    *
    * @throws AuthenticationException
    */
   public ResultSet getUsersList() throws AuthenticationException
   {
      String sSQL;
      DataAgent conn = null;

      try
      {
         sSQL = "SELECT   usrlogin   As Login, " +
                "         usrname    As Nom, " +
                "         usrmail    As Mail, " +
                "         usrcreated As Creat " +
                "FROM " + TABLE_NAME + " " +
                "ORDER BY usrlogin Asc";

         conn = DataFactory.getInstance(workspace);
         return conn.executeSql(sSQL);
      }
      catch (Exception ex)
      {
         throw new AuthenticationException(ex.getMessage(), ex);
      }
      finally
      {
         if (conn != null)
         {
            conn.disconnect();
         }
      }
   }


   //==============================================
   // Private members
   //==============================================

   /**
    * Lee un usuario de un registro en una instancia de {@link ResultSet}.
    *
    * @param rs La instancia de {@link ResultSet} con el cursor debidamente posicionado.
    *
    * @return Una instancia de {@link User} que representa al usuari leido.
    *
    * @throws SQLException
    */
   private User readUser(ResultSet rs) throws SQLException
   {
      User user = new User();
      user.setLogin(rs.getString("usrlogin"));
      user.setMail(rs.getString("usrmail"));
      user.setName(rs.getString("usrname"));
      user.setCreated(rs.getDate("usrcreated"));
      user.setLastLogin(rs.getDate("usrlastlogin"));
      user.setLogonCount(rs.getInt("usrlogoncount"));

      return user;
   }

   /**
    * Determina si la cuenta de usuario se encuentra bloqueada (por exceso de intentos fallidos de login).
    * <br /><br />
    * La llamada a este m�todo tambi�n limpia la tabla de los registros cuyo bloqueo a caducado, de esta manera no se
    * precisa mantenimiento de esta tabla ya que se actualiza a cada login que realiza cualquier usuario.
    *
    * @param login Una cadena que contiene el login para el que se desea realizar la comprobaci�n.
    * @param attempts Un valor entero que representa el n�mero de intentos fallidos necesarios para bloquear una cuenta.
    * @param timeout N�mero de minutos que se mantendr� la cuenta bloqueada.
    *
    * @return {@code false} si la cuenta est� habilitada o {@code true} si la cuenta se encuentra bloqueada (o en cualquier otro caso).
    *
    * @throws AuthenticationException
    */
   private boolean isLocked(String login, int attempts, int timeout) throws AuthenticationException
   {
      String sql;
      DataAgent conn = null;

      try
      {
         // Obtiene y abre la conexi�n a BBDD
         conn = DataFactory.getInstance(workspace);

         // Limpia bloqueos caducados (de m�s de [timeout] minutos)
         sql = "DELETE FROM " + TABLE_LOCKS + " " +
               "WHERE ((DATE_PART('day', CURRENT_TIMESTAMP - lastattempt) * 24 + " +
               "        DATE_PART('hour', CURRENT_TIMESTAMP - lastattempt)) * 60 + " +
               "        DATE_PART('minute', CURRENT_TIMESTAMP - lastattempt) >= " + timeout + ")";
         conn.execute(sql);

         // Consulta si el usuario dispone de un registro bloqueado:
         // Dispone de N intentos (o m�s) y el �ltimo intento hace menos de M minutos que se produjo
         sql = "SELECT Count(*) " +
               "FROM " + TABLE_LOCKS + " " +
               "WHERE ((DATE_PART('day', CURRENT_TIMESTAMP - lastattempt) * 24 + " +
               "        DATE_PART('hour', CURRENT_TIMESTAMP - lastattempt)) * 60 + " +
               "        DATE_PART('minute', CURRENT_TIMESTAMP - lastattempt) < " + timeout + ") And " +
               "        lower(login) = '" + DataAgent.sqlFormatTextValue(login) + "' And " +
               "        fails >= " + attempts;
         int nregs = conn.executeScalar(sql);

         return (nregs > 0);
      }
      catch (DataException ex)
      {
         throw new AuthenticationException(ex.getMessage(), ex);
      }
      catch (Exception ex)
      {
         throw new AuthenticationException(ex.getMessage(), ex);
      }
      finally
      {
         if (conn != null)
         {
            conn.disconnect();
         }
      }
   }

   /**
    * Registra un fallo de login, actualizando los datos de registro para evitar intentos de acceso mediante fuerza bruta.
    *
    * @param login Una cadena que contiene el login para el que se desea registrar el fallo de autenticaci�n.
    * 
    * @throws AuthenticationException
    */
   private void loginFail(String login) throws AuthenticationException
   {
      String sql;
      DataAgent conn = null;

      try
      {
         // Obtiene y abre la conexi�n a BBDD
         conn = DataFactory.getInstance(workspace);

         // Consulta si el login tiene un registro asociado
         sql = "SELECT Count(*) " +
               "FROM  " + TABLE_LOCKS + " " +
               "WHERE lower(login) = '" + DataAgent.sqlFormatTextValue(login) + "'";
         int nregs = conn.executeScalar(sql);

         if (nregs > 0)
         {
            sql = "UPDATE " + TABLE_LOCKS + " " +
                  "SET lastattempt = CURRENT_TIMESTAMP, fails = fails + 1 " +
                  "WHERE lower(login) = '" + DataAgent.sqlFormatTextValue(login) + "'";
            conn.execute(sql);
         }
         else if (nregs <= 0)
         {
            if (loginExist(login))
            {
               sql = "INSERT INTO " + TABLE_LOCKS + " (login, fails, lastattempt, ipaddress) " +
                     "VALUES ('" + DataAgent.sqlFormatTextValue(login) + "', 1, CURRENT_TIMESTAMP, '" + workspace.getServerRequest().getRemoteAddr() + "')";
               conn.execute(sql);
            }
         }

         // Confirma los cambios en la bbdd
         if (!conn.isAutoCommit()) conn.commit();
      }
      catch (DataException ex)
      {
         throw new AuthenticationException(ex.getMessage(), ex);
      }
      catch (Exception ex)
      {
         throw new AuthenticationException(ex.getMessage(), ex);
      }
      finally
      {
         if (conn != null)
         {
            conn.disconnect();
         }
      }
   }
}
TOP

Related Classes of com.cosmo.security.auth.impl.PostgreSqlAuthenticationImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.