DNI Electrónico Helper

Siempre me ha parecido muy mala la implantación del DNI electrónico: hay muchas páginas que sólo lo soportan en navegadores específicos, otras necesitan lectores específicos y otras directamente no lo soportan aunque aparentemente sí que lo hagan.

En cualquier caso, incluso las que funcionan bien, te exigen escribir la clave unas cuantas veces durante el proceso: para acceder al almacén de certificados, selecionarlo, utilizarlo… Al final si tienes una clave segura resulta muy incómodo utilizarlo.

Pensando en esta comodidad y en poder hacer aplicaciones de integración con bancos automatizadas he desarrollado un pequeño programa que al ser ejecutado detecta todas las interacciones con el lector de DNIs e introduce la contraseña sin necesidad de intervención del usuario. Aunque no os interese la funcionalidad es interesante ver cómo se detecta una ventana y se envían caracteres a otra aplicación.

La presentación es mejorable :-P, of course, pero funciona! La clave se guarda en AppData encriptada. La opción de «seleccionar certificado» hace que tambien se seleccione automáticamente el certificado, útil si sólo tienes uno en tu equipo. El segundo check muestra un aviso en el área de notificación cada vez que se detecta la ventana y espera un segundo antes de actuar automáticamente.

Veamos un poco de código:

                 //Detección de la Ventana de introducción clave DNI electrónico y envío de caractéres
                dniApp = FindWindow(null, «DNI electrónico»);
                if (dniApp.ToInt32() > 0)
                {
                    if (chkNotificaciones.Checked)
                    {
                        notify.ShowBalloonTip(500, «DNI Electrónico Helper», «Peticion de clave DNI», ToolTipIcon.None);
                        System.Threading.Thread.Sleep(1000);
                    }
                    SetForegroundWindow(dniApp);
                    SendKeys.SendWait(Crypto.DecryptStringAES(Properties.Settings.Default.Clave, claveEncriptacion));
                    SendKeys.SendWait(«{ENTER}»);
                }

        //Imports de DLLs
        [DllImport(«USER32.DLL», CharSet = CharSet.Unicode)]
        public static extern IntPtr FindWindow(string lpClassName,
            string lpWindowName);
       
        // Activate an application window.
        [DllImport(«USER32.DLL»)]
        public static extern bool SetForegroundWindow(IntPtr hWnd);
        #endregion

Adjunto el código y el ejecutable para probarlo, espero que os sea útil!

 

Asociar tabla a aspnet_users

A pesar de no tener ninguna complicación especial, son muchos los programadores que utilizan enlaces no naturales para vincular tablas con el sistema de usuarios de ASP.NET.  En la jerarquía de tablas instalada, la tabla de usuarios ASPNET_Users tiene como clave primaria la columna UserId y esa será la columna que debamos de utilizar para enlazar con el resto de tablas.

El problema es que esta columna es un GUID, característica que hace que nos pueda parecer poco idónea para utilizarla como clave foránea. Las alternativas que tenemos como claves candidatas en la tabla de usuarios es la combinación de ‘LoweredUserName’ y ‘ApplicationId’. Aún en el caso de que ignoremos ‘ApplicationId’ por utilizar la base de datos para una única aplicación, el campo LoweredUserName es de tipo varchar(256), mucho mayor que un GUID y desde luego mucho menos adecuado para utilizar como clave externa.

Veamos un ejemplo básico de como se personaliza un control CreateUserWizard para añadir información personalizada en una tabla viculada con la estructura de Membership.

1.- Diseño de la tabla: la clave primaria coincide con el campo UserId de ASPNET_Users. En este sencillo ejemplo vamos a guardar el nombre, apellido y fecha de nacimiento.

2.- Añadimos un paso más al control para solicitar la información adicional:

 3.- Controlamos la navegación en el primer paso del Wizard, que sigue siendo el paso de creación del usuario:

4.- Guardamos la información adicional al finalizar el asistente. Evidentemente podemos utilizar cualquier tecnología de acceso a datos aunque para mayor claridad haya escogido la más básica.

 Fíjate que este código es independiente del proveedor del Membership Provider que utilicemos, ya que la propiedad ProviderUserKey devuelve un objeto con la clave primaria del proveedor. En nuestro caso esta clave primaria es el UserId de tipo Guid, para otros proveedores puede ser cualquier otra cosa.

Change Data Capture. Histórico de cambios en SQL Server 2008 R2

SQL Server posee un mecanismo propio de traza de cambios DML y DDL que permite consultar versiones anteriores de una misma tabla, ver las modificaciones que ha sufrido durante un determinado espacio de tiempo, o un amplio espectro de modificaciones DDL de la propia tabla.

