Generando la autenticación de Joomla 1.5 , 1.6 desde otra aplicación en PHP.

El Joomla es un sistema manejador de contenidos(CMS por sus siglas en ingles) que últimamente  ha adoptado cierta tendencia a ser una especie de frameWork de desarrollo de aplicaciones Rich por Internet, sin embargo si deseas unir este CMS con algún otro desarrollo empotrado ahí es en donde se complican un poco las cosas(sólo un poco).

Uno de los problemas derivados es el sistema de autenticación cuando tenemos una BD compartida entre ambos sistemas.

Hace un par de días me enfrente con este problema(el de autenticación) y después de varias horas revisando el código del Joomla, logre plantear la solución que a continuación expongo.

Consideraciones:

Técnica y reglas a emplear:

Hay ciertas cosas que un desarrollador debe(y no debe) de hacer para no meterse en problemas o si a caso se llega a meter le puedan ayudar, en este caso son 3:

  • No modificar ninguna librería interna del Joomla (en todo caso hacer un componente).
  • En el caso de usar otro FrameWork(tal es el caso) de igual forma no modificar lo que no debes y seguir la documentación adecuada.
  • La técnica empleada fue estudiar el login del Joomla para implementarlo (en este caso al cakephp), en otras palabras el sistema de autenticación cakephp sera quien se adapte al Joomla(todas las modificaciones se hicieron sobre el cakephp).

Nota: El siguiente post  habla sobre el sistema de autenticación: de como los sistemas pueden compartir una misma BD de sus usuarios con sus mismos campos(user,password). El manejo de sesiones (posterior a la autenticación) sera tema que espero tocar proximamente.

Sistema de autenticación de Joomla 1.5 y 1.6

El sistema de login de Joomla en la actualidad es bastante complejo ( antes no lo era tanto, por ahí recuerdo algunos xploits con js ) y ya que ofrece distintos mecanismos de cifrados y demas opciones.
“De todos estos mecanismos sólo  vamos extraer el que estemos ocupando en nuestra aplicación Joomla”.
para esto estudiemos dichos mecanismos.

Lo que hace el Joomla cuando se autentifica un usuario en un sitio.

  • El usuario abre la pagina de Internet y por medio de modulo de login(JOOMLA/modules/mod_login) o por medio de la ruta de administracion (http://mi-joomla/administrator/) inserta su username y password.
  • Estos datos enviados en el arreglo $credencials y son procesados por la función onUserAuthenticate del archivo JOOMLA/plugins/authentication/joomla.php la cual nos sede la autenticación en caso de ser valida.

Eso es todo!, al parecer el generar la autenticación se resume al estudiar y replicar el comportamiento de la función onUserAuthenticate que aquí se muestra:

#función de Joomla definida en: JOOMLA/plugins/authentication/joomla.php

function onUserAuthenticate($credentials, $options, &$response)
	{
		jimport('joomla.user.helper');
		$response->type = 'Joomla';}
 #Validación/Sanidad de los datos
		if (empty($credentials['password'])) {
			$response->status = JAUTHENTICATE_STATUS_FAILURE;
			$response->error_message = JText::_('JGLOBAL_AUTH_EMPTY_PASS_NOT_ALLOWED');
			return false;
		}

		$conditions = '';

		#buscamos un usuario en la BASE de datos a partir de su 'username'
		$db		= JFactory::getDbo();
		$query	= $db->getQuery(true);

		$query->select('id, password');
		$query->from('#__users');
		$query->where('username=' . $db->Quote($credentials['username']));

		$db->setQuery($query);
		$result = $db->loadObject();
		if ($result) {
            		#Jooomla guarda el password en 2 parte la cadena encriptada y el secury.Salt ver Nota1
			$parts	= explode(':', $result->password);
			$crypt	= $parts[0];
			$salt	= @$parts[1];
            		#getCryptedPassword(cadena,salt) genera la cadena encriptada Revisar Nota2
			$testcrypt = JUserHelper::getCryptedPassword($credentials['password'], $salt);
			if ($crypt == $testcrypt) {
            		#login correcto
				$user = JUser::getInstance($result->id);
				$response->email = $user->email;
				$response->fullname = $user->name;
				$response->status = JAUTHENTICATE_STATUS_SUCCESS;
				$response->error_message = '';
			} else {
            		#contraseña erronea
				$response->status = JAUTHENTICATE_STATUS_FAILURE;
				$response->error_message = JText::_('JGLOBAL_AUTH_INVALID_PASS');
			}
		} else {
            		#El usuario no existe
			$response->status = JAUTHENTICATE_STATUS_FAILURE;
			$response->error_message = JText::_('JGLOBAL_AUTH_NO_USER');
		}
	}

