Limpiado de Sesiones en Symfony 1.4

Ayer encontre un “bug” (por llamarlo de una manera) en el manejo de sesiones de Symfony 1.4 usando sfGuard, me dí cuenta que cuando inicias una session con el usuario A, guardas un dato en la session del usuario A, deslogeas al usuario A, logueas a un usuario B, en los datos de la session de B estan los datos guardados para el usuario A. Obviamente se pueden dar cuenta de los problemas que puede llegar a traer esto.

Para probar puse en fragmento de código que setea una variable de session con el timestamp, una vez seteada la variable nunca mas se cambia el valor. Se supone que si me logeo la variable se setea por primera vez, al deslogearme la variable debería eliminarse.

Aqui un par de screenshots the la barra de debug de symfony, la primera es antes de logear al user, aqui pueden ver que la session esta limpia.

la segunda es con el el usuario logeado mostrando el valor de session, la variable fue seteada.

y la última es con el usuario deslogeado nuevamente pero con el valor de session ahi.

Si vuelvo a entrar con ese usuario u otro usuario la variable nunca cambia el valor.

El problema sucede tanto si usas setAttribute del Objeto de usuario o si metes el valor directamente en $_SESSION.

Una solución rápida que encontré fue colgarme del evento de user auth change para ver si el usuario se esta deslogeando, en ese caso llamo a un session destroy para limpiar todos los valores de la session.

Como sería esto, bueno, primero, cree una clase para el método que quiero hookear, llamemosla swSessionReset y pongamosla en lib. La clase sería algo así

class swSessionReset {
 
  static public function resetSession($event) {
    if ($event['authenticated'] === false) {
      session_destroy();
    }
  }
 
}

Después tenemos que hookear el método de esta clase. Para eso vamos al ApplicationConfiguration y añadimos estas lineas al método configure.

class applicationNameConfiguration extends sfApplicationConfiguration {
 
  public function configure() {
      //Some Code ....
      $dispatcher = ProjectConfiguration::getActive()->getEventDispatcher();
      $dispatcher->connect('user.change_authentication', 
          array('swSessionReset', 'resetSession')
      );
  }
 
}

Con esto, cada vez que el usuario se deslogue forzaremos la limpieza de todos los valores de session.

Después de ver esto @gerzenstl me hizo notar que Drupal al deslogear un usuario invoca al session_destroy, aca esta el código para que los desconfiados revisen

http://api.drupal.org/api/drupal/modules!user!user.pages.inc/function/user_logout/6

Conclusión, “ojo al parche torrente!”, tengan cuidado con esta consideración por que puede traerles graves problemas de seguridad. Prueben si a ustedes les pasa lo mismo, puede ser que el sitio en el que estoy trabajando actualmente tenga algo modificado que rompe esta funcionalidad (IMHO no creo), después me cuentan como les fue.

Share

sfGuard con LDAP

Ayer me vi en la necesidad de pasar el control de usuarios de un sistema que utiliza el sfGuardPlugin por una base Ldap (Active Directory). Para eso me puse a investigar un poco como hacerlo y ví que el famoso plugin provee de un mecanismo para enganchar una función personalizada para el chequeo del password, dicha función personalizada es la que debe realizar la autenticación del username/password contra el controlador de Dominio; si esta devuelve verdadero significa que el usuario fue validad correctamente, si devuelve falso quiere decir que no. Una consideración a tener en cuenta es que el usuario debe existir dentro de la tabla sf_guard_user por que primero verifica la existencia del username y la autenticación la realiza contra el Controlador de Dominio.

Para poder empezar necesitamos una cuenta válida para bindearnos al arbol LDAP y poder hacer una busqueda por el username (pidanselo a su “amigo” sysadmin). De mas esta decir que debemos tener un Controlador de Dominio funcionando. Tambien deberán tener las extensiones de ldap para PHP, si estan en un debian/ubuntu instalen el paquete php5-ldap.

Paso siguiente es cargar en la base de datos del sistema local todos los nombres de usuario que podrán usar nuestro sistema y que estan en el arbol LDAP, consideren que si quisieran que cualquier usuario definido en el ldap pueda loguearse puedan sincronizar la información o dar de alta al usuario en la base local automáticamente si este no existe la primera vez que intenta loguearse.