Para ello no es  necesario crear ningún mecanismo externo, ni implementar complejas soluciones basadas en triggers: basta con habilitar CDC: Change Data Capture.

  1. Habilitar CDC en la base de datos
  2. Habilitar CDC para la tabla correspondiente, puede habilitarse para todas las columnas o para columnas en particular
  3. Siempre que queramos utilizamos la consulta cdc.fn_cdc_get_all_changes_nombreTabla para obtener los cambio realizados entre dos LSN determinados.

Los pasos a seguir son muy sencillos, y pueden verse la demostración completa en el código de ejemplo. Se trata de la creación de una tabla de ejemplo con columnas IdProducto, Nombre, Precio, PrecioRebajado, donde despues de habilitar CDC se realizan tres inserciones, dos actualizaciones y un borrado. El resultado de consultar los cambios es una tabla como esta:

Las operaciones se ordenan por orden de ejecución, siendo la primera la más antigua. Podemos observar tres primeros INSERT y un DELETE. En estos casos la información que aparece es la insertada y la borrada de la tabla respectivamente.

En el caso de UPDATE, se insertan dos filas por actualización. La primera de ellas se corresponde con lo que había en la tabla antes del UPDATE y la segunda con lo que hay después. En esta operación también es interesante consultar la columna __$update_mask, que nos indica cuáles han sido las columnas afectadas por la actulización. Por ejemplo, 0x0A=0000 1010 indica que se han modificado la segunda y la cuarta, mientras que 0x02=0000 0010 indica que únicamente se ha visto afectada la segunda columna de la tabla.

Autonumérico entre varias tablas

Tenemos dos tablas de datos en SQL Server en las cuales se van a insertar filas indiscriminadamente. Necesitamos saber en qué orden fueron insertadas estas filas, poder sacar un UNION entre ambas tablas y ordenarla por su inserción. Es necesario además detectar ‘huecos’ en las filas, es decir, queremos crear un autonumérico pero que implique a ambas tablas.

Cómo sabemos, si utlizamos un campo integer la numeración es independiente en cada una de las tablas implicadas, así que no nos sirve. Un timestamp tampoco es la solución, ya que varía con cada versión de la fila: cada update que hagamos sobre la fila actualizará a su vez la columna timestamp.

La otra solución sería utilizar como clave un campo fecha, lo cual nos solucionaría el problema de unir las dos tablas pero no el de la detección de huecos. No habría forma a saber si entre dos filas de la tabla A hay alguna de la tabla B a no ser que hagamos la union directamente.

Desde 2005 tenemos una solución a este problema: newsequentialId(). Esta función sólo se puede utilizar como Default Constraint y nos devuelve un GUID distinto de cada vez, pero de forma secuencial.

En la imagen superior podemos ver el resultado de realizar dos inserciones en la tabla A, una en la B, y a continuación seguir insertando en la A. El byte menos significativo del GUID está representado por los dos primeros caracteres hexadecimales. Cómo se puede ver, son secuenciales E7, E8, falta el E9 y continua con EA, EB… La serie es muy sencilla de predecir: ECD0, EDD0, EED0, EFD0, F0D0, F1D0, …, FFD0, 00D1, 01D1 ,…

Objetivo cumplido: orden, unicidad y densidad! El código de prueba para generar las tablas de ejemplo es el siguiente:

create table #TablaA
(
  
IdNum int identity(1,1),
  
IdGuid uniqueidentifier default(NEWSEQUENTIALID()),
  
valor varchar(50)
);

create table #TablaB
(
   IdNum int identity(1,1),
  
IdGuid uniqueidentifier default(NEWSEQUENTIALID()),
  
valor varchar(50)
);

insert into #TablaA(valor)values (‘a’)
insert into #TablaA(valor)values (‘a’)
insert into #TablaB(valor)values (‘a’)
insert into #TablaA(valor)values (‘a’)
insert into #TablaA(valor)values (‘a’)

select * from #TablaA
select * from #TablaB

Examen de actualización a MCPD 40 Web Developer

Ya está disponible la información del examen de actualización 70-523: Updgrade transition your MCPD.NET Framework 3.5 Web Developer Skills to MCPD Framework 4 Web Developer.

Aunque la certificación se pudo obtener desde hace ya unos meses gracias a los exámenes beta, y desde el 22 de octubre está disponible en Prometric, no existía hasta hoy ninguna página (pública) donde se detallase el contenido de dicho examen.

Ahora falta saber cómo está de acertado en nivel, porque si es de un nivel parecido al de certificación MCPD en 4.0 directo sale más rentable empezar certificándose en 3.5 y migrar con este único examen a 4.0. La ruta directa para ser MCPD 4.0 consta de tres exámenes MCTS y un último para ser MCPD. En cambio la ruta alternativa consta de dos exámentes MCTS, uno para ser MCPD en 3.5 y un último de transición.

