%myvars; ]> Sistema de Permisos de OpenACS Tediosamente Explicado por Vadim Nasardinov. Modificado y convertido a Docbook XML por Roberto Mello. Traducido por David Arroyo. Introducción En OpenACS 3.x se consiguió tener un sistema reutilizable de control de permisos y poder contestar a la pregunta clave quié puede hacer qué en tal objeto. Sin embargo, no se consiguió que este control de permisos estuviera realmente unificado. En ocasiones, el control de permisos se construía en base a cada módulo/paquete, ó incluso en base a cada p´gina. De este modo, algunos módulos usaban "roles" y otros no. Otros módulos hacían todo el control de acceso basándose en simples reglas de código. Los problemas resultantes de esto fueron sobre todo inconsistencias y código redundante. De este modo, en OpenACS 4 se busca proporcionar un unificado y consistente sistema de permisos para que tanto programadores y administradores puedan usarlo de manera amigable. En OpenACS 4 la pregunta quién puede hacer qué en tal objeto, el "quién" se responde a través de la jerarquía de party (la generalización de personas, usuarios, miembros de un grupo, etc.), el "tal objeto" también se establece a través de la jerarquía de objetos y por último el "qué" se establece a travé de una jerarquía de posibles acciones. Ahora iremos aclarando todos estos conceptos. En el corazón del sistema de permisos tenemos dos tablas: acs_privileges y acs_permissions: create table acs_privileges ( privilege varchar2(100) not null constraint acs_privileges_pk primary key, pretty_name varchar2(100), pretty_plural varchar2(100) ); create table acs_permissions ( object_id not null constraint acs_permissions_on_what_id_fk references acs_objects (object_id), grantee_id not null constraint acs_permissions_grantee_id_fk references parties (party_id), privilege not null constraint acs_permissions_priv_fk references acs_privileges (privilege), constraint acs_permissions_pk primary key (object_id, grantee_id, privilege) ); La tabla acs_privileges almacena el propio nombre de los privilegios como leer, escribir, borrar, crear y administrar. La tabla acs_permissions responde a la ya famosa pregunta clave quién (grantee_id) puede hacer qué (privilege) en tal objeto (object_id). Ahora vamos a profundizar cómo funciona el sistema de permisos a través de la jerarquía de objetos, de parties y de privilegios. Jerarquía de Objetos La tabla acs_objects se crea de la siguiente manera: create table acs_objects ( object_id integer not null constraint acs_objects_pk primary key, object_type not null constraint acs_objects_object_type_fk references acs_object_types (object_type), context_id constraint acs_objects_context_id_fk references acs_objects(object_id), security_inherit_p char(1) default 't' not null, constraint acs_objects_sec_inherit_p_ck check (security_inherit_p in ('t', 'f')), creation_user integer, creation_date date default sysdate not null, creation_ip varchar2(50), last_modified date default sysdate not null, modifying_user integer, modifying_ip varchar2(50), constraint acs_objects_context_object_un unique (context_id, object_id) disable ); De este modo, supongámos que los objetos A, B, ..., F tienen la siguiente jerarquía: Aobject_id=10 Bobject_id=20 Cobject_id=30 Dobject_id=40 Eobject_id=50 Fobject_id=60
Esto podría ser representado en de la siguiente manera: object_id context_id 20 10 30 10 40 20 50 20 60 30
Así se expresa que el objeto 20 es descendiente del objeto 10 y que el objeto 40 es descendiente del objeto 10, etc. Mediante una consulta CONNECT BY es posible computar que el objeto 40 es descendiente de segunda generación del objeto 10. Con esto en mente si nosotros queremos grabar que Juan tiene permisos de lectura en los objetos A,...,F, solo necesitamos introducir el siguiente registro en la tabla . Instancia en acs_permissions object grantee privilege A Juan read
El hecho de Juan también puede leer B,C,...,F puede ser deducido determinando que estos objetos son hijos de A en la jerarquía de objetos. El coste computacional de estas consultas en la jerarquía es bastante costoso. Una manera de solucionar esto podría ser una delgada vista del árbol de contexto como esto: object ancestor n_generations A A 0 B B 0 C C 0 C A 1 D D 0 D B 1 D A 2 E E 0 E B 1 E A 2 F F 0 F C 1 F A 2
La solución de crear una vista tampoco es válida debido a que crece exponecialmente con respecto a la profundidad del árbol de contexto, dando graves problemas de almacenamiento y mantenimiento. Finalmente, el árbol de contexto es almacenado en la tabla acs_object_context_index: create table acs_object_context_index ( object_id not null constraint acs_obj_context_idx_obj_id_fk references (object_id), ancestor_id not null constraint acs_obj_context_idx_anc_id_fk references (object_id), n_generations integer not null constraint acs_obj_context_idx_n_gen_ck check (n_generations >= 0), constraint acs_object_context_index_pk primary key (object_id, ancestor_id) ) organization index; Esta tabla se sincroniza con mediante triggers como este: create or replace trigger acs_objects_context_id_in_tr after insert on acs_objects for each row begin insert into acs_object_context_index (object_id, ancestor_id, n_generations) values (:new.object_id, :new.object_id, 0); if :new.context_id is not null and :new.security_inherit_p = 't' then insert into acs_object_context_index (object_id, ancestor_id, n_generations) select :new.object_id as object_id, ancestor_id, n_generations + 1 as n_generations from acs_object_context_index where object_id = :new.context_id; elsif :new.object_id != 0 then -- 0 is the id of the security context root object insert into acs_object_context_index (object_id, ancestor_id, n_generations) values (:new.object_id, 0, 1); end if; end; Para finalizar con tan solo decir que si configuramos un objeto con el campo security_inherit_p a 'f', de ese modo no hay herencia de permisos de unos objetos a otros.
Jerarquía de Privilegios Los privilegios también son organizados jerárquicamente. Además de los cinco principales privilegios de sistema definidos en el ACS Kernel Data Model, los desarrolladores de aplicaciones pueden definir los suyos propios. Gracias al modelo de datos de OpenACS es sencillo para los desarrolladores administrar permisos. Por ejemplo, para darle a un usuario privilegios de lectura, escritura, creación y borrado en un objeto, basta con darle a un usuario el privilegio administración (admin)y los otros cuatro privilegios le serán dados automáticamente. Por ejemplo, la estructura de privilegios de los foros es la siguiente: Jerarquía de privilegios de los foros admin create delete read write moderate forum create category create forum create message delete category delete forum delete message read category read forum read message write category write forum write message
Al igual que en la jerarquía de objetos, es bueno tener una representación integrada de la estructura jerárquica. Esto se consigue definiendo la siguiente vista: create or replace view acs_privilege_descendant_map as select p1.privilege, p2.privilege as descendant from p1, p2, where p2.privilege in (select child_privilege from start with privilege = p1.privilege connect by prior child_privilege = privilege ) or p2.privilege = p1.privilege; Como el número esperado de privilegios en el sistema es razonablemente pequeño una vista funciona bien.
Jerarquía de parties Veamos ahora la tercera jerarquía que juega un papel importante en el sistema de permisos. El modelo de datos de parties es el siguiente: parties persons groups users
create table parties ( party_id not null constraint parties_party_id_fk references acs_objects (object_id) constraint parties_party_id_pk primary key, email varchar2(100) constraint parties_email_un unique, url varchar2(200) ); create table persons ( person_id not null constraint persons_person_id_fk references parties (party_id) constraint persons_person_id_pk primary key, first_names varchar2(100) not null, last_name varchar2(100) not null ); create table users ( user_id not null constraint users_user_id_fk references persons (person_id) constraint users_user_id_pk primary key, password char(40), -- other attributes ); create table groups ( group_id not null constraint groups_group_id_fk references parties (party_id) constraint groups_group_id_pk primary key, group_name varchar2(100) not null ); Recuerda que el campo grantee_id de la tabla referencia a parties.party_id. Esto significa que tu puedes dar privilegios en un objeto a una party, persona, usuario, o grupo. Los grupos representan agregaciones de parties. En general, los grupos son una colección de usuarios, aunque podemos tener colecciones de personas, grupos, parties, ó cualquier mezcla entre unos y otros. Dado que el uso más común de los grupos es para hacer conjuntos de usuarios ¿cómo construiremos los grupos?. Para entender esto debemos echar un rápido vistazo a lo ya explicado en el y recordar que la manera en que se relacionan objetos es mediante acs_rels. La relación que utilizaremos será la de membresía. Si tenemos un grupo llamado Papiroflexia, podemos asingnarle los miembros Pedro, María y Sara. El hecho de que estos usuarios son miembros de Papiroflexia será almacenada en las tablas membership_rels y acs_rels: create table acs_rels ( rel_id not null constraint acs_rels_rel_id_fk references (object_id) constraint acs_rels_pk primary key, rel_type not null constraint acs_rels_rel_type_fk references acs_rel_types (rel_type), object_id_one not null constraint acs_object_rels_one_fk references (object_id), object_id_two not null constraint acs_object_rels_two_fk references (object_id), constraint acs_object_rels_un unique (rel_type, object_id_one, object_id_two) ); create table membership_rels ( rel_id constraint membership_rel_rel_id_fk references (rel_id) constraint membership_rel_rel_id_pk primary key, -- null means waiting for admin approval member_state varchar2(20) constraint membership_rel_mem_ck check (member_state in ('approved', 'banned', 'rejected', 'deleted')) ); Las entradas en la tabla serían de la siguiente manera: Instancia en acs_rel rel_type object_one object_two membership_rel Papiroflexia Pedro membership_rel Papiroflexia María membership_rel Papiroflexia Sara
Otra manera de crear grupos es añadiendo subgrupos. Supón que nosotros definimos Papiroflexia China y Papiroflexia Japonesa como subgrupos de Papiroflexia. Esta información es guardada en las tablas y composition_rels: create table composition_rels ( rel_id constraint composition_rels_rel_id_fk references (rel_id) constraint composition_rels_rel_id_pk primary key ); Las entradas que nos interesan serían de la siguiente manera: Instancia en acs_rel rel_type object_one object_two composition_rel Papiroflexia Papiroflexia China composition_rel Papiroflexia Papiroflexia Japonesa
El significado de la relación de composición implica que si añdimos a Marcos, Teresa y Pablo a Papiroflexia China también los añdimos a Papiroflexia. Así, el modo de determinar si un usuario es miembro de un grupo es similar al problema de determinar si un objeto a un determinado contexto en la jerarquía. La relación de composición puede formar jerarquías entre los grupos debido a que esta es transitiva. De nuevo, las búsquedas jeráquicas son costosas. En este caso lo mejor es hacer una cache de resultados de consultas mediante una tabla mantenida por triggers de la misma manera que lo hicimos en la jerarquía de objetos y contextos. El modelo de datos de Open ACS 4.X define las siguientes tablas: create table group_component_index ( group_id not null constraint group_comp_index_group_id_fk references (group_id), component_id not null constraint group_comp_index_comp_id_fk references (group_id), rel_id not null constraint group_comp_index_rel_id_fk references (rel_id), container_id not null constraint group_comp_index_cont_id_ck references (group_id), constraint group_component_index_ck check (group_id != component_id), constraint group_component_index_pk primary key (group_id, component_id, rel_id) ) organization index; create table group_member_index ( group_id not null constraint group_member_index_grp_id_fk references (group_id), member_id not null constraint group_member_index_mem_id_fk references (party_id), rel_id not null constraint group_member_index_rel_id_fk references (rel_id), container_id not null constraint group_member_index_cont_id_fk references (group_id), constraint group_member_index_pk primary key (member_id, group_id, rel_id) ) organization index;