El tercer paso es crear una Clase dentro de lib (para que la tome el autoloader de symfony) que posea un método estático llamado checkPassword el cual deberá recibir como argumentos el username y el passoword.  Esto debería quearles algo asi

NOTA: Corregir en el ejemplo los Distinguished Names por los que correspondan en tu arbol ldap.

El paso final es configurar dentro de la configuración de tu aplicación (donde usar el sfGuardAuth) editar el archivo app.yml y especificar que vas a usar la clase LdapLogin y el método checkPassword.

all:
  sf_guard_plugin:
    check_password_callable: [LdapLogin, checkPassword]

Con esto el control de validación user/pass debería ir al controlador de dominio integrando asi tus aplicaciones.

Mas info en http://www.symfony-project.org/plugins/sfGuardPlugin

Share

Embebed Forms en Symfony, una forma fácil

Los formularios embebidos de symfony son una características muy buena del framework, el problema es que utilizarlos es bastante complicado y existe poca documentación oficial al respecto.

Toda esta duda me surgió debido a este video de Francois donde vi un nuevo método de Propel 1.5 para facilitar el proceso de embeber fomularios. El tema es que en varios proyectos que estoy llevando a cabo no tengo pensado migrar a nuevas versiones de symfony en un corto plazo, por eso quise facilitarme la vida implementando algo parecido. Vean el video para ver de lo que les hablo.

mergeRelation from Francois Zaninotto on Vimeo.

He trabajado un poco en el tema y realice una pequeña implementación para facilitar el trabajo parecida a la que se ve en el video, aquí la explicación.

El modelo

Todo parte de la suposición que tenemos una tabla 1 (tbl1) y otra tabla 2 (tbl2) que posee una relación con la primera, a travez del campo tbl1_id, deberemos respetar esta notación siempre para utilizar esta utilidad, la relación debe definirse a travez del campo tabla padre nombre seguido por _id, por ejemplo xxx_id.

Aquí un schema de ejemplo.

Generamos todo, modelo, forms, filters, sql

./symfony propel:build-all

Luego cargar las nuevas tablas a la base. Si no hay datos

./symfony propel:insert-sql

Modificaciones en las clases bases

Ahora, el siguiente paso es modificar la clase base de los forms para contar con los métodos reutilizables a fin de embeber formularios. Personalmente pienso que el mejor lugar es lib/forms/BaseFormPropel.class.php, si conocen un lugar mas apropiado para ubicarlos acepto la recomendación. Los métodos a añadir son tres.

loadAsEmbebedForm : Que añade la relación como un formulario embebido, se utilizará en el método configure de la clase Form del padre.

bindObjectToEmbebed : Al guardar, en caso de realizarse cambios, asocia el objeto padre a los objetos hijos de los formularios embebidos, se llama por bind.

is_seted_value : bindObjectToEmbebed lo utiliza para saber si se produjeron cambios en el formulario embebido.

bind : Redefinimos el metodo para llamar a bindObjectToEmbebed antes de realizar el bind de la clase base. El único que utilizaremos nosotros será loadAsEmbebedForm, los otros se invocarán automaticamente cuando corresponda. Debería quedarles algo así

El widget sfWidgetFormInputAjaxDelete

Luego será necesario que incorporen el widget sfWidgetFormInputAjaxDelete.class.php (lib/widget/) , este creará una entrada de texto con un boton X el cual al ser cliqueado invocará a la accion del modulo que especifiquen para borrar el elemento. Esta acciòn deberá ser implementada por ustedes en el modulo que les parezca.

Deberán tener instalado ysJQueryRevolutionsPlugin para que este widget funcione

http://www.symfony-project.org/plugins/ysJQueryRevolutionsPlugin

Configurar el Formulario Embebido

Con esto tenemos configurada nuestra instalación para embeber todos los formularios que querramos. Veamos como sería para embeber en Tbl1 el formulario Tbl2, lo único que deberemos hacer es configurar el form del modelo Tbl1 (tabla padre) para contener a Tbl2 (tabla hija). Para esto vamos a modificar el método configure utilizando el método recientemente añadido a la clase BaseFormPropel, loadAsEmbebedForm.