Existen ciertas cosas que vale la pena comentar de la autenticación de Joomla, primero que

Nota1: Joomla guarda en el campo password 2 cadenas separadas por el simbolo ‘:’ de la forma primera_parte:segunda_parte la primera_parte es la contraseña cifrada(por algún mecanismo de cifrado irreversible) y la segunda parte es una cadena “security salt” la cual ocupa(en conjunto con el password sin cifrar) para generar la cadena cifrada.

Nota2: Ese proceso de generar la contraseña cifrada lo hace la función getCryptedPassword como sigue.

#función de joomla definida en: JOOMLA/libraries/joomla/user/helper.php 
public static function getCryptedPassword($plaintext, $salt = '', $encryption = 'md5-hex', $show_encrypt = false)
	{
		#notar que los parametros por defecto #$encryption = 'md5-hex' #$show_encrypt = false

		$salt = JUserHelper::getSalt($encryption, $salt, $plaintext);

		switch ($encryption)
		{
			case 'plain' :
				return $plaintext;
 #mas cassos.....
 #mas cassos.....
			case 'md5-hex' :
			default :
				#Esto es lo unico que se ejecuta.
				$encrypted = ($salt) ? md5($plaintext.$salt) : md5($plaintext); return ($show_encrypt) ? '{MD5}'.$encrypted : $encrypted;
                    }
}

Como se puede ver al llamar a la función getCryptedPassword sin los argumentos encryption, show_encrypt la única parte del código que se ejecuta del joomla es la que esta de color morado, que para este caso se puede apreciar que la configuración por defecto del Joomla ocupa MD5 como mecanismo de cifrado irreversible.

Generando nuestro sistema de login para joomla.

Una vez que estudiamos las entrañas del Joomla y comprendemos como funciona podemos generar este comportamiento de autenticación, para hacerlo mas genérico y se pueda implementar dejo un código en PHP(versión 4 ) puro:

La versión de autenticación con php puro(M4ch0-c0d3r) la puedes descargar desde: http://gist.github.com/575849

#Requere que se configure la ruta del joomla para extraer la configuracion de la BD $joomla_path = './joomla1.6';

require_once($joomla_path.'/'.'configuration.php');

function joomla_login($username, $pass){
	$jconf = new JConfig;

	$conexion = @mysql_connect($jconf->host,$jconf->user,$jconf->password,false,0);
	@mysql_select_db($jconf->db);
	$sql = sprintf("SELECT * FROM %susers  WHERE  %susers.username='%s' LIMIT 1;",
				$jconf->dbprefix,$jconf->dbprefix, $username);
	$request = @mysql_query($sql);

	if (mysql_num_rows($request) == 1) {
		$user = array('User' => mysql_fetch_assoc($request));
		print_r($user);
		$parts	= explode( ':', $user['User']['password'] );
		$crypt	= $parts[0];
		$salt	= @$parts[1];
		$testcrypt = md5($pass.$salt);
		if ($crypt == $testcrypt) {
			echo "login correct!\n";
		}
	}
	if($request)
		mysql_free_result($request);

}

#probamos la función con un usuario que conozcamos 
joomla_login('admin','libertad')

implementación en cakephp

Una vez que se entiende el funcionamiento del login del Joomla quizás implementarlo en algún framework sea lo mas fácil, dejo aqui la implementa.

Definiendo la BD.

Se edito el archivo de la Base de datos del cakePHP para que reconociera de forma trasparentes las tablas se le tuvo que agregar el atrubuto prefix.