Son cuatro exámenes igual, pero de la forma alternativa tienes las certificaciones de 2008 y las de 2010, además de demostrar que eres capaz de aprobar el 70-536 y por tanto conoces los fundamentos de la plataforma.

Cada uno que saque sus conclusiones!

Conversiones implícitas en SQL Server

Una de las novedades que Sql Server 2008 trajo en su día fue la capacidad de poder pasar listas de valores a una instrucción Insert Into sin tener que unirlos en una tabla mediante UNION. El siguiente código es totalmente correcto:

create table #pruebas (idCargo varchar(50),nombre varchar(50));

insert into #pruebas values
   
(0,‘Persona_1’),
    (
‘1’,‘Persona_2’),
    (
»,‘Persona_3’);

Fíjate que estamos insertando en una tabla con dos columnas tipo texto distintos valores, como son un número, un caracter y un texto sin contenido. El resultado es el siguiente:

Lo que nos debería de preocupar de este resultado es la fila 3. Se puede observar que se ha convertido la cadena vacía a un 0, lo cual significa que ha utilizado la conversión implícita desde varchar a integer. Este efecto puede tener consecuencias desastrosas, cómo en este otro ejemplo:

insert into #pruebas values
   
(0,‘Persona_1’),
    (
‘1’,‘Persona_2’),
    (
‘a’,‘Persona_3’);
 

Este «inocente» script hace que salte una excepción
«Msg 245, Level 16, State 1, Line 3 Error de conversión al convertir el valor varchar ‘a’ al tipo de datos int.»

¡¡¡Se está quejando de que no puede convertir el texto a número cuando estamos insertando textos en un campo de texto!!!

El motivo es que en la primera fila estamos insertando un int, e internamente Sql Server va a tratar de crear una tabla para los tres valores con la estructura que define esa primera fila, en nuestro caso un entero en la primera columna y un varchar en la segunda 🙁

 

 

ASP.NET Web Forms y los nuevos selectores CSS3

Aunque no es en absoluto la mejor opción, tengo observado en programadores de ASP.NET el intento de incluir estilos en elementos buscándolos por su identificador. Siempre será una opción menos mala que la de meter el estilo en línea, pero no tan buena cómo utilizar clases CSS.

En ASP.NET Web Forms nos encontramos con una barrera adicional: no conocemos los identificadores de los elementos en tiempo de diseño. Un ejemplo sencillo, incluyo una <asp:label id=»lblPrueba»> dentro de un <asp:content> que a su vez utilizará una MasterPage, y el resultado final será que dicho elemento se me renderiza cómo <span id=»MainContent_lblPrueba»>. En ese caso si queremos aplicar un estilo CSS a dicho elemento buscándolo por id, tendremos que escribir el selector correspondiente para el id final, es decir, el que se enviará al navegador. Es evidente que un cambio en los contenedores del elemento puede dar al traste con esta feliz unión entre CSS e Id del elemento.

Aprovechando la circunstancia de que el identificador original que nosotros dimos al elemento siempre aparece al final del identificador cliente podemos utilizar el nuevo selector de CSS3 para seleccionar lo que nos interesa. En este caso nos valdría algo así:

     *[id$=lblPrueba]        { «estilos para la etiqueta lblPrueba»           }

Este selector viene diciendo: selecciona cualquier elemento (*) cuyo id termine en «lblPrueba», con lo cual asunto zanjado.

Puedes ampliar información sobre este selector y otros parecidos en esta página.

Intercambio de passwords en ASP.NET

A veces para depurar nuestra aplicación necesitamos entrar «como si fuésemos» otro usuario. Si utilizamos la seguridad de formularios de ASP.NET la información de usuarios se guardará normalmente en una base de datos de SQLServer. Por seguridad la contraseña de los usuarios no se guarda en la BBDD, sino que se guarda un hash que nos sirve para saber si el usuario ha metido la contraseña correcta.

La forma menos intrusiva y más sencilla que tenemos de autenticarnos como otro usuario, es copiar nuestro password en el suyo. Para ello debemos de copiar dos campos de la tabla aspnet_Membership: Password y PasswordSalt. Para encontrar cuál es el usuario tenemos también que acceder a la tabla aspnet_Users, en donde podemos buscarlo por UserName. Y al acabar de hacer las pruebas estaría bien dejar todo como estaba, así que lo ideal sería haber guardado la información original en algún sitio.

El siguiente script hace todo esto por nosotros: intercambia la contraseña de dos usuarios, definidos por sendos parámetros al principio. De esta forma si quiero hacer una prueba en mi aplicación con el usuario ‘admin’, basta con que ejecute el script, entre con mi contraseña y el usuario ‘admin’ y finalmente vuelva a ejecutar el script para intercambiar de nuevo las contraseñas y dejar todo como estaba.