class Tbl1Form extends BaseTbl1Form {
public function configure() {
$this->loadAsEmbebedForm(‘tbl2’, ‘thetext’, ‘demo/deleteTbl2’);
}
}

Aquí deberán pasar como argumentos al método el nombre del modelo hijo, el método para leer la propiedad que se utilizará como etiqueta del formulario embebido y la URI para la acción que borrará el formulario embebido.
Al terminar deberían probar con alguno módulo que utilice el form y les saldría una cosa parecida a esta.sshot-1

Eso es todo amigos, les parece sencillo ?

Share

Comenzar un proyecto Symfony 1.2 desde un Sandbox

Esta es una pequeña guía de como encarar un proyecto nuevo con symfony 1.2 desde un sandbox, sobre un sistema Debian, con Apache 2, MySQL corriendo localmente y que utilizaremos como ORM Propel.

Por mas que symfony 1.2 esta viejo para el equipo de Symfony dado que ya no lo tienen para descargar en si lista de downloads, decidí realizar esta guía por que todavía hay gente que lo usa y no piensa migrarlo por el momento. Si estas por empezar desde cero, cero, cero y quieres saber symfony te recomiendo que veas instalar la versión 1.4, esta guía no debería diferenciarse en mucho para esa versión.

Para este ejemplo suponemos que el proyecto se llama mysandbox.com, reemplazar por el nombre que les parezca. Asumimos que será un entorno de prueba y obviamos las medidas de seguridad para hacer más sencillo el ejemplo.

Empezamos

1 – Descargar el Sandbox http://www.symfony-project.org/get/sf_sandbox_1_2.tgz

2 – Copiarlo descomprimido a un directorio para configurar el Vhost de Apache P.Ejemplo : /var/www/mysandbox.com

3 – Configurar el Vhost de Apache para nuestro proyecto, en el caso de debian añadir la siguiente definición de vhost en un archivo dentro de la carpeta /etc/apache2/sites-enabled.

<VirtualHost *:80>
ServerName mysandbox.com
DocumentRoot “/var/www/mysandbox.com/web”
DirectoryIndex index.php
<Directory “var/www/mysandbox.com/web”>
AllowOverride All
Allow from All
</Directory>
</VirtualHost>

Reemplazar los mysandbox.com por el nombre de su directorio y nombre de dominio.

4 – Añadir el nombre dominio de proyecto en su tabla de hosts. En caso de tener el DNS apuntando a su server para el Vhost saltear este paso. En caso de GNU/Linux la tabla de hosts se encuentra en /etc/hosts, deberán añadir una linea como la siguiente.

127.0.0.1  mysandobx.com

Reemplazar mysandbox.com por el nombre de su dominio, el que debe ser igual a la definición de la directiva ServerName en la definición del Vhost.

5 – Reiniciar Apache

/etc/init.d/apache2 reload

6 – Setear los permisos de los archivos del vhost adecuadamente, para salir rápido del paso (SOLO EN DESARROLLO!)

chmod 777 /var/www/plughelp.com -R

NOTA: Para una asignación de permisos segura en un entorno de producción les recomiendo que vean http://articles.slicehost.com/2007/9/18/apache-virtual-hosts-permissions.

En este paso debería tener el sandbox casi listo, si entra en su navegador a la direccion del dominio debería ver la pagina por defecto del proyecto.

http://mysandbox.com

7 – Levantar la base en mysql

#mysql -u root -p #Luego ingresar la password

mysql>create database mysandbox_com;

8 – Configurar el acceso del sandbox a mysql. Para esto debe modificarse el archivo databases.yml y propel.ini

Database.yml : Recuerden que el YML lleva una identación especifica, en esta pagina puede ser altera la identación por ende no copien y peguen esto por que seguramente no les va a funcionar adecuadamente. Verfiquen y cambien los valores del YML que viene por defecto.

