A veces, puede interesarnos limitar la cantidad de usuarios que se conectan a una Base de Datos usando nuestra aplicación. Por ejemplo, si le vendimos 5 licencias a un cliente queremos que solamente puedan conectarse desde 5 computadoras.
Fíjate que no le podemos limitar las conexiones a la Base de Datos a nuestro cliente, porque los datos son suyos, no nuestros. O sea que si él quiere conectarse usando ISQL.EXE, EMS SQL Manager, IBExpert, Flame Robin, o cualquier otro programa puede hacerlo, no se lo podemos impedir a no ser que él haya firmado un contrato donde está claramente establecido que tiene prohibido hacerlo.
En otras palabras, no le podemos impedir que se conecte, 10, 20, ó 590 veces concurrentemente desde afuera de nuestra aplicación.
Pero lo que sí podemos hacer es limitar la cantidad de usuarios que se conectan usando nuestra aplicación. Por ejemplo, podemos impedirle que se conecten más de 5 usuarios usando nuestro sistema de Contabilidad.
Para conseguir nuestro objetivo usaremos excepciones y triggers de la Base de Datos.
Ejemplo:
Tenemos una aplicación cuyo ejecutable se llama MIAPLICACION.EXE y queremos limitar a un máximo de 5 conexiones simultáneas.
CREATE EXCEPTION E_ACCESO_NO_PERMITIDO 'La cantidad de licencias ya alcanzó el límite';
Método 1
CREATE TRIGGER DB_TRIGGER_CONNECT_1 ACTIVE ON CONNECT POSITION 0 AS DECLARE VARIABLE lnCantidadConexiones SMALLINT; BEGIN lnCantidadConexiones = (SELECT COUNT(*) FROM MON$ATTACHMENTS WHERE MON$REMOTE_PROCESS LIKE '%MIAPLICACION.EXE%'); IF (lnCantidadConexiones > 5) THEN EXCEPTION E_ACCESO_NO_PERMITIDO; END;
Esto funcionará perfectamente … siempre y cuando alguien no le cambie el nombre al archivo .EXE porque la verificación se hace tomando en cuenta el nombre del archivo .EXE
Método 2
En este caso creamos una tabla llamada CONEXIONES y cada vez que alguien se conecta a la Base de Datos usando nuestro programa le agregamos una fila a esa tabla y cuando se desconecta le borramos esa fila. La tabla CONEXIONES tiene esta estructura:
Captura 1. Si haces clic en la imagen la verás más grande
donde como puedes ver tres columnas tienen valores por defecto, eso significa que si no se especifican en el INSERT los valores de los campos CON_FECHOR, CON_USUARI y CON_CONEXI el Firebird automáticamente colocará en esas columnas la fecha y hora actuales, el nombre del usuario, y la conexión, respectivamente. Desde luego que puedes agregarle más columnas a esta tabla si lo deseas.
En nuestra Base de Datos creamos un trigger de Base de Datos, como éste:
CREATE TRIGGER DB_TRIGGER_CONNECT_2 ACTIVE ON CONNECT POSITION 1 AS DECLARE VARIABLE lnCantidadConexiones SMALLINT; BEGIN lnCantidadConexiones = (SELECT COUNT(*) FROM CONEXIONES); IF (lnCantidadConexiones > 4) THEN EXCEPTION E_ACCESO_NO_PERMITIDO; END
donde lo que hacemos es verificar que la cantidad de conexiones no sobrepase el límite que habíamos determinado. ¿Pero por qué ponemos 4 y no 5? Porque la inserción en la tabla CONEXIONES se hace después de haberse conectado a la Base de Datos y por lo tanto en la variable lnCantidadConexiones no se está contando la conexión actual, por ese motivo hay que colocar el número anterior al del límite. Si la cantidad de conexiones supera a 5 entonces se lanzará una excepción y se desconectará automáticamente de la Base de Datos.
Y cuando alguien ejecuta nuestro programa lo primero que hacemos es insertarle una fila a la tabla CONEXIONES, de esta manera:
INSERT INTO CONEXIONES (CON_IPXXXX) SELECT MON$REMOTE_ADDRESS FROM MON$ATTACHMENTS WHERE MON$ATTACHMENT_ID = CURRENT_CONNECTION
Y cuando alguien sale de nuestra aplicación eliminamos una fila de la tabla CONEXIONES, así:
DELETE FROM CONEXIONES WHERE CON_CONEXI = CURRENT_CONNECTION END
Entonces, lo que sucede es lo siguiente:
- Usando nuestra aplicación se conectan a la Base de Datos
- Verificamos si se sobrepasó el límite de conexiones permitidas. Si así fue, se desconecta de la Base de Datos
- Desde nuestra aplicación le insertamos una fila a la tabla CONEXIONES
- Cuando salen de nuestra aplicación borramos una fila de la tabla CONEXIONES
De esta manera conseguimos el objetivo que nos habíamos propuesto: que jamás usen nuestra aplicación más de 5 computadoras a la vez.
Sin embargo este método tiene un pequeño problema: si ocurre un corte de la energía eléctrica varias computadoras podrían haberse desconectado de la Base de Datos pero en la tabla CONEXIONES continuarán sus datos, como si estuvieran conectadas.
En el peor de los casos podríamos tener 5 computadoras desconectadas pero la tabla CONEXIONES nos dirá que las 5 están conectadas.
Para solucionar el problema podemos usar el método 3.
Método 3
En nuestro programa, antes de insertar la fila en la tabla CONEXIONES, borramos todas las filas de la tabla CONEXIONES cuyos datos no se encuentren en la tabla MON$ATTACHMENT.
De esa manera en la tabla CONEXIONES siempre tendremos conexiones actuales y nunca tendremos conexiones «fantasmas».
¿Por qué?
Porque en la tabla MON$ATTACHMENT siempre tenemos los datos de todas las conexiones actuales. Si en la tabla CONEXIONES tenemos una conexión que no existe en la tabla MON$ATTACHMENT entonces está mal y debemos borrarla.
DELETE FROM CONEXIONES WHERE CON_CONEXI NOT IN (SELECT MON$ATTACHMENT_ID FROM MON$ATTACHMENTS)
Entonces, lo que sucedería es lo siguiente:
- Usando nuestra aplicación se conectan a la Base de Datos
- Verificamos si se sobrepasó el límite de conexiones permitidas. Si así fue, se desconecta de la Base de Datos
- Desde nuestra aplicación borramos todas las filas de la tabla CONEXIONES que no se encuentren también en la tabla MON$ATTACHMENTS
- Desde nuestra aplicación le insertamos una fila a la tabla CONEXIONES
- Cuando salen de nuestra aplicación borramos una fila de la tabla CONEXIONES
Conclusión:
Si limitamos la cantidad de conexiones a la Base de Datos entonces tenemos una protección contra la piratería pues aunque consigan copiar nuestra aplicación en otra computadora no podrán realizar la conexión y de nada les servirá. Además, tenemos la posibilidad de instalar nuestra aplicación en muchas computadoras para que la revisen y la evalúen pero nos aseguramos que no la usen más usuarios de los que determinemos nosotros.
Artículos relacionados:
wrov
Dic 02, 2013 @ 13:07:02
Para saber como proteger los stored procedures y triggers puedes leer este artículo:
En general, se trate de tablas .DBF o no, siempre debes firmar un contrato con tu cliente donde delimites claramente tus responsabilidades y obligaciones. En ese contrato debes establecer específicamente que cualquier alteración a los metadatos por alguna persona que no haya sido expresamente autorizada por tí elimina todas las garantías. Y que regresar la Base de Datos a su estado operativo normal tendrá un costo monetario el cual dependerá de la gravedad de las alteraciones y el tiempo que te tomará repararlas.
Para que a tí te resulte muy fácil repararla (aunque el cliente no lo sepa) siempre debes tener un script de los metadatos. Lo correcto es documentar a los scripts (aunque requiere trabajar y sabemos muy bien que la mayoría de los programadores no queremos documentar). Si en cada script además de la fecha de modificación escribes todas las modificaciones realizadas tendrás una documentación excelente.
Saludos.
Walter.
Edgardo Linares
Mar 13, 2021 @ 17:50:21
Saludos, una consulta hay manera de permitir solo un Usuario se conecte a la Base de datos, pero que no sea simultáneamente? me explico tengo un usuario y contraseña y entro en un equipo a la Base de datos, pero en otro equipo si uso ese Usuario y contraseña accede y lo que quiero que sea es que si ya esta ese usuario abierto no se pueda conectar con el mismo usuario se podrá restringir esa conexión?
Jose Angel
Dic 05, 2013 @ 06:56:40
Hola, muy interesante tu blog, tengo una duda, que problema habría en resistrar las conexiones de los usuarios a la BD desde otro TRIGGER de BD, otro ON CONNECT que hiciera
SELECT MON$REMOTE_ADDRESS FROM MON$ATTACHMENTS
WHERE MON$ATTACHMENT_ID = CURRENT_CONNECTION
INTO :CON;
INSERT INTO CONEXIONES_BD (ID,IP)
VALUES (GEN_ID(GEN_ID_CON, 1),:CON);
en lugar de hacerlo manualmente desde la Aplicación-
Un saludo
wrov
Dic 05, 2013 @ 08:59:30
No veo que haya algún problema, también podría hacerse de esa manera.
Saludos.
Walter.
georgeonil
Jun 04, 2014 @ 13:26:27
Y si lo que se desea es que dos usuarios no se conecten al mismo tiempo con el mismo nombre de usuario y contraseña ¿hay forma de evitar eso? o al menos detectarlo y que nuestra aplicación avise de eso detalle, para bloquear la conexión.
wrov
Jun 04, 2014 @ 21:09:14
Lo de la contraseña no es tan sencillo, salvo que guardes en algún lugar la contraseña de cada usuario y en ese caso estarías debilitando la seguridad de tu Base de Datos.
Con respecto a los nombres de los usuarios sí podrías guardarlos en una tabla cada vez que se conectan (lo harías en un trigger de la Base de Datos) y los borrarías de esa tabla cuando se desconectan (mediante otro trigger de la Base de Datos). Lo aconsejable por supuesto es que los nombres de usuarios que guardes en esa tabla se encuentren encriptados.
Saludos.
Walter.
Carlos
Ago 08, 2014 @ 18:12:30
Buen Aporte, Gracias.
Tengo un detalle haciendo las prueba, a continuación explico:
1.- Nombre del programa Ejemplo.exe
1.- Dos computadoras «Servidor» y «Estacion1»
2.- Ejecuto ibexpert en «Servidor»
3.- Ejecuto Ejemplo.exe en «Servidor» y «Estacion1»
4.- Hago consulta por ibexpert SELECT * FROM MON$ATTACHMENTS; dice que tengo 3 registros (perfecto)
5.- En «Estacion1» tengo abierto Ejemplo.exe desconecto el cable de red (simulación de desconexión abrupta)
6.- Vuelvo a ejecutar por ibexpert SELECT * FROM MON$ATTACHMENTS; dice que tengo todavía 3 registros, deberían aparecer 2 registros.
Pasa lo mismo al suspender «Estacion1».
Que consejo me podrías dar para este caso?
wrov
Ago 09, 2014 @ 12:46:54
Hola Carlos
Bueno, hay dos temas allí.
1. Si estás usando SuperServer entonces todas las consultas que hagas se guardarán en el caché
2. Si tu conexión es a través de TCP/IP entonces el Firebird será notificado de que la conexión ha caído solamente después de 2 horas ¿por qué eso? porque ese es el valor por defecto del protocolo TCP/IP. Tienes dos formas de disminuir ese tiempo:
a) modificando la entrada DummyPacketInterval del archivo FIREBIRD.CONF
b) cambiando el valor de keepalive en las propiedades de TCP/IP
Ten en mente que si usas la forma b) podrías estar afectando a otros programas que suponen que la ruptura de la conexión debe demorar más tiempo.
Prueba esas soluciones y avísame si funcionaron.
Saludos.
Walter.
erick
Oct 29, 2014 @ 15:44:32
Muy interesante, aunque yo utilizo lo que seria un 4to método que me ha funcionado de maravillas. No me apoyo de las tablas de sistema, creo una tabla en mi base de datos, donde almaceno el ip y la fecha/hora del momento, con un timer, cada tres minutos actualizo el campo de la fecha/hora de cada usuario conectado, lo que me que quiere decir que si el campo de fecha/hora tiene mas de tres minutos es porque el usuario ya no esta en la aplicación, por lo tanto hago un conteo de los registros con menos de tres minutos para obtener los usuarios conectados y compararlos con la cantidad de usuarios autorizados a usar la aplicación
wrov
Oct 29, 2014 @ 16:55:17
Sin dudas que funcionará, pero:
1. Deberás asegurarte que todos tus programas tengan un control Timer que cada 3 minutos actualice la tabla. Si te olvidaste en alguno, fallará
2. Estarás sobrecargando la red con tráfico innecesario. Si por ejemplo tienes 1.000 usuarios conectados entonces cada 3 minutos además de las actividades normales de ellos tendrás 1.000 actualizaciones a la tabla, en 1 hora 20.000 actualizaciones y en un día normal laboral 160.000 actualizaciones. Para mí, es una exageración porque se puede resolver de otro modo.
3. Al aumentar el tráfico en la red las solicitudes del Cliente y las respuestas del Servidor podrán tardar más de lo debido, si hay pocos usuarios ni se notará pero en entornos donde hay muchos usuarios concurrentes sí.
Saludos.
Walter.
erick
Oct 30, 2014 @ 10:44:02
Si, tienes razón, en entornos «MACRO», seria una situación un poco engorrosa, pero cada caso es diferente, solo tengo una aplicación que necesita dicho control y ningún cliente que posee esta aplicación sobrepasa los 8 usuarios simultáneos… aunque siempre hay que pensar en grande 🙂
Saludos,