miércoles, 3 de mayo de 2006

Aventuras en un Framework de Seguridad en .NET (2)

En esta ocasión voy a analizar algunos elementos que se requieren para satisfacer los puntos 1 y 2 de mis requerimientos planteados anteriormente.

Recordemos brevemente:

1. Que los programadores puedan implementar la seguridad sin programar.
2. Que podamos hacer visibles/invisibles o habilitados/deshabilitados elementos de la IU de manera totalmente declarativa, ya sea en una BD o en XML.

Para satisfacer el punto 1, podemos desarrollar un control gráfico, que simplemente se arrastre con drag and drop a cada forma del proyecto en la que se desea tener seguridad (aquí estamos hablando de Windows Forms).

Mi primera intención fue crear un componente no gráfico (heredando de System.Windows.Forms.Component) ya que este implementador de seguridad no requiere de una interfaz gráfica para si mismo. Sin embargo, me enfrenté a la dificultad de que un componente de este tipo no puede determinar en tiempo de ejecución a que forma pertenece. Existen algunos trucos para determinar esto en tiempo de diseño pero no me gustaron ya que quedaría una solución frágil ante renombramientos y cambios de ese tipo.

Por lo tanto opté por desarrollar un control (heredando de System.Windows.Forms.UserControl) que se vuelve invisible en tiempo de ejecución. No tan elegante como quisiera pero funcional. Se parece al viejo timer de VB6, ¿se acuerdan?

Para que este control pueda encargarse de configurar la interfaz en tiempo de ejecución, necesita analizar al correr todos los elementos gráficos de la forma e ir encendiendo/apagando/habilitando/deshabilitando los elementos de la IU conforme a las reglas de seguridad declaradas. Sin embargo, no es suficiente con hacer esto al cargar la forma. ¿Qué pasa si el código del programador enciende o habilita programáticamente un control que no debería para el usuario firmado? Por esta razón es necesario también establecer manejadores de los eventos OnEnabledChanged y OnVisibleChanged de los controles bloqueados para poder levantar una excepción de seguridad en caso necesario.

El código que itera sobre los controles se ve más o menos así:

private void AuthorizeControls( Control main)
{
IPrincipal iprin = Thread.CurrentPrincipal;
Behavior b;
bool bEvaluated = false;
foreach (Control c in main.Controls)
{


if (c.Controls.Count > 0)
AuthorizeControls(c);


if (!Authorize(iprin, c.Name, out b, out bEvaluated))
{
switch (b)
{
case Behavior.Enabled:
c.Enabled = false;
c.EnabledChanged += new EventHandler(c_EnabledChanged);
break;
case Behavior.Visible:
c.Visible = false;
c.VisibleChanged += new EventHandler(c_VisibleChanged);
break;
case Behavior.Both:
c.Enabled = false;
c.EnabledChanged += new EventHandler(c_EnabledChanged);
c.Visible = false;
c.VisibleChanged += new EventHandler(c_VisibleChanged);
break;
}
}
else
{
if (bEvaluated)
{
switch (b)
{
case Behavior.Enabled:
c.Enabled = true;
c.EnabledChanged -= new EventHandler(c_EnabledChanged);
break;
case Behavior.Visible:
c.Visible = true;
c.VisibleChanged -= new EventHandler(c_VisibleChanged);
break;
case Behavior.Both:
c.Enabled = true;
c.EnabledChanged -= new EventHandler(c_EnabledChanged);
c.Visible = true;
c.VisibleChanged -= new EventHandler(c_VisibleChanged);
break;
}
}
}
}
}

void c_VisibleChanged(object sender, EventArgs e)
{
throw new System.Security.SecurityException
(m_strUISecurityException);
}

void c_EnabledChanged(object sender, EventArgs e)
{
throw new System.Security.SecurityException
(m_strUISecurityException);
}

Este código está un poco simplificado. Para completarlo hay que contemplar la posibilidad de manejar controles que viven dentro de objetos ToolStrip. Desafortunadamente estos contenedores no tienen a sus controles "hijos" en una colección Controls sino en Items por lo que hay que considerar esa diferencia también.

En la siguiente actualización voy a hablar de como se autorizan los controles (como funciona el método Authorize.

No hay comentarios:

Publicar un comentario