#CAKEPHP/APP/config/database.php
class DATABASE_CONFIG {

	var $default = array(
		'driver' => 'mysql',
		'persistent' => false,
		'host' => 'localhost',
		'login' => 'root',
		'password' => '',
		'database' => 'sidep',
		'prefix' => 'jos_', #Se le agrego el prefijo _jos
	);
}

Agregando el elemento de autenticación para la aplicación.

Se le agrego el componente de Auth al controlador de la aplicación.

#CAKEPHP/APP/app_controller.php
class AppController extends Controller {
	var $components = array('Auth');
}

Creando el modelo Users.

Se creo el modelo de usuario como se muestra a continuación.

#CAKEPHP/APP/models/user.php
class User extends AppModel {
	var $name = 'User';
	var $displayField = 'name';
	var $validate = array();
	#aqui van las relaciones(belonsTo,hasMany,etc..) con otras tablas, agregar las necesarias para su aplicacion.
}

Vista login:

Se le agrego la vista del login la cual es solo el formulario de acceso.

#CAKEPHP/APP/views/users/login.ctp
	if ($session->check('Message.flash')) {
		$session->flash();
	}
	if ($session->check('Message.auth')) {
		$session->flash('auth');
	}
    echo $session->flash('auth');
    echo $form->create('User', array('action' => 'login'));
    echo $form->input('username');
    echo $form->input('pass', array('type'=>'password'));
    echo $form->end('Login');

Controlador Users definiendo la función login.

Aquí es donde va la lógica que tanto estuvimos estudiando previamente, la cual se basa del código en PHP definido  previamente.

#CAKEPHP/APP/controllers/users.php
function login(){
	$user = null;
	#buscamos un usuario en la BASE de datos a partir de su 'username'
	if(!empty($this->data) && strlen($this->data['User']['pass'])>6)
		$user = $this->User->findAllByUsername($this->data['User']['username']);
		#verificamos que el usuario exista
		if(count($user) == 1) {
			$user = $user[0];
			#hacemos el login del joomla #dividiendo el password en la BD en 2 partes a partir del simbolo ':'
			$parts	= explode( ':', $user['User']['password'] );
			#la 1ra parte es el password ya encriptado
			$crypt	= $parts[0];
			#la 2da la cadena salt q se concatena con el password para ecriptar
			$salt	= @$parts[1];
			#Joomla ocupa MD5 como mecanismo de encriptacion y cakephp SHA1
			$passcrypt = md5($this->data['User']['pass'].$salt);
			#comparamos el password encriptado con el generado.
			if ($crypt == $passcrypt) {
				#CONGRATULATION! ';¬D,
				$this->data['User']['password'] = $passcrypt.':'.$salt;
				$this->Auth->login($this->data);
				$this->redirect('index');
			}
	}
}

espero les sea útil

Anuncios

