COPA [1] es un sistema desarrollado por RedIRIS [2] que permite establecer clasificaciones (introducir estructuras, típicamente jerárquicas) en sistemas de información allí donde la disposición lógica de los items no las reflejan, o donde se puede establecer en ellos distintas jerarquías según diferentes conceptos (en este documento usaremos los términos clasificación, jerarquía, vista y árbol de forma más o menos equivalente, aunque una clasificación puede tener un sólo nivel). También se puede usar COPA para mantener y gestionar grupos, expresar ontologías, etc.
Aunque COPA puede aplicarse en distintos ámbitos, nos centramos en este documento en su aplicación a LDAP, para lo cual se ha definido un esquema específico. La versión de COPA que vamos a explicar es la 2.1.0. LDAP es un ámbito de aplicación muy adecuado para COPA debido a que actualmente se acepta que la estructura del DIT debe ser lo más plana posible, y COPA nos permite expresar jerarquías en un contexto plano.
La documentación de COPA en su página Web está muy centrada en la utilización de interfaces de navegación por las distintas vistas que se definan. En este documento el énfasis se hace más en su utilización desde aplicaciones que necesiten la funcionalidad que permite.
Por ejemplo:urn:mace:rediris.es:<id-institucion>:classif:<id-clasificacion>:<version>:codigo
donde todo lo que precede a codigo es lo que llamamos prefijo.urn:mace:rediris.es:uco.es:classif:por_relacion:1.0:a1b2
Ése es el valor que se asignará a cada entrada en un atributo que típicamente será irisClassifCode.
Pero lo importante es el código en sí, que como hemos dicho refleja la estructura que se quiere reflejar. Los códigos de los que hablamos están compuestos de secuencias de conjuntos de una letra y uno o más dígitos, e incorporan en sí mismas información sobre la jerarquía. Así a1, a2, a3, ... son las etiquetas de los nodos (Áreas) del primer nivel. a1b1, a1b2, a1b3... son las de los hijos de a1. El nivel se expresa con la letra (a, b, c, ...) y los distintos nodos dentro del mismo nivel, con números tras la letra. Así a2b1c3 es el tercer elemento de los hijos del primer elemento de los hijos del segundo elemento del primer nivel.
a1 a2 a3 | +----+------+ a2b1 a2b2 a2b3 | +-------+-------+ a2b1c1 a2b1c2 a2b1c3Estos códigos no incluyen por tanto el nombre de los nodos. Si el uso de COPA es para aprovechar su funcionalidad desde aplicaciones ad-hoc, éstas incluirán el conocimiento sobre los mismos. Si queremos usar algún interfaz de navegación (como navega 2.0 [7] ) debemos declarar en una rama del DIT sus nombres, así como otros datos (ver el apartado dedicado a la navegación). Incluso en el caso de aplicaciones propias puede ser interesante declarar estos nombres.
Veamos un ejemplo que refleja la estructura de una Universidad, originado en la Universidad de León [5]:
Hay otras clasificaciones en [6]. La idea es tan simple como potente:a1 Centros a1b1 Aulario a1b2 Escuela Superior y Técnica de Ingeniería Agraria ... a2 Departamentos a2b1 Biología Animal a2b2 Biología Celular y Anatomía ... a3 Fundaciones a3b1 Fundación Perico de los Palotes ... a4 Gerencia a4b1 Area de Recursos Humanos a4b2 Biblioteca Universitaria y Archivo ... a5 Organos, Comisiones y Representaciones a5b1 Defensor de la Comunidad Universitaria a5b2 Programa Institucional de Calidad ... a6 Rectorado a6b1 Secretaría General a6b2 Vicerrectorado Del Campus XXX ... a7 Institutos ... a8 Consejo Social ...
Y podemos obtener la lista de todos los que pertenecen a algún departamento con:(irisClassifCode=*id-clasificacion*:a2b2)
(irisClassifCode=*id-clasificacion*:a2*)
Los elementos de una implementación típica de COPA con soporte para interfaces de navegación son:
El esquema COPA es relativamente difícil de entender a primera vista, quizá porque gran parte de él está orientado a facilitar el desarrollo de herramientas gráficas o basadas en Web que permitan la navegación por las distintas vistas o jerarquías, y para que éstas encuentren toda la información que necesitan de forma automática en el directorio y no tengan que ser configuradas. Si obviamos este uso y sólo vamos a usar COPA desde aplicaciones propias todo es bastante más sencillo, pues en este caso realmente no habría que usar el esquema COPA en absoluto, simplemente incluir en cada entrada del DIT que deba formar parte de una clasificación un código COPA en el atributo irisClassifCode (esquema Iris, clase irisObject) o copaCode (esquema COPA, clase copaResource).
Las clases de objeto y sus atributos son:
copaClassifBase | 1 | El DN de la base del subárbol del DIT que contiene los objetos del tipo copaArea que definen la jerarquía. |
copaPrefix | 1 | El prefijo del URN que se incluye en cada entrada del DIT y cuya última parte es el código COPA. En el apartado Conceptos hemos explicado su formato. Se usa entre otras cosas para distinguir a qué clasificación se refiere. |
copaName | 1 | Nombre de la clasificación (típicamente para mostrarlo en las aplicaciones de navegación). |
copaCodeAttr | 1 | Nombre del atributo de los objetos bajo copaClassifBase (que definen los nodos del árbol) donde se especifica su código COPA, sin el prefijo. Típicamente será copaAreaCode si esos objetos son del tipo copaArea. |
copaPrintAttr | 1 | Nombre del atributo de los objetos bajo copaClassifBase (que definen los nodos del árbol) donde se especifica el nombre del Área o nodo. Típicamente será copaName si esos objetos son del tipo copaArea. |
copaAreaObjectClassName | 1+ | Clase/s de los objetos bajo copaClassifBase. Típicamente copaArea. |
copaCodeResourceAttr | 1 | Nombre del atributo que habrá en cada entrada del DIT y donde se indica su código COPA, con el prefijo. Típicamente será irisClassifCode (esquema Iris, clase irisObject) o copaCode (esquema COPA, clase copaResource). |
copaRelatedAttr | 1 | ??? |
description | 1+ | Pues eso... |
copaDefVvNav | ? | Contiene el DN del objeto copaClassifBase que contiene la vista por defecto. |
copaVvNav | 0+ | Contiene los DNs del resto de objetos copaClassifBase que contienen otras vistas. |
copaAreaCode | 1 | El código COPA de un área o nodo del árbol, sin prefijo. |
copaName | 1 | El nombre de un área o nodo del árbol. |
description | 1+ | Descripción |
copaCode | 0+ | Permite asignar al nodo códigos COPA de otras jerarquías. |
copaCode | 1+ | El código COPA (URN con prefijo) que asociamos a cada entrada del DIT. Alternativa a irisClassifCode. |
copaExternalCode | 0+ | ??? |
Donde ? indica 0 ó 1 ocurrencia, 0+ indica 0 o n, 1+ uno o más y 1 exactamente una ocurrencia, obligatoria.
En primer lugar vamos a desarrollar una clasificación COPA que simplemente refleja la pertenencia de la persona a uno de esos tres estamentos (pueden ser más de uno). Para este caso no es necesario COPA, existen atributos bien establecidos, como eduPersonAffiliation, que sirven perfectamente para eso.
Posteriormente ampliaremos el ejemplo para contemplar una estructura jerárquica de posibles relaciones con la Universidad. De esta forma con un simple atributo en la entrada de cada persona se podrá hacer búsquedas más o menos específicas (todos los PAS, dentro de estos los laborales, o los funcionarios interinos, etc.), algo difícil de conseguir sin COPA.
Puesto que ya se han explicado los conceptos y el esquema de COPA y para no ser farragosos, mostramos simplemente el fragmento de fichero LDIF que contiene toda la información necesaria. Se necesitan los esquemas Iris [3] (para los atributos idnc y irisClassifCode) y COPA [4]:
1 ##################################################################### 2 ### Raiz y ramas 3 ##################################################################### 4 5 dn: dc=uco,dc=es 6 objectClass: top 7 objectClass: dcObject 8 objectClass: organization 9 objectClass: copaVirtualViewNav 10 o: uco.es 11 dc: uco 12 copaDefVvNav: idnc=por_relacion,ou=copa,dc=uco,dc=es 13 14 dn: ou=copa,dc=uco,dc=es 15 objectClass: top 16 objectClass: organizationalUnit 17 ou: copa 18 19 dn: ou=areas,ou=copa,dc=uco,dc=es 20 objectClass: top 21 objectClass: organizationalUnit 22 ou: areas 23 24 dn: ou=People,dc=uco,dc=es 25 objectClass: top 26 objectClass: organizationalUnit 27 ou: People 28 29 30 ##################################################################### 31 ### Configuracion de la jerarquia 32 ##################################################################### 33 34 dn: idnc=por_relacion,ou=copa,dc=uco,dc=es 35 objectclass: copaVirtualViewSpec 36 objectclass: irisObject 37 idnc: por_relacion 38 copaClassifBase: ou=por_relacion,ou=areas,ou=copa,dc=uco,dc=es 39 copaPrefix: urn:mace:rediris.es:uco.es:classif:por_relacion:v1 40 copaName: Por relacion 41 copaCodeAttr: copaAreaCode 42 copaPrintAttr: copaName 43 copaAreaObjectClassName: copaArea 44 copaCodeResourceAttr: irisClassifCode 45 description: Tipo de relacion con la Universidad 46 47 48 ##################################################################### 49 ### Rama especifica para las areas de esta jerarquia 50 ##################################################################### 51 52 dn: ou=por_relacion,ou=areas,ou=copa,dc=uco,dc=es 53 objectClass: top 54 objectClass: organizationalUnit 55 ou: por_relacion 56 57 58 ##################################################################### 59 ### Un objeto para cada area/nodo 60 ##################################################################### 61 62 dn: copaAreaCode=a1,ou=por_relacion,ou=areas,ou=copa,dc=uco,dc=es 63 objectclass: copaArea 64 copaAreaCode: a1 65 copaName: PAS 66 description: Personal de Administracion y Servicios 67 68 dn: copaAreaCode=a2,ou=por_relacion,ou=areas,ou=copa,dc=uco,dc=es 69 objectclass: copaArea 70 copaAreaCode: a2 71 copaName: PDI 72 description: Personal Docente e Investigador 73 74 dn: copaAreaCode=a3,ou=por_relacion,ou=areas,ou=copa,dc=uco,dc=es 75 objectclass: copaArea 76 copaAreaCode: a3 77 copaName: Alumnos 78 description: Alumnos, becarios, etc. 79 80 81 ##################################################################### 82 ### Lo mas importante: las personas... 83 ##################################################################### 84 85 dn: uid=rgomez,ou=People,dc=uco,dc=es 86 uid: rgomez 87 cn: Rafael Gomez 88 sn: Gomez 89 objectClass: inetorgperson 90 objectClass: irisObject 91 objectClass: top 92 irisClassifCode: urn:mace:rediris.es:uco.es:classif:por_relacion:v1:a1 93 94 dn: uid=aperez,ou=People,dc=uco,dc=es 95 uid: aperez 96 cn: Antonia Perez 97 sn: Perez 98 objectClass: inetorgperson 99 objectClass: irisObject 100 objectClass: top 101 irisClassifCode: urn:mace:rediris.es:uco.es:classif:por_relacion:v1:a1 102 irisClassifCode: urn:mace:rediris.es:uco.es:classif:por_relacion:v1:a2 |
En este caso concreto hemos creado un subárbol específico (ou=copa) para toda la información relativa a COPA (con la obvia excepción de los atributos que hay que incluir en la entrada de cada usuario mediante irisClassifCode y el atributo copaDefVvNav de la raíz).
Bajo esa rama creamos un objeto para la configuración de cada clasificación o vista (en este caso en la línea 34) y un subárbol (ou=areas) bajo el cual se crea otro para los objetos que definen los nodos de las clasificaciones (en este caso ver la línea 52 y siguientes). Esta es una posible organización, se puede optar por cualquier otra.
En la línea 12 vemos la vista por defecto, si hubiera otras se declararían mediante el atributo copaVvNav. Esto no es realmente necesario, pero sí muy conveniente para que el software de navegación por vistas pueda localizar automáticamente la información que necesita.
Vemos también que hay una persona que pertenece a PAS y PDI, por lo que en el caso de una navegación por vistas aparecerá en dos puntos distintos del árbol. Si tuviéramos definida otra vista (por cargos, por servicios, etc.) no habría conflicto al añadir más atributos irisClassifCode porque el prefijo de su valor sirve para distinguir entre ellas.
Localizar a todo el PAS es tan sencillo como:
ldapsearch -x -b "dc=uco,dc=es" "(irisClassifCode=*por_relacion*:a1)" |
Por supuesto se podría usar un atributo distinto para cada vista, simplificando un poco el filtro de búsqueda, pero no creo que merezca la pena pues complica el esquema.(&(irisClassifCode=*por_relacion*:a1)(irisClassifCode=*por_relacion*:a2))
Ahora vamos a establecer una jerarquía de relaciones con la Universidad e introducir otra vista, basada en la organización (en ambos casos muy sencillas al tratarse sólo de ejemplos).
1 2 ########################################################################## 3 ### Las ramas basicas 4 ########################################################################## 5 6 dn: dc=uco,dc=es 7 objectClass: top 8 objectClass: dcObject 9 objectClass: organization 10 objectClass: copaVirtualViewNav 11 o: uco.es 12 dc: uco 13 copaDefVvNav: idnc=por_relacion,ou=copa,dc=uco,dc=es 14 copaVvNav: idnc=por_organizacion,ou=copa,dc=uco,dc=es 15 16 dn: ou=copa,dc=uco,dc=es 17 objectClass: top 18 objectClass: organizationalUnit 19 ou: copa 20 21 dn: ou=People,dc=uco,dc=es 22 objectClass: top 23 objectClass: organizationalUnit 24 ou: People 25 26 dn: ou=areas,ou=copa,dc=uco,dc=es 27 objectClass: top 28 objectClass: organizationalUnit 29 ou: areas 30 31 32 ########################################################################## 33 ### Los objetos que definen las vistas 34 ########################################################################## 35 36 dn: idnc=por_relacion,ou=copa,dc=uco,dc=es 37 objectclass: copaVirtualViewSpec 38 objectclass: irisObject 39 idnc: por_relacion 40 copaClassifBase: ou=por_relacion,ou=areas,ou=copa,dc=uco,dc=es 41 copaPrefix: urn:mace:rediris.es:uco.es:virtualview:por_relacion:v1 42 copaName: Por relacion 43 copaCodeAttr: copaAreaCode 44 copaPrintAttr: copaName 45 copaAreaObjectClassName: copaArea 46 copaCodeResourceAttr: irisClassifCode 47 description: Tipo de relacion con la Universidad 48 49 dn: idnc=por_organizacion,ou=copa,dc=uco,dc=es 50 objectclass: copaVirtualViewSpec 51 objectclass: irisObject 52 idnc: por_organizacion 53 copaClassifBase: ou=por_organizacion,ou=areas,ou=copa,dc=uco,dc=es 54 copaPrefix: urn:mace:rediris.es:uco.es:virtualview:por_organizacion:v1 55 copaName: Por organizacion 56 copaCodeAttr: copaAreaCode 57 copaPrintAttr: copaName 58 copaAreaObjectClassName: copaArea 59 copaCodeResourceAttr: irisClassifCode 60 description: Organizacion dentro de la Universidad 61 62 63 ########################################################################## 64 ### La rama que define la vista por relacion con la Univ. 65 ########################################################################## 66 67 dn: ou=por_relacion,ou=areas,ou=copa,dc=uco,dc=es 68 objectClass: top 69 objectClass: organizationalUnit 70 ou: por_relacion 71 72 dn: copaAreaCode=a1,ou=por_relacion,ou=areas,ou=copa,dc=uco,dc=es 73 objectclass: copaArea 74 copaAreaCode: a1 75 copaName: PAS 76 description: Personal de Administracion y Servicios 77 78 dn: copaAreaCode=a1b1,ou=por_relacion,ou=areas,ou=copa,dc=uco,dc=es 79 objectclass: copaArea 80 copaAreaCode: a1b1 81 copaName: Laboral 82 description: Personal laboral 83 84 dn: copaAreaCode=a1b2,ou=por_relacion,ou=areas,ou=copa,dc=uco,dc=es 85 objectclass: copaArea 86 copaAreaCode: a1b2 87 copaName: Funcionario 88 description: Personal funcionario 89 90 dn: copaAreaCode=a1b2c1,ou=por_relacion,ou=areas,ou=copa,dc=uco,dc=es 91 objectclass: copaArea 92 copaAreaCode: a1b2c1 93 copaName: Funcionario fijo 94 description: Personal funcionario fijo 95 96 # ... 97 98 ########################################################################## 99 ### La rama que define la vista por organizacion 100 ########################################################################## 101 102 dn: ou=por_organizacion,ou=areas,ou=copa,dc=uco,dc=es 103 objectClass: top 104 objectClass: organizationalUnit 105 ou: por_organizacion 106 107 dn: copaAreaCode=a1,ou=por_organizacion,ou=areas,ou=copa,dc=uco,dc=es 108 objectclass: copaArea 109 copaAreaCode: a1 110 copaName: Servicios 111 description: Servicios universitarios 112 113 dn: copaAreaCode=a1b1,ou=por_organizacion,ou=areas,ou=copa,dc=uco,dc=es 114 objectclass: copaArea 115 copaAreaCode: a1b1 116 copaName: Informatica 117 description: Servicio de Informatica 118 119 dn: copaAreaCode=a1b2,ou=por_organizacion,ou=areas,ou=copa,dc=uco,dc=es 120 objectclass: copaArea 121 copaAreaCode: a1b2 122 copaName: Personal 123 description: Servicio de Personal 124 125 # ... 126 127 ########################################################################## 128 ### Las personas... 129 ########################################################################## 130 131 dn: uid=rgomez,ou=People,dc=uco,dc=es 132 uid: rgomez 133 cn: Rafael Gomez 134 sn: Gomez 135 objectClass: inetorgperson 136 objectClass: irisObject 137 objectClass: top 138 irisClassifCode: urn:mace:rediris.es:uco.es:virtualview:por_relacion:v1:a1b2c2 139 irisClassifCode: urn:mace:rediris.es:uco.es:virtualview:por_organizacion:v1:a1b1 140 141 dn: uid=aperez,ou=People,dc=uco,dc=es 142 uid: aperez 143 cn: Antonia Perez 144 sn: Perez 145 objectClass: inetorgperson 146 objectClass: irisObject 147 objectClass: top 148 irisClassifCode: urn:mace:rediris.es:uco.es:virtualview:por_relacion:v1:a1b2c1 149 irisClassifCode: urn:mace:rediris.es:uco.es:virtualview:por_relacion:v1:a2b3 150 irisClassifCode: urn:mace:rediris.es:uco.es:virtualview:por_organizacion:v1:a1b1 151 irisClassifCode: urn:mace:rediris.es:uco.es:virtualview:por_organizacion:v1:a2b2 |
Por relación:
a1 Personal de Administracion y Servicios a1b1 Personal laboral a1b2 Personal funcionario a1b2c1 Personal funcionario fijo a1b2c2 Personal funcionario interino a2 Personal Docente e Investigador a2b1 Catedratico a2b2 Titular de Universidad a2b3 Ayudante a3 Alumnos, becarios, etc. a3b1 Estudiante de ensenanzas regladas a3b2 Estudiante de ensenanzas no regladas a3b3 Becario a3b3c1 Becario FPI a3b3c2 Becario Erasmus
Por organización:
a1 Servicios universitarios a1b1 Servicio de Informatica a1b2 Servicio de Personal a1b3 Servicio de Deportes a2 Departamentos universitarios a2b1 Departamento de Filosofia a2b2 Departamento de Informatica
Saber si una persona es PAS, PDI o alumno se reduce a buscar en su entrada usando el filtro "(irisClassifCode=*por_relacion*)", obtener la parte tras el último ':' y de ahí la parte antes de la primera 'b'. Puede parecer farragoso, pero no cuesta mucho hacer unas pequeñas funciones para realizar esas tareas y usarlas en todas las aplicaciones.
Se puede obtener igualmente el nombre del colectivo al que pertenece una persona obteniendo su irisClassifCode, elimimando el prefijo y buscando lo que queda en copaAreaCode bajo ou=por_relacion,ou=areas,ou=copa,dc=uco,dc=es, por ejemplo. Igualmente se puede obtener el nombre de sus supercolectivos parseando fácilmente su código y separándolo en sus componentes.
Localizar los distintos tipos de alumnos, becarios, etc:
Como ejemplo final, podemos localizar a un profesor de apellido Ventura del Dpto. de Informática:ldapsearch -x -b "ou=por_relacion,ou=areas,ou=copa,dc=uco,dc=es" '(&(copaAreaCode=*a3b*)(!(copaAreaCode=*a3b*c*)))'
ldapsearch -x -b "dc=uco,dc=es" '(&(cn=*ventura*)(irisClassifCode=*por_organizacion*a2b2))'
[1] - http://www.rediris.es/ldap/copa
[2] - http://www.rediris.es
[3] - http://www.rediris.es/ldap/esquemas/iris.schema
[4] - http://www.rediris.es/ldap/schema/copa.schema
[5] - http://www.rediris.es/ldap/copa/clasif/copaUniv.txt
[6] - http://www.rediris.es/ldap/copa/clasif/
[7] - https://forja.rediris.es/projects/siledap/