dev:
propel:
param:
classname: DebugPDO
test:
propel:
param:
classname: DebugPDO
all:
propel:
class: sfPropelDatabase
param:
classname: PropelPDO
dsn: ‘mysql://host=127.0.0.1;dbname=mysandbox_com’
username: root
password: tupass
encoding: utf8
persistent: true
pooling: true

Propel.ini

propel.targetPackage       = lib.model
propel.packageObjectModel  = true
propel.project             = mysandbox.com
propel.database            = mysql
propel.database.driver     = mysql
propel.database.url        = mysql://dbname=mysandbox_com;host=127.0.0.1
propel.database.creole.url = ${propel.database.url}
propel.database.user       = root
propel.database.password   = tupass

; mysql options
propel.mysql.tableType     = InnoDB

propel.addVendorInfo       = true
propel.addGenericAccessors = true
propel.addGenericMutators  = true
propel.addTimeStamp        = true
propel.addValidators       = false

propel.useDateTimeClass       = true
propel.defaultTimeStampFormat = d-m-Y H:i:s
propel.defaultTimeFormat      = H:i:s
propel.defaultDateFormat      = d-m-Y

propel.schema.validate        = false
propel.samePhpName            = false
propel.disableIdentifierQuoting     = false
propel.emulateForeignKeyConstraints = true

; directories
propel.home                    = .
propel.output.dir              = /var/www/production/sfweb/www/cache/symfony-for-release/1.2.10/sf_sandbox
propel.schema.dir              = ${propel.output.dir}/config
propel.conf.dir                = ${propel.output.dir}/config
propel.phpconf.dir             = ${propel.output.dir}/config
propel.sql.dir                 = ${propel.output.dir}/data/sql
propel.runtime.conf.file       = runtime-conf.xml
propel.php.dir                 = ${propel.output.dir}
propel.default.schema.basename = schema
propel.datadump.mapper.from    = *schema.xml
propel.datadump.mapper.to      = *data.xml

; builder settings
propel.builder.peer.class              = plugins.sfPropelPlugin.lib.builder.SfPeerBuilder
propel.builder.object.class            = plugins.sfPropelPlugin.lib.builder.SfObjectBuilder
propel.builder.objectstub.class        = plugins.sfPropelPlugin.lib.builder.SfExtensionObjectBuilder
propel.builder.peerstub.class          = plugins.sfPropelPlugin.lib.builder.SfExtensionPeerBuilder
propel.builder.objectmultiextend.class = plugins.sfPropelPlugin.lib.builder.SfMultiExtendObjectBuilder
propel.builder.mapbuilder.class        = plugins.sfPropelPlugin.lib.builder.SfMapBuilderBuilder

propel.builder.addIncludes  = false
propel.builder.addComments  = true
propel.builder.addBehaviors = true

9 – Con eso deberías tener listo tu sandbox para comenzar a programar. El metacomando de symfony puede usarse desde el root del proyecto con ./symfony

10 – Como Seguir, algunas de las cosas a saber. Puedes seguir esta guía desde el capítulo 4 del libro para continuar aprendiendo

http://www.symfony-project.org/book/1_2/04-The-Basics-of-Page-Creation

Otra alternativa es ver esta pequeña introducción

http://www.symfony-project.org/tutorial/1_2/en/my-first-project

Cosas que deberías dominar

– Definir el esquema

– Generar

– build-model

– build-forms

– build-filters

– build-sql

– build-schema

– data-load

– insert-sql

– Crear Aplicaciones

– Crear Modulos

– Limpiar la cache ./symfony cc

A symfonear

Les dejo este post en texto plano por si quieren ver los ejemplos tal cual como son sin problemas de identación Descarga

Share

Soporte para Symfony

Mucha gente suele enviarme correos para que los ayude con sus desarrollos con Symfony, mirando mis readers de hoy me encontré con este sitio:

http://www.symfonyexperts.com

Un foro donde uno puedo hacer preguntas sobre symfony poniendo una ganancia por la pregunta realizada, una vez que se responda el que pregunta debe pagar por su respuesta y el que respondió la pregunta se llevará parte de la recompensa.

Los invito a todos a participar, a ver si así recupero algo de mi tiempo invertido en responder preguntas.

Share