33 thoughts on “Generando la autenticación de Joomla 1.5 , 1.6 desde otra aplicación en PHP.

  1. Pingback: Twitted by HuevoM

  2. Pingback: Como Joomla genera sus passwords « Fitorec – Pensamientos Libres!

  3. Gracias por el aporte lo estoy probando pero quisiera saber en la ruta de joomla que se requiere al comienzo se pone solo la ruta del directorio Joomla o la ruta del archivo Joomla.php?.. Gracias

    • No se a que te refieres exactamente. Cuando hago mensión de los archivos lo hago de forma relativa en función de donde se encuentre tu sistema joomla p.e. cuando menciono:

      JOOMLA/plugins/authentication/joomla.php

      la palabra JOOMLA hace referencia a la ruta para acceder a tu sistema joomla desde tu sistema de archivos p.e. una ruta tipica en Linux seria /var/www/joomla o bien en Windows c://apache2/public_html/.

      No se si a eso te refieras, sin mas espero haber resuelto tu duda, si no puedes dejar otro comentario.

  4. Me parece de bastante ayuda esta explicación, me he puesto analizar muy poco la librería de joomla y actualmente estoy aprendiendo a usar Zend.

    Saludos!!!!

  5. Increíble, menudo trabajazo, pero hay una cosa que no me queda muy claro, las cookies de sesión … estoy trabajando con Jomsocial y el registro esta dominado pero lo de las cookies lo llevo fatal, me puedes ayudar?

    • Hasta ahorita ¿Que llevas haciendo?, Revisa tu archivo:

      ./libraries/joomla/session/session.php

      Por ahi de la linea 400(depende de la version de joomla) trae la funcion _start, revisa como es que la crea, seguramente ahí encontraras la solución de lo que deseas.

      Desgraciadamente por le momento no tengo tiempo, pero si tienes algo de código fuente con gusto lo podría revisar. saludos.

  6. Saludos, primeramente muy agradecido por tal trabajo, estoy implementando tu codigo, tal cual lo escribiste en este blog con ese ejemplo y usando la versión de cakephp1.3.3 pero al llamar desde el navegador a la aplicacion me sale el siguiente error:
    #CAKEPHP/APP/app_controller.php class AppController extends Controller { var $components = array(‘Auth’); } Fatal error: Class ‘AppController’ not found in /var/www/applista/cake/libs/controller/pages_controller.php on line 32

    Por favor si puedes ayudarme.

      • Saludos de nuevo estimado, veo la documentación a la que me haces referencia y según el ejemplo debo colocar var $components = array(‘Math’, ‘Session’); pero no se donde, o en su defecto colocar var $components = array(‘Auth’); pero donde debo colocarlo?

        Vi tres archivos:
        applista/cake/libs/controller/app_cotrollers.php
        applista/cake/libs/controller/pages_cotrollers.php
        applista/app/app_cotrollers.php

        A parte de eso le coloco “<?php" en la primero línea de applista/app/app_cotrollers.php y al utilizar el navegado en la dirección del proyecto me dice Not Found The requested URL /applista/users/login was not found on this server.

      • De los archivos que me mencionas: Los archivos de la librerías de cake No los debes de tocar.


        - applista/cake/libs/controller/app_cotrollers.php
        - applista/cake/libs/controller/pages_cotrollers.php

        Típicamente el mejor lugar donde poner el componente seria en el controlador de tu aplicación, asi los demas controladores van a heredar estos componentes.

        applista/app/app_cotrollers.php <--controlador de la aplicacion.

        Por otra parte también lo podrías mandar llamar en algunos controladores especificos p.e.:

        applista/app/controllers/ users_controller.php

        Básicamente de que depende de los controladores que vallas a utilizar utilizan dicho componente de ser lo mejor es ponerlo en applista/app/app_cotrollers.php.

  7. Hola! he probado el ejemplo tal como lo dice, pero no logro hacer login, creo que el problema esta en al encritacion de la clave, actualemtne lo estoy probando en j1.6

  8. Hola amigo.
    excelente tutorial. Un favor especial si me podrias ayudar,
    veras me ejecuta todo el joomla_login(al cargar la pagina me visualiza los datos con el mensaje de login correcto!), pero quisiera saber como hacer para que al ser incorrecta la contraseña me muestre otro archivo .php
    La idea es, estoy en mi ventana principal de joomla. coloco un boton en el backend(Panel de Control) hay llamo a una ventana emergente… y es hay es donde llamo a un archivo de reportes http://localhost/joomla/reportes/index.php; sin un logueo cualkiera podria entrar a los reportes es por ese motivo que kisiera saber como puedo incluir tu codigo para verificar si se a logueado desde el administrador ingrese directo a reportes caso contrario ingrese al logueo de joomla.
    agredeceria mucho tu ayuda..
    saludos!

    • Veras no es tan complejo revisa la funcion joomla_login, solo es cuestion de modificarla,p.e.


      function joomla_login($username, $pass){
      $jconf = new JConfig;

      $conexion = @mysql_connect($jconf->host,$jconf->user,$jconf->password,false,0);
      @mysql_select_db($jconf->db);
      $sql = sprintf("SELECT * FROM %susers WHERE %susers.username='%s' LIMIT 1;",
      $jconf->dbprefix,$jconf->dbprefix, $username);
      $request = @mysql_query($sql);

      if (mysql_num_rows($request) == 1) {
      $user = array('User' => mysql_fetch_assoc($request));
      print_r($user);
      $parts = explode( ':', $user['User']['password'] );
      $crypt = $parts[0];
      $salt = @$parts[1];
      $testcrypt = md5($pass.$salt);
      if ($crypt == $testcrypt) {
      echo "login correct!\n";
      }else{
      include('reportes.php');
      }
      }
      if($request)
      mysql_free_result($request);
      }

      Suerte!

      • hola, antes q nada agradecer de manera infinita q me allas contestado.
        gracias por solucionar mi inquietud.
        otra inquietud… es q como usar tu funcion en un formulario de ingreso. Colocando en user y el paswword mediante dos cajas de textos.
        espero no sea muy ostigozo de mi parte… pero me seriviria de mucho tu ayuda.

        gracias, Saludos!

  9. excelente aporte, me sirvió muchisimo, estoy haciendo una aplicacion con joomla q los usuarios me los facilitan desde otra base de datos en SQL Server 2008, y perdí muchas horas analizando la forma de encriptación de joomla, pero con eso se me facilita muchisimo escelente

  10. Hola, no tengo idea de Joomla y necesito exportar los usuarios de mi aplicacion (php y mysql) hacia la tabla de usuarios de Joomla, sé que si estudio con detenimiento tu post lo lograré pero también se que tú ya tienes la respuesta, básicamente necesito saber cómo hago la encripción del password para que quede bien almacenado en joomla. muchisimas gracias por tu colabración

  11. Hola estimado, excelente tutorial.
    Mi situación es la siguiente: Tengo Joomla 1.5 como intranet, y tengo mi aplicación externa, la cual estoy agregando la funcionalidad de control de horario. Quiero que los usuarios digiten su contraseña de Joomla para registrar sus ingresos y salidas. ¿Qué tendría que modificar de la función joomla_login para que solo me tome solo la contraseña? ¿Hay un componente que verifique si hay contraseñas repetidas?.

    Un saludo cordial.

    • Joomla! cifra todas las contraseñas por un mecanismo no-reversible (md5 por defecto), de tal forma que no podemos asegurar si existe alguna contraseña repetida, lo que si se checa es que no exista un par cadena cifrada con la cadena salt(el grano de sal utilizado para cifrar), en este post explico a mas detalle el mecanismo:

      https://fitorec.wordpress.com/2010/09/25/como-joomla-genera-sus-passwords/

      Ahora respecto a la función de login que pongo esta hecha pesada en ser utilizada en cakePHP, pero puedes seguir tal cual la lógica e implementarla en tu framework, Blog, CMS ó tu sistema favorito.

  12. Saludos compañero, excelente aplicación, la he puesto en Cakephp 2.1 y funciona perfectamente en mi localhost con el core de php 5.3.6, sin embargo en mi hosting no funciona correctamente me da el error de Notice (8): Undefined index: User [APP/Controller/UsersController.php, line 14], le he dado mil y un vueltas y no encuentro como hacerlo funcionar en el hosting.

    La aplicación de mi localhost es identica a la del hosting, si me puedes dar una mano te lo agradecería.

    • @kuwox el mensaje:

      (8): Undefined index: User [APP/Controller/UsersController.php, line 14]

      PHP nos esta indicando que el archivo APP/Controller/UsersController.php en la linea 14 esta intentando acceder en un arreglo hacia un indice 'User' El cual no esta disponible, seguramente tienes algo similar a:


      $this->data['User']

      Revisa el contenido de tu arreglo, puedes ocupar el print_r o el var_dump:


      print_r( $this->data['User']);
      var_dump( $this->data['User']);

      Suerte 😈!

      • Saludos amigo, gracias por tu respuesta, anexo el cófigo HTML que genera la aplicación con el error:

        $usuario = array(
        (int) 0 => array(
        'password' => '*****',
        'id' => '71',
        'name' => 'Reauca',
        'username' => 'reauca',
        'email' => 'reauca@hotmail.com',
        'usertype' => 'Registered',
        'block' => '0',
        'sendEmail' => '0',
        'gid' => '18',
        'registerDate' => '2011-11-14 19:40:14',
        'lastvisitDate' => '2011-11-25 13:54:14',
        'activation' => '',
        'params' => 'admin_language=

        Aun no he probado con lo que me comentaste, voy a hacer al rato, sin embargo me dió curiosidad ver que dentro del array $usuario está el array pero (int), no entiendo mucho, de todos modos te anexo el código, voy a probar lo que me comentaste y te aviso.

        No se si es de ayuda este código pero creo que es clave.

        Gracias de antemano.

      • Saludos amigo, fijate algo en este arreglo:

        Array
        (
        [0] => Array
        (
        [0] => Array
        (
        [id] => 71
        [name] => Reauca
        [username] => reauca
        [email] => reauca@hotmail.com
        [password] => c11862c63e522d3c67268a9bad72821d:kG8P2ZaP6r5lJBWD8OY17JgKeoUgrGDR
        [usertype] => Registered
        [block] => 0
        [sendEmail] => 0
        [gid] => 18
        [registerDate] => 2011-11-14 19:40:14
        [lastvisitDate] => 2011-11-25 13:54:14
        [activation] =>
        [params] => admin_language=
        language=
        editor=
        helpsite=
        timezone=0

        )

        )

        )

        El valor de los dos array son [0], sin embargo el segundo debería ser [User], claro en mi localhost el nombre que le coloca al segundo array es [User], pero en el hosting lo coloca tal cual coloque arriba, el segundo arreglo tambien lo pone [0].

        No se porque surgen estos cambios si el contenido de mi UserController.php es el mismo de mi localhost.

        Sin embargo hasta ahora he tenido que colocar en el UserController.php de mi hosting $parts = explode( ‘:’, $usuario[0][‘password’] ); para que funcione.

        ¿Como podria yo decirle al segundo array deberia de llamarse [User]?

        Gracias de antemano.

      • Al parecer al usuario ‘reauca’ lo agregaste desde tu aplicación en cakephp, Aqui debes de tener cuidado ya que la contraseña no se debe guardar en texto plano (“123456789”) revisa como joomla genera/almacena sus contraseñas ( https://fitorec.wordpress.com/2010/09/25/como-joomla-genera-sus-passwords/ ) y guardara de igual manera ó quizás lo recomendable sea dejar solamente habilitado el registro desde tu Joomla y desde cake solo atrapar la autenticación.

        Suerte.

  13. Hice lo que me comentaste a ver que código lanza el $this->data[‘User’], usé el código print_r( $this->data[‘User’]); bajo la línea:

    $usuario = null;
    #buscamos un usuario en la BASE de datos a partir de su ‘username’
    if ($this->request->is(‘post’)) {
    if ($this->User->validates(array(‘fieldList’ => array(‘username’, ‘password’)))) {
    if(isset($this->data) && strlen($this->data[‘User’][‘password’])>6)
    $usuario = $this->User->findAllByUsername($this->data[‘User’][‘username’]);
    #verificamos que el usuario exista
    if(count($usuario) == 1) {
    print_r( $this->data[‘User’]);

    Y dice:
    Array
    (
    [username] => reauca
    [password] => 123456789
    )

    Ahora bien, no entiendo mucho que debo hacer, ando documentandome, no se si debo cambiar todos los User por username, o de que manera llamar $parts = explode( ‘:’, $usuario[‘User’][‘password’] );

    Gracias de antemano.

  14. Buenas Tardes… Excelente publicación. No tengo mucha idea de el manejo de joomla y menos de php, apenas estoy aprendiendo. Resulta que tengo que hacer login por código, y creo que eso ya está (con el código que usted puso). Pero lo que tengo que hacer es comunicarme con un componente de joomla y mostrar ciertos datos de el módulo, para hacer esto debo tener sesión, o eso es lo que entiendo con el “defined( ‘_JEXEC’ ) or die( ‘Restricted access’ )”. No sé cómo hacer para iniciar sesión y poder traer esos datos. ¿Podría ayudarme?

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s