Apuntes para una seguridad a nivel de usuario en Access 2010 II. Macros de datos
Después de casi dos años tomando impulso, por fin me animo a continuar con la seguridad a nivel de usuario usando las macros de datos. El que consiga acabar este artículo me disculpará por el retraso, pues se dará cuenta de que hacía falta tener ánimo para ponerse a hablar de las macros de datos: Apenas nadie utiliza macros, el editor y muchas características son completamente nuevos y las macros de datos son una novedad absoluta en Access 2010, a lo que se añade que apenas existen ayuda o facilidades al respecto.
Tendría que explicar el funcionamiento de las macros, los tipos, sus posibilidades, las macros de datos y, cuando empezara por la forma de usarlas para asegurar una tabla a nivel de usuario, ya habría escrito todo un tratado, seguramente muy aburrido. Así que voy directamente al grano, a empezar dando unas ideas de cómo crear macros que eviten que los usuarios no autorizados expresamente manipulen los datos de las tablas y, a medida que las voy explicando, mencionaré pormenores y detalles de las macros y de su editor. Espero que se entienda algo.
En el artículo anterior veíamos que, para asegurar una base de datos Access, es necesario hacer inaccesible el back-end y dábamos unas pistas acerca de cómo protegerlo. Si un usuario sin permiso expreso puede acceder directamente al contenido de las tablas, o, peor aún, a su diseño, todo lo hablado aquí no sirve para nada. Una vez conseguido esto, podemos abordar la utilización de macros de datos para que solo los usuarios autorizados puedan añadir, editar o borrar datos solo en las tablas para las que se les haya concedido permiso. Tenga en cuenta que solo son unos apuntes y que es su propia responsabilidad procurar que no queden agujeros en la seguridad.
Grosso modo, lo que vamos a hacer con las macros es verificar si el usuario tiene permisos para la operación que está pretendiendo hacer y, si no los tiene, abortarla. Un paso mas adelante, aprovechando de que conocemos el usuario, vamos a guardar en cada registro qué usuario lo crea, lo que aparte de un rudimentario seguimiento de cambios, nos permite dar permisos para que sólo el autor de un registro pueda modificarlo.
Las macros de datos
Las macros de datos son nuevas en Access 2.010 y el editor de macros, de datos o no, también es nuevo, por lo que no es de extrañar que haya muy poco publicado al respecto.
Las macros de datos están disponibles en la Vista Diseño de la tabla, en la pestaña Diseño de la cinta de opciones, en el grupo de “Eventos de campo, registro y tabla”
Es decir, en Access 2010 las tablas tienen eventos y podemos escribir macros en respuesta a estos eventos. Los evento son Después de insertar, Después de actualizar, Después de eliminar, Validar eliminación y Antes del Cambio. Desde el punto de vista de la seguridad por usuarios, los tres primeros son apropiados para registrar al usuario que produce el evento, y los dos últimos para verificar si el usuario tiene los permisos necesarios y, en caso contrario, abortar la operación.
Vamos a empezar por algo muy, muy básico, impedir que se pueda borrar un registro accidentalmente, pero antes debemos abrir el editor de macros.
El editor de macros
Da la sensación de que el editor de macros en Access 2010 está a medio acabar, pues le faltan cosas tan elementales como un menú contextual para copiar y pegar, que, sin embargo, ya existe en 2013, y la ayuda es mínima. No obstante, lo esencial funciona correctamente y sirve a la perfección para nuestro propósito.
Pinchando en uno de los eventos del desplegable de la cinta de opciones que veíamos antes, se abre el editor de macros. Las acciones disponibles varían según el tipo de macro y el tipo de eventos. Es decir, las macros de datos tienen muchas menos acciones disponibles y los eventos Validar eliminación y Antes del cambio, menos aún. Y, sin embargo, con estas macros se pueden conseguir cosas antes impensables.
Hemos abierto el editor de macros para el evento Validar eliminación. A la derecha se muestran todas las acciones disponibles, bien pocas, y a la izquierda una ventana de edición, más grande con un símbolo mas y un cuadro desplegable en el que podemos elegir la acción que queremos anotar. Si no se mostrara el catálogo de acciones, podemos hacerlo visible pulsado en el botón “Catálogo de acciones” de la cinta de opciones.
Las acciones se eligen en el cuadro desplegable o se arrastran desde el Catálogo de acciones que tenemos a la derecha. No es un editor donde podamos escribir código, solo elegir entre las opciones que tenemos y tan solo escribiremos algunas expresiones cuando sean parámetros de la acción que hemos elegido.
Nuestra primera macro
Vamos a crear nuestra primera macro. En el desplegable elegimos Provocar error.
Escribimos el valor de los parámetros que nos pide, el Número de error, que es el que queramos, y la Descripción del error, que será el mensaje que se muestre al producirse el error. Guardamos y ya tenemos nuestra primera macro de datos
Abra la tabla e intente borrar un registro. Imposible, nos sale el mensaje que hemos puesto en la descripción del error: ¡Prohibido borrar! y ni en un formulario, ni por código, ni por consulta de eliminación, ni directamente en la Vista Hoja de datos de la tabla podremos borrar ningún registro.
Bueno, nos hemos pasado, hemos de dar alguna posibilidad de borrar un registro. Por ejemplo, podemos añadir a la tabla un campo Sí/No que se llame Borrar, y, si es verdadero, sí se puede borrar el registro.
Ahora, lo que tenemos que hacer con la macro es comprobar el valor del Campo Borrar y, si no es verdadero, provocar el error.
Vamos a la Vista Diseño de la tabla y abrimos la macro del evento Validar eliminación. Arrastramos el grupo Si al editor e introducimos las expresiones, para lo cual contamos con la ayuda del estupendo generador de expresiones
En la condición escribimos [Borrar] <> True … Entonces, lo que tenemos que hacer es provocar el error, como hemos hecho antes. Pero el código para provocar el error se encuentra después de Finalizar si, es decir fuera del bloque If..,Endif. Vamos a arreglarlo.
Observe que el bloque donde estamos introduciendo las expresiones está marcado, delimitado por un cuadrado y con fondo mas oscuro. Si pinchamos con el ratón sobre el bloque de ProvocarError, es éste el que queda marcado y, si movemos el ratón manteniéndolo pulsado, el bloque se mueve.
Pues bien, arrastramos el bloque de ProvocarError dentro del bloque Si…Finalizar si y voilà, ya tenemos nuestra macro:
Guardamos la macro, cerramos y comprobamos si podemos borrar. Podremos hacerlo si, previamente, para ese registro hemos marcado el campo Borrar como verdadero. Es sencillo y, tal como está, vale para cualquier tabla, por lo que vamos a copiarla para pegar en otras tablas y mandarnos el código por correo al trabajo, para hacerlo también allí.
Editamos de nuevo la macro.
Como no tenemos marcado ningún bloque, el código se queda mas limpio, mas claro. Podemos observar que nos ha cambiado True por Verdadero, que a las macros les gusta el castellano, incluso a veces no entienden el inglés.
Arrastramos el ratón para seleccionar el texto, pero no se selecciona nada, en cambio, si pinchamos, se seleccionan los bloques que hemos visto antes. Si tenemos cuidado y/o pulsamos también
Mayúsculas o Control al tiempo que el ratón, podemos seleccionar todos los bloques en un bloque único.
Buscamos un botón para copiar, pero no existe, pulsamos el botón derecho del ratón y no aparece ningún menú (bueno, si tienes instalado también Access 2013, sí aparece) ¿Cómo copiamos? Pues como hacíamos antes de los ratones, con Control+C.
Probamos, abrimos otra macro y pegamos. Se pega correctamente y se replica el texto la macro que habíamos copiado.
Llegados a este punto de lectura, usted ya tiene ganas de probar el editor de macros, pero quiere darse prisa y empezar copiando el código desde este blog ¿Desde dónde, si todo son imágenes? Está bien, ahí va el código de la macro:
<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<DataMacros
xmlns="http://schemas.microsoft.com/office/accessservices/2009/11/application"><DataMacro
Event="BeforeDelete"><Statements><ConditionalBlock><If><Condition>[Borrar]<>True</Condition><Statements><Action
Name="RaiseError"><Argument
Name="Number">1000</Argument><Argument
Name="Description">¡Prohibido
borrar!</Argument></Action></Statements></If></ConditionalBlock></Statements></DataMacro></DataMacros>
Efectivamente, las macros se guardan como código xml y, si usted copia ese texto y lo pega en el editor de macros, el resultado se mostrará como la macro original. Para pegar, no pinche sobre la ventana de edición de la macro, sino sobre la pestaña de la ventana.
Verificar permisos de un usuario
Ya tenemos nuestra primera macro de datos y consigue algo que resulta imposible en cualquier versión anterior de Access, pero de lo que trata este artículo es de la seguridad a nivel de usuarios y lo que queremos es poder discriminar qué usuarios pueden borrar y cuáles no. Para ello debemos empezar por una tabla con los permisos de los usuarios, buscar si el usuario actual tiene permiso de borrado y, en caso contrario, provocar un error.
La cuestión ahora es cómo pasamos a la macro el nombre del usuario para que lo busque en la tabla permisos. Las macros de datos no pueden usar ninguna función personalizada, ni siquiera Variables Temporales (Variables Locales, que no son globales, sí) ni existe ninguna propiedad o función incorporada que nos devuelva el usuario de Access o el de Windows y que sea accesible para la macro de datos.
El truco está en que, aunque no se muestren en el generador de expresiones, se pueden hacer búsquedas en consultas y las consultas pueden utilizar una función personalizada como origen de un campo calculado y que las macros de datos pueden buscar en una consulta igual que en una tabla, aunque, para despistar, no se muestren en el generador de expresiones ni el Intellisense para las macros. O sea, que podemos tener una consulta que tenga una función que devuelva el nombre del usuario y la macro de datos lo lea como si fuera un campo.
Las macros de datos y las consultas se ejecutan en el Front-End, aunque se alojen en el Back-End, por lo que la función que devuelva al usuario debe encontrarse en el Front-End y, si no queremos ir haciendo pruebas mientras diseñamos la macro, conviene que el Back-End tenga una copia de esta función. Cómo se gestiona el login de cada usuario y cómo se obtiene la función que devuelve el nombre del usuario actual es cosa personal de cada cual y no se va a tratar aquí, aunque adelanto que para eso también se podrían utilizar macros de datos.
En resumen, la macro debe buscar en una consulta los permisos del usuario actual, obtenido en la consulta mediante una función, y si carece de permiso para borrar en esta tabla, producir un error.
De momento, nos vale con una cosa muy sencillita, basta con que, para cada tabla que queremos proteger, recoja los permisos de cada usuario autorizado.
Se supone que cada cual tendrá construido un sólido sistema para el control de los usuarios que pueden entrar en nuestra aplicación, con usuarios, contraseñas, registro de logins, etc., y la forma de recuperar el usuario actual. Sin embargo, para las pruebas no vale cualquier cosa que nos devuelva un usuario, por ejemplo, el usuario de Windows; así que nos hacemos esta función.
Public Function fUsuarioActual()
fUsuarioActual = Environ(«USERNAME»)
End Function
Y con la tabla de permisos y la función que devuelve el usuario actual, nos podemos montar la consulta que nos devuelva los permisos del usuario actual:
SELECT tblPermisos.Usuario, tblPermisos.Tabla, tblPermisos.Insertar, tblPermisos.Editar, tblPermisos.Borrar
FROM tblPermisos
WHERE (((tblPermisos.Usuario)=fUsuarioActual()));
Y se ve así:
Macro Validar Eliminación
Ahora en nuestra macro Validar eliminación podemos consultar fácilmente si el usuario actual tiene permiso para borrar y, en caso contrario, se lo impedimos. Esto en una macro, se refleja así:
Estudiemos un poco la macro, pues hemos metido alguna cosa nueva y, aunque parezca muy sencilla, tiene su intríngulis.
Está claro que hacemos una búsqueda en la consulta qPermisos con la condición de que el campo Tabla sea “Contactos”.
En vez de comprobar directamente el campo [Borrar], lo hemos pasado a una variable local de nombre PermisoBorrar, que es la que comprobamos después ¿Porqué?
Fíjese en tenemos en bloques distintos Buscar un registro en y Si Nz(PermisoBorrar…. Ocurre que las macros son muy puñeteras y hay que tener mucho cuidado con dónde va y qué va en cada bloque y, dependiendo de esto, la lógica cambia por completo. Fuera del bloque Buscar un registro en qPermisos, el campo [Permisos].[Borrar] no existe y por eso lo pasamos a una VariableLocal, PermisosBorrar, que tiene alcance en cualquier lugar de la macro. Observe que luego la forma de llamar a la variable es como a un campo, con el nombre entre corchetes.
¿Podríamos haber metido el bloque de Si Nz(PermisoBorrar…. dentro del de Buscar un registro en y así nos ahorrábamos la VariableLocal? Efectivamente, pero ya hemos comentado que al meter un bloque en otro cambia la lógica.
En un bloque Buscar un registro en las acciones que vayan dentro de él solo se ejecutan si se ha encontrado el registro buscado, de manera que, si no encuentra los permisos del UsuariActual, no se ejecutaría el código que provoca el error y el registro se borraría sin ningún problema.
Puede tener su lógica que los usuarios para los que no se han establecido permisos para una tabla puedan borrar en ésta, pero resulta mucho mas seguro al revés, que solo los usuarios a los que expresamente se ha dado permiso puedan borrar en la tabla. Tal como está la macro, si no existe el usuario para la tabla Contactos, no se ejecuta lo que hay dentro del bloque Buscar un registro en y, por tanto, no se establece la VariableLocal, de manera que en el siguiente bloque no la encuentra y Access provoca un error y muestra un msgbox con “No se encontró el identificador ‘[PermisoBorrar]’.”, de forma que no se puede borrar un registro. Es decir, si no existe el usuario, se produce un error de Access en la macro y no se puede borrar.
Ya tenemos completa la macro Validar eliminación y abajo pegamos el código xml. Si quiere trasladar esta macro a cualquier tabla, cópielo al portapapeles, abra la tabla en Vista Diseño, edite la macro, posicione el ratón sobre la pestaña y pulse Control+V. Solo le falta cambiar en la Condición Where el nombre de la tabla.
<?xml version="1.0" encoding="UTF-16"
standalone="no"?>
<DataMacros xmlns="http://schemas.microsoft.com/office/accessservices/2009/11/application"><DataMacro
Event="BeforeDelete"><Statements><ConditionalBlock><If><Condition>[Borrar]<>True</Condition><Statements><Action
Name="RaiseError"><Argument
Name="Number">1000</Argument><Argument Name="Description">No se
puede borrar sin haber marcado
previamente el campo
[Borrar]</Argument></Action></Statements></If></ConditionalBlock><LookUpRecord><Data
Alias="Permisos"><Reference>qPermisos</Reference><WhereCondition>[Tabla]="Contactos"</WhereCondition></Data><Statements><Action
Name="SetLocalVar"><Argument
Name="Name">PermisoBorrar</Argument><Argument
Name="Value">Nz([Permisos].[Borrar])</Argument></Action></Statements></LookUpRecord><ConditionalBlock><If><Condition>Nz([PermisoBorrar])<>True</Condition><Statements><Action
Name="RaiseError"><Argument
Name="Number">2000</Argument><Argument Name="Description">Usted
no tiene permisos para
borrar</Argument></Action></Statements></If></ConditionalBlock></Statements></DataMacro></DataMacros>
Macro Antes del cambio
Todo lo que hemos contado para la macro Validar eliminación es válido para la macro Antes del cambio, por tanto, copiar y pegar y corregir algunas cosillas nos puede valer para empezar, aunque en la macro Antes del cambio hay que tener en cuanta alguna cosa mas.
Copiamos al portapapeles la macro Validar eliminación, abrimos la macro Antes de cambio, nos posicionamos en la pestaña y pegamos pulsando Control+V. Borramos el bloque de If Borrar <> Verdadero… Dentro del bloque Buscar un registro en, cambiamos la variable local PermisoBorrar por PermisoEditar y la expresión Nz([Permisos].[Borrar]) por Nz([Permisos].[Editar]) y, después, en el bloque If, [PermisoBorrar] por [PermisEditar] y en el mensaje de error borrar por editar, y el código de error por un número distinto.
La macro queda así:
Con esto debería valernos. Probamos y funciona. Sin embargo, falta algo…
¿Macro Antes de insertar?
No. No existe la macro de datos Antes de insertar, pero tenemos que saber de alguna manera si estamos intentando insertar un nuevo registro. Para ello contamos con la propiedad EsInsertar
Propiedad EsInsertar
En la macro Antes del cambio (y también en la macro Después de Actualizar) podemos consultar la propiedad EsInsertar. (La escasa información que hay sobre macros de datos de Access en internet está inglés, y en inglés es IsInsert, sin embargo, si tratamos de utilizar IsInsert en nuestro Access en Castellano, no lo reconocerá)
Entonces, en nuestra macro Antes del cambio debemos comprobar también si se trata de insertar y, en ese caso, comprobar en la tabla permisos si hay permiso para insertar y actuamos en consecuencia.
No es necesaria una nueva búsqueda. En la misma búsqueda añadimos una variable local mas, PermisoInsertar, y le asignamos el valor del campo insertar.. En un bloque siguiente, comprobamos la propiedad EsInsertar y, si es verdadera, verificamos el valor de PermisoInsertar y, si no, el de PermisoEditar y, si no lo hay, provocamos un error.
Lo probamos y funciona, así que ya podemos pegar el código xml por si alguien mas lo quiere probar.
<?xml version=»1.0″ encoding=»UTF-16″ standalone=»no»?>
<DataMacros xmlns=»http://schemas.microsoft.com/office/accessservices/2009/11/application»><DataMacro Event=»BeforeChange»><Statements><LookUpRecord><Data Alias=»Permisos»><Reference>qPermisos</Reference><WhereCondition>[Tabla]=»Contactos»</WhereCondition></Data><Statements><Action Name=»SetLocalVar»><Argument Name=»Name»>PermisoEditar</Argument><Argument Name=»Value»>Nz([Permisos].[Editar])</Argument></Action><Action Name=»SetLocalVar»><Argument Name=»Name»>PermisoInsertar</Argument><Argument Name=»Value»>[permisos].[insertar]</Argument></Action></Statements></LookUpRecord><ConditionalBlock><If><Condition>[IsInsert]</Condition><Statements><ConditionalBlock><If><Condition>Nz([PermisoInsertar])<>True</Condition><Statements><Action Name=»RaiseError»><Argument Name=»Number»>4000</Argument><Argument Name=»Description»>Usted no tiene permisos para Insertar</Argument></Action></Statements></If></ConditionalBlock></Statements></If><Else><Statements><ConditionalBlock><If><Condition>Nz([PermisoEditar])<>True</Condition><Statements><Action Name=»RaiseError»><Argument Name=»Number»>3000</Argument><Argument Name=»Description»>Usted no tiene permisos para Editar</Argument></Action></Statements></If></ConditionalBlock></Statements></Else></ConditionalBlock></Statements></DataMacro></DataMacros>
Lo mismo que con la macro Validar eliminación, podemos pegarlo en cualquier tabla y solo tendríamos que cambiar en la Condición Where el nombre de la tabla.
Incluyendo estas dos macros en cualquier tabla, conseguimos que ningún usuario que no esté expresamente autorizado pueda insertar, editar o borrar ningún registro. Obviamente, antes hay que asegurarse de que no tenga acceso directamente al back-end, pues podría modificar las macros o la tabla permisos, a la que también deberíamos dar permisos, y de que contamos con un sistema de identificación de usuario y verificación del inicio de sesión suficientemente seguros, pero eso corresponde al lector y va mas allá de lo que se puede tratar en este artículo.
Identificar al usuario que crea cada registro
Tal como hemos dejado las macros, son suficientes para asegurar una tabla, pero hemos afirmado que íbamos a dar un paso mas y lo vamos a hacer. Primero, vamos a identificar en cada registro qué usuario lo ha creado y. luego, vamos a añadir un tercer tipo de permiso, aparte de sí o no, que solo el usuario que ha creado el registro pueda editarlo o borrarlo.
Lo de identificar al usuario es sencillo. Tenemos que añadirle un campo mas a nuestra tabla, que llamaremos CreadoPor. En la macro añadimos una VariableLocal mas, [Usuario], y, si EsInsertar asignamos a CreadoPor el valor de [Usuario] utilizando la acción de macro EstablecerCampo. Así:
Pero surge una pega: cualquiera podría modificar el valor del campo CreadoPor y, por tanto editar o borrar el registro. Tenemos que evitarlo; si no EsInsertar y alguien intenta modificar el campo CreadoPor debemos provocar un error, pero ¿Cómo saber si se intenta modificar un campo concreto?
Función Actualizado
En la macro Antes del cambio la función Actualizado (“Nombre de campo”) nos dice si el campo cuyo nombre pasamos en una cadena como argumento se ha cambiado. Entonces, en nuestra macro podemos comprobar, si no EsInsertar, si Actualizado (“CreadoPor”) es verdadero y, en ese caso provocamos un error para evitarlo. La parte final de la macro (no cabe entera en la pantalla para copiarla) queda así:
Objeto Antiguo
Provocando un error hemos evitado que se cambie el valor de CreadoPor, pero resulta muy poco amable bloquear la edición de todo un registro porque el usuario ha intentado modificar un campo que no debía ¡A lo mejor ha sido sin querer! Lo que podemos hacer es, en vez de provocar el error, volver el valor del campo a su estado original, y para eso contamos con el objeto Antiguo. Basta con escribir Antiguo. en una expresión y el Intellisense nos muestra los campos a los que se puede aplicar
Si queremos se mas amables, podemos sustituir la acción de ProvocarError por una de EstablecerCampo en la que el valor original se sustituye por el Antiguo. El final de la macro modificada queda de la siguiente manera.
Y el código xml:
<?xml version=»1.0″ encoding=»UTF-16″ standalone=»no»?>
<DataMacros xmlns=»http://schemas.microsoft.com/office/accessservices/2009/11/application»><DataMacro Event=»BeforeChange»><Statements><LookUpRecord><Data Alias=»Permisos»><Reference>qPermisos</Reference><WhereCondition>[Tabla]=»Contactos»</WhereCondition></Data><Statements><Action Name=»SetLocalVar»><Argument Name=»Name»>PermisoEditar</Argument><Argument Name=»Value»>Nz([Permisos].[Editar])</Argument></Action><Action Name=»SetLocalVar»><Argument Name=»Name»>PermisoInsertar</Argument><Argument Name=»Value»>[permisos].[insertar]</Argument></Action><Action Name=»SetLocalVar»><Argument Name=»Name»>UsuarioActual</Argument><Argument Name=»Value»>[permisos].[Usuario]</Argument></Action></Statements></LookUpRecord><ConditionalBlock><If><Condition>[IsInsert]</Condition><Statements><ConditionalBlock><If><Condition>Nz([PermisoInsertar])<>True</Condition><Statements><Action Name=»RaiseError»><Argument Name=»Number»>4000</Argument><Argument Name=»Description»>Usted no tiene permisos para Insertar</Argument></Action></Statements></If></ConditionalBlock><Action Name=»SetField»><Argument Name=»Field»>CreadoPor</Argument><Argument Name=»Value»>[UsuarioActual]</Argument></Action></Statements></If><Else><Statements><ConditionalBlock><If><Condition>Nz([PermisoEditar])<>True</Condition><Statements><Action Name=»RaiseError»><Argument Name=»Number»>3000</Argument><Argument Name=»Description»>Usted no tiene permisos para Editar</Argument></Action></Statements></If></ConditionalBlock><ConditionalBlock><If><Condition>Updated(«Contactos.CreadoPor»)</Condition><Statements><Action Name=»SetField»><Argument Name=»Field»>CreadoPor</Argument><Argument Name=»Value»>[Old].[CreadoPor]</Argument></Action></Statements></If></ConditionalBlock></Statements></Else></ConditionalBlock></Statements></DataMacro></DataMacros>
Permitir cambios solo al usuario que ha creado el registro
No tiene mucho sentido identificar al usuario que ha creado el registro y que luego lo pueda editar cualquiera. Una alternativa es identificar también al usuario que modifica el registro, para lo necesitaríamos un nuevo campo, ModificadoPor, al que le daríamos valor de la misma manera que a CreadoPor, solo que cuando no sea EsInsertar (estaría bien añadir también campos de fecha para la creación y la modificación). Otra alternativa, que no es incompatible con la anterior, es crear un sistema de permisos en el que, en los que Borrar y Editar tengan valores equivalentes “Sí”, “No” y “Sólo los registros creados por mí”, que es lo que vamos a hacer ahora.
Empezamos por modificar la tabla de permisos, cambiando Editar y Borrar de Sí/No a numérico con los valores 0,1,2 (No,Sí y ‘Los propios’)
Ahora, en la macro tenemos que comprobar, si no es EsInsertar, si tiene permisos para editar todo o solo para editar los registros propios y, en ese caso, verificar el creador del registro.
La parte de la macro que comprueba los permisos para modificar queda así:
Y el código xml de la macro definitiva Antes del cmbio es el siguiente:
<?xml version=»1.0″ encoding=»UTF-16″ standalone=»no»?>
<DataMacros xmlns=»http://schemas.microsoft.com/office/accessservices/2009/11/application»><DataMacro Event=»BeforeChange»><Statements><LookUpRecord><Data Alias=»Permisos»><Reference>qPermisos</Reference><WhereCondition>[Tabla]=»Contactos»</WhereCondition></Data><Statements><Action Name=»SetLocalVar»><Argument Name=»Name»>PermisoEditar</Argument><Argument Name=»Value»>Nz([Permisos].[Editar])</Argument></Action><Action Name=»SetLocalVar»><Argument Name=»Name»>PermisoInsertar</Argument><Argument Name=»Value»>[permisos].[insertar]</Argument></Action><Action Name=»SetLocalVar»><Argument Name=»Name»>UsuarioActual</Argument><Argument Name=»Value»>[permisos].[Usuario]</Argument></Action></Statements></LookUpRecord><ConditionalBlock><If><Condition>[IsInsert]</Condition><Statements><ConditionalBlock><If><Condition>Nz([PermisoInsertar])<>True</Condition><Statements><Action Name=»RaiseError»><Argument Name=»Number»>4000</Argument><Argument Name=»Description»>Usted no tiene permisos para Insertar</Argument></Action></Statements></If></ConditionalBlock><Action Name=»SetField»><Argument Name=»Field»>CreadoPor</Argument><Argument Name=»Value»>[UsuarioActual]</Argument></Action></Statements></If><Else><Statements><ConditionalBlock><If><Condition>Nz([PermisoEditar])<>True</Condition><Statements><ConditionalBlock><If><Condition>[PermisoEditar]=0</Condition><Statements><Action Name=»RaiseError»><Argument Name=»Number»>3000</Argument><Argument Name=»Description»>Usted no tiene permisos para Editar</Argument></Action></Statements></If></ConditionalBlock><ConditionalBlock><If><Condition>[PermisoEditar]=2</Condition><Statements><ConditionalBlock><If><Condition>[Contactos].[CreadoPor]<>[usuarioActual]</Condition><Statements><Action Name=»RaiseError»><Argument Name=»Number»>6000</Argument><Argument Name=»Description»>Sólo puede modificar registros en la tabla ‘Contactos’ si es el creador del registro</Argument></Action></Statements></If></ConditionalBlock></Statements></If></ConditionalBlock></Statements></If></ConditionalBlock><ConditionalBlock><If><Condition>Updated(«Contactos.CreadoPor»)</Condition><Statements><Action Name=»SetField»><Argument Name=»Field»>CreadoPor</Argument><Argument Name=»Value»>[Old].[CreadoPor]</Argument></Action></Statements></If></ConditionalBlock></Statements></Else></ConditionalBlock></Statements></DataMacro></DataMacros>
Ya solo nos queda hacer los cambios en la macro Validar eliminación para que pueda permitir borrar los registros propios. Como es similar a lo que hemos visto, me limito a pegar el código xml.
<?xml version=»1.0″ encoding=»UTF-16″ standalone=»no»?>
<DataMacros xmlns=»http://schemas.microsoft.com/office/accessservices/2009/11/application»><DataMacro Event=»BeforeDelete»><Statements><ConditionalBlock><If><Condition>[Borrar]<>True</Condition><Statements><Action Name=»RaiseError»><Argument Name=»Number»>1000</Argument><Argument Name=»Description»>No se puede borrar sin haber marcado previamente el campo [Borrar]</Argument></Action></Statements></If></ConditionalBlock><LookUpRecord><Data Alias=»Permisos»><Reference>qPermisos</Reference><WhereCondition>[Tabla]=»Contactos»</WhereCondition></Data><Statements><Action Name=»SetLocalVar»><Argument Name=»Name»>PermisoBorrar</Argument><Argument Name=»Value»>Nz([Permisos].[Borrar])</Argument></Action><Action Name=»SetLocalVar»><Argument Name=»Name»>UsuarioActual</Argument><Argument Name=»Value»>Nz([Permisos].[Usuario])</Argument></Action></Statements></LookUpRecord><ConditionalBlock><If><Condition>Nz([PermisoBorrar])=False</Condition><Statements><Action Name=»RaiseError»><Argument Name=»Number»>2000</Argument><Argument Name=»Description»>Usted no tiene permisos para borrar</Argument></Action></Statements></If></ConditionalBlock><ConditionalBlock><If><Condition>[PermisoBorrar]=2</Condition><Statements><ConditionalBlock><If><Condition>[Contactos].[CreadoPor]<>[UsuarioActual]</Condition><Statements><Action Name=»RaiseError»><Argument Name=»Number»>5000</Argument><Argument Name=»Description»>Sólo puede borrar aquellos registros creados por vd.</Argument></Action></Statements></If></ConditionalBlock></Statements></If></ConditionalBlock></Statements></DataMacro></DataMacros>
Conclusión
Las macros en Access 2010 no serían difíciles si las utilizáramos con frecuencia, pero eso nunca va a ser ocurrir pues, para casi todo, es mas sencillo y mas cómodo utilizar VBA con el que, además, podemos llegar mucho mas lejos. Sin embargo, las macros de datos nos permiten hacer cosas impresionantes, impensables en versiones anteriores de Access e imposibles de hacer con código VBA, por lo tanto, merece la pena pagar el precio de trabajar, aunque sea torpemente, con estas macros pesadas y puñeteras e ir aprendiendo algo.
A las macros de datos les queda recorrido aparte de lo tratado aquí. No hemos vistos nada de las otras macros de datos, las de “después de”, ni de las macros de datos con nombre. Desde el punto de vista de la seguridad a nivel de usuario, nos permitirían, por ejemplo, hacer un seguimiento de cambios, con registro del usuario que los realiza y del valor anterior. Algún día, espero no tardar tanto como en esta ocasión, escribiré sobre ello.
One Responseso far
¡Necesitamos que nos ayudes un poco mas con las macros de datos y con un registro de cambios, por favor!