declare @Usuario1 varchar(250),@Usuario2 varchar(250);
--Datos de prueba
select @Usuario1='miUsuario',@Usuario2='admin';

begin
tran
    --Guardo la informacin del usuario1 y 2
   
select U.UserName,M.UserId,M.ApplicationId,M.Password,M.PasswordSalt
   
into #infoUsuario
   
from aspnet_Users U
   
inner join aspnet_Membership M
   
on U.UserId=M.UserId and U.ApplicationId=M.ApplicationId
   
where U.UserName=@Usuario1 or U.UserName=@Usuario2;

    --Intercambio las informaciones
   
update aspnet_Membership
   
set [Password]=n.[Password], PasswordSalt=n.PasswordSalt
   
from
   
(--Consulta que hace el verdadero swap
       
select A.UserName, A.UserId,A.ApplicationId,B.Password,B.PasswordSalt
        from
#infoUsuario A,#infoUsuario B
       
where A.UserId<>B.UserId
   
) n
   
where
    aspnet_Membership
.ApplicationId=n.ApplicationId
   
and aspnet_Membership.UserId=N.UserId;

    --Borrado de la tabla temporal
    if(OBJECT_ID('tempdb..#infoUsuario') is not null)
   
    drop table #infoUsuario;
commit tran

 

Fíjate que la base de datos de membership puede ser compartida por varias aplicaciones de asp.net, de ahí que introduzca en la consulta el campo ApplicationId. Si tienes esta información en tu base de datos exclusiva para la aplicación puedes obviar esta condición.

¿Sencillo, no?

Doodle K

Estos días está todo el mundo conjeturando acerca de las intenciones y significados de los últimos doodels. Más allá de sus motivos (supongo que hacer ruido) el efecto es cuánto menos curioso y muy sencillo de replicar:

¿Cuál es el truco? No hay Flash, ni Silverlight ni nada complicado detrás de este efecto: simplemente tenemos dos divs con su background establecido a la misma imagen, una de ellas en color y la otra en gris. El único truco de este efecto es una función de javascript que responde al evento onkeyup(this.value.length) que envía el número de caracteres del cuadro de texto para redimensionar el div que contiene la imagen en color.
 
function SetFrontWidth(value)
{
    var div1=document.getElementById(‘divK’);
    var longitud=Math.round(700*value/6);
    if(longitud>580)
        longitud-=20;
    div1.style.width=longitud;
}

Para más detalle se puede bajar el código completo de este ejemplo (no utiliza css ni código externo alguno).

 

¿Por qué cambiar IsNull por Coalesce?

IsNull es la función de Sql Server que la mayoría de los programadores que llevan unos años escribiendo consultas utilizan. Su función es devolver un valor por defecto si el primer argumento pasado es nulo.

Por ejemplo, supongamos que tenemos la siguiente tabla:

declare @Clientes as table (Id int, Nombre varchar(100));

 Y queremos actualizar un campo determinado, por ejemplo el Id=2, para que el nombre coincida con un parámetro que nos pasarán desde programa. Entonces la consulta a ejecutar sería tan sencilla como esta:

update @Clientes set Nombre = @NuevoNombre where Id=2;

Ahora  queremos mejorar nuestra consulta, de forma que si el parámetro @NuevoNombre es nulo la fila no se actualice y mantenga su valor. Entonces podríamos substituir la segunda línea por cualquiera de estas dos opciones:

        a) set Nombre=IsNull(@NuevoNombre,Nombre);

        b) set Nombre=Coalesce(@NuevoNombre,Nombre) 

 Siempre que el parámetro esté correctamente definido ambas consultas se comportarán igual. El problema viene cuando el parámetro (por ejemplo por utilizar inferencia en el sistema de origen) viene con una definición distinta. Imaginemos por ejemplo que definimos así el parámetro y ejecutamos la consulta:

declare @NuevoNombre as varchar(2)= null;

En el update con calesce el resultado es el esperado, mientras que en el caso de IsNull, la tabla se actualizará incorrectamente. Esto es debido a que realmente IsNull no devuelve el segundo parámetro, sino el resultado de convertir el valor del segundo parámetro al tipo del primero, lo cual consituye una diferencia fundamental.

A continuación listamos el código completo para copiar y pegar y demostrar el funcionamiento. Basta con descomentar alternativamente uno u otro UPDATE para comparar los resultados.

declare @Clientes as table ( Id int, Nombre varchar(100));
insert into @Clientes values
    (1,'Francisco Otero'),
    (
2,'Otro fulano');

declare @NuevoNombre as varchar(2)=null;
--update @Clientes
--set Nombre= @NuevoNombre
--where Id=2

--update @Clientes
--set Nombre=Coalesce(@NuevoNombre,Nombre)
--where Id=1

select * from @Clientes;