domingo, 27 de noviembre de 2011

Curso Django Primera Parte

Hace tiempo que quiero hacer un pequeño curso-manual sobre django. En esta primera parte comenzaremos con el software base para llevarlo a la práctica. Espero que una vez terminado quede suficientemente claro y sobre todo, que sea de utilidad.

Software base:
- Debian 6.0.3
- Python
- Python setuptools
- gcc
- python-imaging
- Mysql
- python-mysqldb
- apache2
- Django-1.3.1.tar.gz -> https://www.djangoproject.com/download/
- digi604-django-smart-selects-1.0.4-0-ga8f9eb9.zip -> https://github.com/digi604/django-smart-selects
Hay un bug (https://github.com/digi604/django-smart-selects/issues/27) que se corrige con el parche que podemos encontrar en: https://github.com/GrAndSE/django-smart-selects/commit/7e2a8d572b3615cc39a3c9f9d60e1b60de06f46f
Habría que actualizar el fichero widgets.py en la carpeta smart_selects, tras esto instalar django-smart-selects.
- django-command-extensions -> svn checkout http://django-command-extensions.googlecode.com/svn/trunk/ django-command-extensions.
- Imaging-1.1.7.tar.gz -> http://www.pythonware.com/products/pil/
- python-pygraphviz

Con esto tendremos todo lo necesario para llevar a cabo el proyecto que vamos a crear en este manual, que será una aplicación de inventario de software.

Para su instalación sobre debian lo haremos con apt-get install, salvo aquellos que nos tengamos que descargar de su propia web, y que normalmente se instalan o bien compilando o bien con el instalador de python (python setup.py install).

Una vez tengamos instalado todo el software, crearemos nuestra bbdd y el usuario necesario, por lo tanto nos conectaremos a nuestra bbdd mysql y lanzaremos las siguientes sentencias:

mysql> create database programas;
mysql> grant all on programas.* to 'programas'@'localhost' identified by 'programas' with grant option;
mysql> grant all on programas.* to 'programas'@'localhost.localdomain' identified by 'programas' with grant option;

 Con esto creamos una bbdd llamada programa, con un usuario y pass programa.
 Ya tenemos todo listo para comenzar a crear nuestro primer proyecto en django.

Nos crearemos un directorio donde emplazar nuestro proyecto, por ejemplo:

mkdir /opt/djcode

Y nos situamos sobre este directorio. Una vez allí crearemos nuestro proyecto:

django-admin.py startproject misitio

Veamos qué ha ocurrido, si hacemos un listado del directorio /opt/djcode descubriremos un nuevo directorio llamado misitio, y si listamos éste a su vez, tendremos algo como esto:

__init__.py
manage.py
settings.py
urls.py

Estos ficheros compondrán el esqueleto de nuestro proyecto. Estos archivos son los siguientes:
__init__.py: Un archivo requerido para que Python trate a este directorio como un paquete (i.e. un grupo de módulos).
manage.py: Una utilidad de línea de comandos que te deja interactuar con este proyecto de Django de varias formas.
settings.py: Opciones/con guraciones para este proyecto de Django.
urls.py: La declaración de las URL para este proyecto de Django; una tabla de contenidos de tu sitio hecho con Django.

Veamos que hemos conseguido con esto, sin tocar ninguno de los fichero anteriores. Lanzaremos el servidor de desarrollos de django de la siguiente manera:

python manage.py runserver 192.168.48.129:8000

Lo que añadimos a continuación de runserver es la ip y puerto por el que queremos que se levante el servidor, y nos debe aparecer algo así:

Validating models...

0 errors found
Django version 1.3.1, using settings 'misitio.settings'
Development server is running at http://192.168.48.129:8000/
Quit the server with CONTROL-C.

Y si nos vamos a un navegador y probamos esta dirección, veremos un bonito mensaje de bienvenida.


Hasta aquí esta primera parte. En la segunda parte comenzaremos a ver cómo crear nuestros modelos y algo de la parte de administración de django, una verdadera joya.

Enlace a la 2ª partes: http://rafabono.blogspot.com.es/2012/06/curso-django-segunda-parte.html

viernes, 4 de noviembre de 2011

Road Salt Two

El nuevo disco de uno de mis grupos favoritos: Pain of Salvation. Obligada escucha del disco, genial, con una intro que te lleva a recordar el "Within You Without You" de The Beatles.

 Tracklist:

1. Road Salt Theme
2. Softly She Cries
3. Conditioned
4. Healing Now
5. To The Shoreline
6. Eleven
7. 1979
8. The Deeper Cut
9. Mortar Grind
10. Through The Distance
11. The Physics Of Gridlock
12. End Credits
 Página Oficial: http://www.painofsalvation.com/

viernes, 28 de octubre de 2011

Resetear pass de admin en Django

Para resetear la password de admin en Django en caso de olvido:

Entramos en la shell:

python manage.py shell

Aparecerá la siguiente interfaz de comandos:

Python 2.7.1+ (r271:86832, Apr 11 2011, 18:05:24)
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>



Una vez dentro ejecutaremos los siguientes comandos:

>>> from django.contrib.auth.models import User
>>> users = User.objects.all()
>>> u = users[0]
>>> u.set_password(‘la clave que queramos’)
>>> u.save()



Y listo, ya tenemos nuestra nueva password.

jueves, 27 de octubre de 2011

Error en ejecución de script en Alfresco

Hace tiempo publiqué una entrada con una serie de webscripts para facilitar ciertas tareas de administración de Alfresco: Pantalla de Administración para Alfresco.

Pues hace poco tuve que cambiar el tipo de autenticación en uno de los webscripts para permitir su uso a usuarios no adminstradores, para ellos cambié la autenticación a user. Fué entonces cuando surgió el problema. Para ciertos grupos, al lanzar el script me devolvía el siguiente mensaje:

500 Description: Un error en el servidor HTTP, el cual le ha impedido cumplir con la solicitud.

Message: 09246007631 Wrapped Exception (with status template): 09246007630 Failed to execute script '/org/alfresco/sample/prueba.post.js (in repository store workspace://SpacesStore/Empresa/Diccionario de datos/Web Scripts)': 09246007629 Acceso denegado. No tiene los permisos apropiados para realizar esta operación.

Exception: net.sf.acegisecurity.AccessDeniedException - Access Denied

Traceando el script me dí cuenta que el error se obtenía en el método: 
 lista = people.getMembers(grupoObtenido, false);

Comparando grupos en los que me daba el error encontré un usuario en común. Al quitar dicho usuario del grupo, el script volvía a correr adecuadamente. Por tanto el error lo estaba provocando dicho usuario.

Pasé a investigar las propiedades de dicho usuario a través del explorador de nodos y me percaté que el usuario poseía un permiso que el resto no tenía:
All ROLE_OWNER ALLOWED

Al tener el ROLE_OWNER, no permitía el acceso al resto de usuarios a obtener sus propiedades (razón por la que saltaba el error en el método getMembers).

Para reproducir el error, prueba a añadir ese permiso a un usuario de otro grupo. Esto lo podemos hacer a través de javascript:

usuario.setPermission("All", "ROLE_OWNER");

El efecto fue el esperado, al lanzar el script con este nuevo grupo saltó el error. Para corregirlo procedí a eliminar el permiso:

usuario.removePermission("All", "ROLE_OWNER");

Sin embargo, cuando volví a lanzar el script, seguía dando el error. Por algún motivo se había perdido un permiso que heredan todos los usuarios:

"Read", "GROUP_EVERYONE"

aunque aparentemente la herencia la tenía activada. Le otorgué este permiso:

usuario.setPermission("Read", "GROUP_EVERYONE");

y al lanzar nuevamente el script, ya corría adecuadamente.

Por algún motivo, al eliminar el permiso de ROLE_OWNER a través de javascript, la herencia de GROUP_EVERYONE se pierde. ¿Se trata de algún bug o estoy utilizando de forma incorrecta los permisos?.

De todos modos, está claro que para lanzar la mayoría de los scripts de administración, lo mejor es tener permisos de administrador, por lo que la solución que mejor se adapta a esto (y evitar este tipo de problemas) es configurar la autenticación con user, pero lanzar los scripts con la directiva runas:

<authentication runas="admin">user</authentication>
 
 Para poder utilizar esta directiva, los webscripts deben guardarse en el Java Class path.

domingo, 23 de octubre de 2011

Django y AJAX

Llevo unos meses utilizando el framework Django y me parece una maravilla. En la última aplicación que estoy realizando me he encontrado con un pequeño problema a la hora de utilizar en una plantilla tecnología AJAX. Concretamente el problema con el que me he topado ha sido para recoger unos datos mediante jQuery con un post. Tengo dos select combinados, de tal forma que al hacer la selección del primero, los datos del segundo se actualizan dependiendo del valor del primero.

Pues debido a la protección que trae Django para CSFR (Cross Site Request Forgery), la llamada post que hago para traerme los datos en JSON no es válida, ya que no estaba mandando el token válido para CSFR.

La solución, la propia documentación de Django te la da.

Básicamente se trata de meter este código en nuestro template:

$(document).ajaxSend(function(event, xhr, settings) {
    function getCookie(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    function sameOrigin(url) {
        // url could be relative or scheme relative or absolute
        var host = document.location.host; // host + port
        var protocol = document.location.protocol;
        var sr_origin = '//' + host;
        var origin = protocol + sr_origin;
        // Allow absolute or scheme relative URLs to same origin
        return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
            (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
            // or any other URL that isn't scheme relative or absolute i.e relative.
            !(/^(\/\/|http:|https:).*/.test(url));
    }
    function safeMethod(method) {
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }

    if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
    }
}); 


Tengo pendiente escribir un post con un pequeño manual de Django, donde explicar las nociones más básicas y crear un pequeño proyecto.

miércoles, 21 de septiembre de 2011

Control de procesos

Gracias a supervisor, podremos controlar de manera fácil y precisa los procesos que queramos. Presentaré cómo instalarlo sobre Ubuntu y un pequeño ejemplo de uso.

Lo primero que haremos será instalarlo desde el repositorio:

sudo apt-get install supervisor

Lo más probable es que nos de un error al no encontrar una dependencia: elementtree. Por tanto lo que haremos es desinstalarlo:

sudo apt-get remove supervisor

e instalar la dependencia:

sudo easy_install elementtree

Tras esto ya podremos instalar supervisor

sudo apt-get install supervisor

Bien, ya lo tenemos instalado y ejecutando, pero sin hacer nada útil, así que vamos a modificar el fichero de configuración que trae por defecto y que se encuentra en /etc/supervisor/supervisord.conf, añadiendo las siguientes entradas:

;activamos la interfaz web
[inet_http_server] ; inet (TCP) server disabled by default
port=*:9001 ; (ip_address:port specifier, *:port for all iface)
username=admin ; (default is no username (open server))
password=admin ; (default is no password (open server))

;añadimos un proceso a controlar
[program:cat]
command=/bin/cat ; the program (relative uses PATH, can take args)
process_name=%(program_name)s ; process_name expr (default %(program_name)s)
numprocs=1 ; number of processes copies to start (def 1)
directory=/tmp ; directory to cwd to before exec (def no cwd)


Existen muchas más opciones, así que lo mejor es que echéis un vistazo a la página oficial.

Una vez hechos los cambios, reiniciaremos supervisor:

sudo /etc/init.d/supervisor stop; /etc/init.d/supervisor start

Y podremos comprobar su funcionamiento accediendo a la url: http://localhost:9001

Desde aquí podemos parar/arrancar el proceso o ver el log (si escribe algún log y lo hemos configurado en superfisord.conf).

Ahora veamos si funciona realmente. Comprobaremos si nuestro proceso (un simple cat) está arrancado por supervisor:


Efectivamente, ahí está activo, con PID 3702 (al igual que vimos en la interfaz web).

Matemos el proceso con un kill -9 3702 y volvamos a hacer un ps:



Y ahí lo volvemos a tener, con un PID nuevo, como podremos comprobar desde la interfaz web.


Además de esto, supervisor tiene un modo consola muy útil que nos permitirá ver el estado de nuestros procesos y hacer con ellos prácticamente de todo. Tan sólo tenemos que entrar supervisorctl:


Si escribimos help, tendremos todas las opciones que nos ofrece.


Pues nada más, ahora a buscarle utilidad a esta gran herramienta.

lunes, 12 de septiembre de 2011

Controla el estado de la memoria en aplicaciones Java

En muchas ocasiones nos encontramos con aplicaciones que tienen leak de memoria, y se va consumiendo este recurso sin que sea liberado adecuadamente. La solución: corregir el código, que suele ser el principal origen del problema. Pero cuando eso no depende de ti y tienes que ofrecer disponibilidad en dicha aplicación, una posible solución es controlar el consumo que hace la aplicación de la memoria, y en función de esta, realizar un reinicio antes de su degradación.

Aquí dejo un script que puede resultar útil. Se basa en controlar como se consume la memoria en las zonas Eden y Old Generation. Al haber una fuga de memoria, la zona Old Generation no se liberará como debe, y la zona Eden se consumirá también, imposibilitando la creación de nuevos objetos.

El script lo que hace es controlar estas zonas y cuando se alcance cierto umbral durante cierto número de ciclos consecutivos, reiniciar la aplicación (evitamos que la aplicación se acabe degradando y que al final la tengamos que reiniciar a mano):



#!/bin/bash

#Se obtiene el PID del proceso java utilizando jps y filtrando por Bootstrap
PID=`jps | grep Bootstrap | cut -d " " -f 1`
DATE=`date +%d-%m-%y----%H:%M:%S`
#Log para registrar las acciones del propio script
LOG="/scripts/reinicio_mem.log"
#Fichero temporal
TMP="/tmp/valores.txt"
#Se lanza el jmap -heap para obtener los valores del Eden y Old Generation
su - tomcat -c "jmap -heap $PID | grep -A4 -e Eden -e Old | grep % | cut -d '%' -f 1" > $TMP
CONT=1
FICH_CONTADOR="/tmp/contador"
FICH_SEGUIDO="/tmp/seguido"
#Script para levantar tomcat
SCRIPT_ARRANQUE="/scripts/arranque_tomcat.sh"
#Script para parar tomcat
SCRIPT_PARADA="/scripts/parada_tomcat.sh"
#Script para el envío de correo
SCRIPT_CORREO="/scripts/envia_correo.py"

#Función para reiniciar tomcat
reinicio () {
    $SCRIPT_PARADA &
    wait
    $SCRIPT_ARRANQUE &
    wait
    $SCRIPT_CORREO &
    wait
    return 0
}


#Función para controlar el conteo de veces seguidas que supera el valor umbral. 
#Tras 5 veces seguidas se procede a reiniciar tomcat
contador () {
    if [ -e $FICH_SEGUIDO ]; then
        VALOR_CONTADOR=`cat $FICH_CONTADOR`
        VALOR_CONTADOR=`expr $VALOR_CONTADOR + 1`
        if [ $VALOR_CONTADOR -eq 5 ]; then
            rm -rf $FICH_SEGUIDO
            echo "Se alcanzan valores por encima del 99% durante 5 veces consecutivas. Se reinicia tomcat." >> $LOG
            echo 0 > $FICH_CONTADOR
            reinicio
        else
            echo $VALOR_CONTADOR > $FICH_CONTADOR
        fi
    else
        touch $FICH_SEGUIDO
        echo 1 > $FICH_CONTADOR
    fi
}

#Se asigna de forma dinámica el nombre de la variable y su valor
for i in $( cat $TMP | tail -2); do
    eval export "VAR_${CONT}=`echo $i`"
    CONT=2
done

echo $DATE >> $LOG
echo "Eden: " $VAR_1"%" >> $LOG
echo "Old: " $VAR_2"%" >> $LOG
let EDEN=`echo $VAR_1 | cut -d "." -f 1`
let OLD=`echo $VAR_2 | cut -d "." -f 1`
if [ $EDEN -ge 99 ] && [ $OLD -ge 99 ]; then
    echo "Eden y Old Generation por encima del 99%" >> $LOG
    contador
else
    echo "Eden y Old Generation por debajo del 99%" >> $LOG
    rm -rf $FICH_SEGUIDO
fi

Para el envío de correo, podemos utilizar un script como este:


 #!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

import os, string,smtplib, socket
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders

maquina = socket.gethostbyaddr(socket.gethostname())[0]

#Funcion para el envio de correo
def sendMail(to, subject, text, server="servidor.domino.es"):
    assert type(to)==list
    fro = maquina + "@dominio.es"

    COMMASPACE = ', '
    msg = MIMEText(text)
    msg['From'] = fro
    msg['To'] = COMMASPACE.join(to)
    msg['Date'] = formatdate(localtime=True)
    msg['Subject'] = subject


    smtp = smtplib.SMTP(server)
    smtp.sendmail(fro, to, msg.as_string() )
    smtp.close()


sendMail(
        ["destinatario@dominio.es"],
        "Reinicio " + maquina,"Se ha reiniciado la " + maquina + " tras 5 ciclos seguidos con valores por encima del umbral de uso de memoria."
        )

jueves, 25 de agosto de 2011

Diangosticar y solucionar problemas de memoria en Java

Adjunto un documento que he traducido del artículo "Chuk-Munn Lee of Sun Microsystems Troubleshoots Java SE 6 Deployment" que me parece muy interesante.

Trata diversas formas de obtener información para analizar posibles problemas de memoria en aplicaciones Java.


Solución de problemas de memoria en java


Espero que sea de utilidad.

domingo, 14 de agosto de 2011

Material Defectuoso. Nuevo disco de Extremoduro.

Siempre he añadido entradas sobre temas de informática, pero hoy me apetece añadir una entrada musical.... Se trata del nuevo disco de Extremoduro. Es difícil no decir nada después de haberlo escuchado. Me ha sorprendido gratamente y creo que a todo aquel que le guste la música que hacen, también habrán disfrutado con esta joyita:


Letra de Tango Suicida


Sangre negra de esta herida brota,
no dejo de pensar que te deje marchar.

Nunca había estado un alma tan rota
desde que tu no estas no quiere recordar.

Se paso una vida entera
y yo solo guardo el recuerdo
de una pocas horas.

Era primavera,
el sol salió ese día
por ponerse a tu vera.

Y el olor de un día de enero,
estribadito en tu agujero,
sígue en mi cabeza.

Y un verano juntos de la mano,
y me pasao la noche fuera.

Ya todo el año me hace daño
y me vuelvo a llevar
a patadas con la primavera.

Junto a las hojas que el otoño vino a derribar
me deje llevar... me deje llevar.

Que te corre por las venas,
que te noto que te falta, nena,
temperatura, ¿que algo te hiela?
eso me apura, toda una vela.

Deja que te diga, nena,
que lo nuestro no es equitativo,
todas las noches que estoy contigo
tu eres quien come, yo soy comido.

Deja que te diga la razón
si tu imaginación
no encuentra una sola respuesta.

Tu deja que te clave en un arpón,
justo en el corazón,
así lo mismo te contesta.

Morir, sin mas, pues nadie me ha venido a despertar.
No estas, me abrí, y nadie me ha venido a despedir.

Sin nada mejor, que andar por dentro hurgándome,
sin nada mejor que hacer, tostándome al sol,
hurgando en el recuerdo y no lo entiendo mejor que ayer.

-Dinos que te pasa
-Estoy jodido, perdí la conciencia,
y ahora ando siempre sumergido
en montones de mierda.

-No ha dormido bien el señorito
-Iros a la mierda
-Que no se acostumbra a estar solito
en esa cabeza.

Dices que te hago daño,
¿es que no entiendes que te extraño a mi manera?

Ya que preguntas, pa' ahogar mis penas,
me fui de putas la noche entera.

Dijiste que nunca mintiera,
que dijera la verdad aunque duela.
¿Por que me miras de esa manera?
Después te fuiste,
y "adiós muy buenas"

Deja que te diga la razón
si tu imaginación
no encuentra una sola respuesta.

Tu deja que te clave en un arpón,
justo en el corazón,
así lo mismo te contesta.

Morir, sin mas, pues nadie me ha venido a despertar.
No estás, me abrí, y nadie me ha venido a despedir.

Hoy noto que no,
que no me da la gana, yo la vida doy por saber
si un mundo mejor esta esperándome mañana,
un mundo mejor que ayer.

Anda y cuéntale a tu diosecito que aquí huele a mierda.
Y dijo Judas: "solo necesito un trozo de cuerda"

-No ha dormido bien el señorito
-Iros a la mierda
-Que no se acostumbra a estar solito en esa cabeza.

Python + Jython. Cómo cambiar el estado de Alfresco 3.2r

Ante la necesidad de cambiar el estado de lectura/escritura de Alfresco de forma automática (para realizar los backup) he realizado unos scripts basándome en el jythonJmxClient.py de matthew@thebitsource.com.

La idea consiste en conectarme vía JMX a la instancia de alfresco para cambiar el atributo server.transaction.allow-writes del mbean Alfresco:Type=Configuration,Category=sysAdmin,id1=default.

Para esto me valgo del script jythonJmxClient.py, pero añadiéndole un nuevo método, def do_setAttribute(self, line). Este método será el que nos permita cambiar el valor del atributo:

def do_setAttribute(self, line):
                """setAttribute -m|--mbeanpath <path to mbeanr> -a|--attribute <name of attribute>
                Set the mbean server for a specific attribute"""

                #annoying option parsing stuff - need cmd2
                parser = optparse.OptionParser(conflict_handler="error")
                parser.add_option('-m', '--mbeanpath', dest='mbp', type='string')
                parser.add_option('-a', '--attribute', dest='attr')
                parser.add_option('-v', '--value', dest='val')
                (options, args) = parser.parse_args(line.split())
        
                value = javax.management.Attribute(options.attr,options.val)
                obn =  javax.management.ObjectName(options.mbp);
                remote.setAttribute(obn, value);

Antes que nada, debemos activar la conexión JMX en nuestro servidor de aplicaciones (tomcat en mi caso) añadiendo las siguientes opciones:

CATALINA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9696 -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.password.file=/opt/alfresco/jmxremote.password -Dcom.sun.management.jmxremote.access.file=/opt/alfresco/jmxremote.access -Dcom.sun.management.jmxremote.ssl=false"


Estos son los scripts que vamos a utilizar:

- estado_alfresco.py (encargado de llamar al script jython de conexion jmx y de pasarle los parámetros adecuados).
- jythonJmxClientAlfresco.py (script en jython para la conexión jmx).
- read_only.txt (fichero de texto con parámetros para conexión y valores a cambiar para poner alfresco en sólo lectura).
- read_write.txt (fichero de texto con parámetros para conexión y valores a cambiar para poner alfresco en lectura/escritura).
- ver_estado.txt (fichero de texto con parámetros para conexión y valores a consultar para ver su estado).

De este modo, si queremos poner alfresco en sólo lectura, haremos la siguiente llamada:

estado_alfresco.py read_only

para ponerlo en lectura/escritura:

estado_alfresco.py read_write

y para ver su estado:

estado_alfresco.py ver_estado

En cada caso imprimirá por pantalla el cambio realizado y su resultado.

Nota: No lo he comentado, pero para que funcione hay que tener instalado jython en el sistema.

lunes, 9 de mayo de 2011

Codificación de caracteres

Si necesitamos cambiar la codificación de caracteres de un fichero, podemos hacerlo de esta manera (por ejemplo, pasar de UTF-8 a ISO-8859-1):

iconv -f UTF-8 -t ISO-8859-1 fichero_origen > fichero_destino

martes, 26 de abril de 2011

Pantalla de Administración para Alfresco

En otras ocasiones, he posteado algunos scripts para facilitar la administración de usuarios/grupos en alfresco.

El inconveniente que le encuentro a trabajar directamente con los scripts, es que para pasarle los datos (usuarios, grupos, listas,...) hay que editar directamente el js, lo que hace perder tiempo y añade la posibilidad de introducir errores en el script.

He estado investigando un poco el tema de los WebScripts, y es precisamente lo que estaba buscando. Con ellos podemos interactuar con los scripts a través de amigables formularios.

He creado una serie de ellos basándome en los js que ya tenía (con algunas modificaciones) y este ha sido el resultado.

Añado algunas imágenes para que veáis cómo queda:

- Pantalla principal de administración:

- Ver grupos por usuario. Dado un usuario, te muestra los grupos a los que pertenece:













- Inserta un usuario en varios grupos.
Dado un usuario y una lista de grupos, añade este usuario a los grupos indicados:














- Copia los permisos de un usuario origen a un usuario destino:




















- Consulta los usuarios de un grupo:
















Aquí os dejo los webscripts.

Tras subir los fichero al espacio Web Scripts, habrá que activarlos. Para ello nos vamos a /alfresco/service/index y pinchamos en "Refresh Web Scripts". Una vez terminado, ya podemos dirigirnos a la nueva ventana de administración: /alfresco/service/sample/administracion

Sólo los usuarios con rol administrador podrán logarse.

Un saludo y espero que os sirva.

martes, 19 de abril de 2011

Administración de usuarios/grupos en alfresco (II)

Siguiendo con los scripts para la administración de usuario y grupos en alfresco, añado algunos más que pueden resultar útiles:

- grupos_por_usuario.js:

Su propio nombre ya es bastante descriptivo, se trata de un script que dado un usuario, nos devuelve los grupos a los que este usuario está añadido. Y digo añadido y no pertenece, puesto que no nos da el listado de grupos padres-hijos a los que pertenece por herencia, sino directamente los grupos a los que este usuario ha sido añadido.

//Variable a rellenar
var usuario="";
//Funcion que comprueba si un usuario dado existe como hijo de un grupo dado
function comprueba_grupo_usuario (grupo, usuario) {
scr_grupo = groups.getGroup(grupo);
var usuarios = scr_grupo.getChildUsers();
var encontrado = false;

var i = 0;
while ( i<usuarios.length && !encontrado) {
if ( usuarios[i].getFullName() == usuario ){
encontrado = true;
}
i++;
}
return encontrado;
}

//Creamos el log que devolveremos
var logFile = space.childByNamePath("documento_salida.txt");
if (logFile == null) {
logFile = space.createFile("documento_salida.txt");
}
logFile.content ="";
//Comprobamos si el usuario existe
var authority = people.getPerson(usuario);
if (authority){
lista = people.getContainerGroups(people.getPerson(usuario));
var log = "";
log+="El usuario: " + usuario + " pertenece a los siguientes grupos.\n";
for(var i=0;i<lista.length;i++)
{
grupo = (lista[i]).properties.authorityName;
grupo = grupo.substring(6,grupo.length);
if (comprueba_grupo_usuario(grupo,usuario)){
log+="Grupo: " + grupo + " \n";
}
}
}
logFile.content += log;


- grupos_por_lista_usuarios.js:

En este caso, lo que le pasamos al script es un conjunto de usuarios de los cuales queremos obtener los grupos a los que están asignados.


//Variable a rellenar
var usuarios = new Array ('usuario1','usuario2','usuario3');

//Funcion que comprueba si un usuario dado existe como hijo de un grupo dado
function comprueba_grupo_usuario (grupo, usuario) {
scr_grupo = groups.getGroup(grupo);
var usuarios = scr_grupo.getChildUsers();
var encontrado = false;

var i = 0;
while ( i<usuarios.length && !encontrado) {
if ( usuarios[i].getFullName() == usuario ){
encontrado = true;
}
i++;
}
return encontrado;
}

var logFile = space.childByNamePath("documento_salida.txt");
if (logFile == null)
{
logFile = space.createFile("documento_salida.txt");
}
logFile.content ="";
var log = "";

for (var j=0;j<usuarios.length;j++)
{
var authority = people.getPerson(usuarios[j]);
if(authority)
{
lista = people.getContainerGroups(people.getPerson(usuarios[j]));

log+="El usuario: " + usuarios[j] + " pertenece a los siguientes grupos.\n";
for(var i=0;i<lista.length;i++)
{
grupo = (lista[i]).properties.authorityName;
grupo = grupo.substring(6,grupo.length);
if (comprueba_grupo_usuario(grupo,usuario)){
log+="Grupo: " + grupo + " \n";
}
}
}
}
logFile.content += log;

- copia_permisos_usuario.js:

El último script de esta tanda resulta bastante útil si tienes que asignar los mismos grupos de un usuario origen a otro usuario destino.


//Variables a completar
var usuario_origen="usuario1";
var usuario_destino="usuario2";
var exclusivo = true;

//Log
var logFile = space.childByNamePath("documento_salida.txt");
if (logFile == null)
{
logFile = space.createFile("documento_salida.txt");
}
logFile.content ="";
var log = "";

//Funcion que comprueba si un usuario dado existe como hijo de un grupo dado
function comprueba_grupo_usuario (grupo, usuario) {
scr_grupo = groups.getGroup(grupo);
var usuarios = scr_grupo.getChildUsers();
var encontrado = false;

var i = 0;
while ( i<usuarios.length && !encontrado) {
if ( usuarios[i].getFullName() == usuario ){
encontrado = true;
}
i++;
}
return encontrado;
}

//Obtenemos los grupos del usuario
function obtiene_permisos (usuario){
var grupos = [];
var authority = people.getPerson(usuario);
if (authority){
lista = people.getContainerGroups(people.getPerson(usuario));
log+="El usuario: " + usuario + " pertenece a los siguientes grupos.\n";
for(var i=0;i<lista.length;i++)
{
grupo = (lista[i]).properties.authorityName;
grupo = grupo.substring(6,grupo.length);
if (comprueba_grupo_usuario(grupo,usuario)){
log+="Grupo: " + grupo + " \n";
grupos.push(grupo);
}
}
}
return grupos;
}

var groupPrefix="GROUP_";

if (exclusivo){
var grupos_destino = obtiene_permisos(usuario_destino);
var authority_destino = people.getPerson(usuario_destino);
for (var i=0;i<grupos_destino.length;i++){
var srcGrpNode_destino=people.getGroup(groupPrefix+grupos_destino[i]);
people.removeAuthority(srcGrpNode_destino,authority_destino);
}
}
//Metemos al usuario destino en los mismos grupos que el usuario origen
var authority = people.getPerson(usuario_destino);
if(authority)
{
var grupos_origen = obtiene_permisos(usuario_origen);
for(var i=0;i<grupos_origen.length;i++)
{
var srcGrpNode=people.getGroup(groupPrefix+grupos_origen[i]);
if (srcGrpNode)
{
var listado = people.getMembers(srcGrpNode);
var array_usuarios = [];
for (var j=0;j<listado.length;j++)
{
array_usuarios[j]=(listado[j]).properties["{http://www.alfresco.org/model/content/1.0}userName"];
}
if (array_usuarios.indexOf(usuario_destino)== -1) //Si el usuario no existe en el grupo, lo insertamos
{
people.addAuthority(srcGrpNode,authority);
log+="- añadido usuario:" + usuario_destino + " al grupo " + grupos_origen[i] + " \n";
}
else
{
log+="- El usuario: " + usuario_destino + " ya esta en el grupo " + grupos_origen[i] + "\n";
}
}
else
{
log+="- El grupo: " + grupos_origen[i] + " no existe.\n";
}
}//final for
}
else
{
log+="- No existe usuario:" + usuario_destino + "\n";
}
logFile.content += log;

En este script, además de tener que pasarle el usuario origen y destino, le debemos indicar el valor a una variable "exclusivo", que determinará si la copia se hace sobre los grupos que ya disponga el usuario destino (false), o si por el contrario, se deben eliminar previamente (true).

miércoles, 13 de abril de 2011

Parsear un fichero de configuración

Con frecuencia necesitamos cambiar ciertas variables o campos de un fichero de configuración para adaptarlo a nuestro sistema. Cuando esto se convierte en una tarea repetitiva y sobre todo cuando son muchos los valores a cambiar, se hace interesante automatizar este proceso.

Una forma de hacerlo es a través de algún script, en mi caso lo he hecho en python.

En dicho script definimos las variables a buscar y los nuevos valores a tomar. Recorrerá el fichero buscando coincidencias con las variables y les pondrá los valores que hemos indicado (independientemente de si tienen o no ya un valor). Sería algo así:
#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
"""
USO:
sustituye.py fichero_origen > fichero_destino
Este script toma un argumento por la linea de comandos: el nombre del fichero a parsear.

El resultado lo podemos redirigir a un nuevo fichero o ver como quedaría por pantalla.
"""
from cStringIO import StringIO
import re, sys
#Conjunto de variables a parsear
var1 = re.compile(r'(variable_1=)(\s+)?(\S+)')
var2 = re.compile(r'(variable_2=)(\s+)?(\S+)')
#Nuevos valores que deben tomar las variables
nuevo_valor_var1 = 'nuevo valor'
nuevo_valor_var2 = 'nuevo valor 2'
def cambia_variables(fichero):
conf_file = StringIO(fichero)
for line in conf_file:
match = var1.search(line)
match2 = var2.search(line)
if match:
sub_line = var1.sub(r'\1%s' % nuevo_valor_var1, line)
line = sub_line
if match2:
sub_line = var2.sub(r'\1%s' % nuevo_valor_var2, line)
line = sub_line
yield line
if __name__ == '__main__':
if not len(sys.argv) > 1:
print __doc__
sys.exit(1)
infile_name = sys.argv[1]
try:
infile = open(infile_name, 'r')
except IOError:
print "Debe especificar un fichero valido a parsear."
print __doc__
sys.exit(1)
fichero = sys.argv[1]
cadena_fichero = open(fichero).read()
for line in cambia_variables(cadena_fichero):
print line,



Como argumento recibe el nombre del fichero a parsear, y mostrará por la salida estandar cómo quedaría el fichero. Se puede redirigir la salida a un nuevo fichero.

En el ejemplo (muy básico) busca las siguientes variables:

variable_1=
y
variable_2=

y les otorga los valores:

nuevo valor
y
nuevo valor2

respectivamente.

miércoles, 6 de abril de 2011

Publicar el contenido de logs a través de tomcat

Se crea en $TOMCAT_HOME/conf/Catalina/localhost/ un xml para el nuevo contexto que se va a utilizar para acceder a los logs:

logs.xml:
<Context path="/logs" docBase="/ruta/de/logs" crossContext="false" debug="1" reloadable="true" >
</Context>


El docBase debe ser la ruta absoluta donde estén los logs que se quieran publicar.

Y en el direcotorio de los logs, creamos un direcotorio WEB-INF, y dentro un web.xml con el siguiente contenido:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">

<servlet>
<servlet-name>logs</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>logs</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<security-constraint>
<web-resource-collection>
<web-resource-name>
Logs
</web-resource-name>
<url-pattern>/</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>manager</role-name>
</auth-constraint>
</security-constraint>

<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Logs</realm-name>
</login-config>
</web-app>



Se ha añadido autenticación básica con los usuarios pertencientes al rol manager.

Si no se quiere hacer esta configuración particular para el contexto logs, se puede hacer de forma genérica para todo el tomcat cambiando en el $TOMCAT_HOME/conf/web.xml la línea correpondiente al parámetro listings, en vez de false, pondríamos true:

   <servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

jueves, 31 de marzo de 2011

LPIC 1

Prueba superada, el pasado 24 de marzo me examiné del LPIC 1, y hoy me han dado el resultado:

LPI ID: LPI000192128
Verification Code: zf4h7v2lux

https://cs.lpi.org/caf/Xamman/certification


Muy recomendable esta prueba, se refrescan muchos datos y se aprenden nuevos conceptos....

Siguiente meta: LPIC 2......

lunes, 28 de marzo de 2011

Ruta en bici

Ayer me di una vuelta con la bici de montaña, y me llevé el móvil para registrar la ruta con el GPS, este fue el resultado:

http://www.sports-tracker.com/#/workout/rafabono/8vlae3g74o195gth

Resultado: Agujetas hasta en el cielo de la boca, pero bueno, cuestión de práctica.....

sábado, 19 de febrero de 2011

Recuperar permisos de un espacio de Alfresco

Recientemente me he encontrado con un nuevo problema con Alfresco. De buenas a primeras habían desaparecido todos los permisos asignado a un espacio, y dicho espacio tenía cientos de subespacios con sus permisos y roles. En este caso se pudo salir al paso recuperando la mayor parte de los permisos de un entorno de preproducción.

La forma de hacerlo fue recorriendo de forma recursiva el espacio del entorno de preproducción y guardando sus permisos en un fichero. Este fichero es parseado posteriormente obteniendo un nuevo fichero con las sentencias necesarias para aplicar los permisos sobre el espacio que los había perdido.

Expongo los scripts:

- backup_permisos.js

//Obtiene el path del espacio

var ruta_origen = (space.displayPath + "/" + space.name).replace("/Empresa/","");


//Crea log en el espacio personal de quien se loga.
var nombre = space.name + "_permisos.log"
var logFile = userhome.childByNamePath(nombre);

if (logFile == null)

{

logFile = userhome.createFile(nombre);

}

logFile.content ="";

var log = "";



//Funcion que recorre un espacio para obtener sus permisos.

function recorre(doc)

{

for each (n in doc.children)

{

if (n.isContainer)

{
cadena_origen = n.displayPath + "/" + n.name;
cadena_destino = cadena_origen.replace("/Empresa/", "");
log += cadena_destino;

if (n.inheritsPermissions())

{

log += ";" + "true" + ";" ;

}
else
{
log += ";" + "false" + ";" ;
}



var cadena = n.getPermissions();

for each (k in cadena)

{

var permisos = k.split(';');
log += permisos[2] + "," + permisos[1] + "|" ;


}
log = log.substring(0, log.length-1);
log += "\n";

recorre (n);

}

}

}



//Comienza main.



//Se obtien los ScriptNode a partir de la ruta de los espacios.

var espacio_origen = companyhome.childByNamePath(ruta_origen);



//Recorremos el espacio origen para dar los permisos al destino.

recorre(espacio_origen);

//Permisos del espacio
cadena_origen = espacio_origen.displayPath + "/" + espacio_origen.name;
cadena_destino = cadena_origen.replace("/Empresa/", "");
log += cadena_destino;
if (espacio_origen.inheritsPermissions())
{
log += ";" + "true" + ";" ;

}
else
{
log += ";" + "false" + ";" ;
}

var cadena_permisos = espacio_origen.getPermissions();
for each (k in cadena_permisos)
{
var permisos = k.split(';');
log += permisos[2] + "," + permisos[1] + "|" ;

}
log = log.substring(0, log.length-1);
log += "\n";


logFile.content += log;

//FIN



Este script lo ejecutaremos como una acción sobre el espacio del cual queremos obtener los permisos asignados. Una vez ejecutado (puede tardar un buen rato, depende de la cantidad de documentos que se tenga) obtendremos en nuestro espacio personal un fichero que recoge los permisos para cada espacio y documento incluido el espacio sobre el que se ha lanzado.

Ese fichero lo parsearemos con el siguiente script en python, el cual nos devolverá un js que podremos ejecutar en alfresco para asignar esos permisos sobre el espacio original en caso de pérdida de permisos.

- crea_script_permisos.py

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

import os, string, sys
#Variables
entrada=sys.argv[1]
script=entrada + "_script.js"

fichero01 = open(entrada, 'r')
fichero02 = open(script, 'w')
for linea_fichero01 in fichero01:
linea=linea_fichero01.split(';')
espacio=linea[0].strip()
herencia=linea[1].strip()
try:
permisos=linea[2].strip()
except:
permisos=""
cadena1="var espacio = companyhome.childByNamePath(" + "\"" + espacio + "\"" + ");"
estructura_if="if ( espacio != null) {"
fin_estructura_if="}"
cadena2="espacio.setInheritsPermissions(" + herencia + ");"
fichero02.write(cadena1 + '\n')
fichero02.write(estructura_if + '\n')
fichero02.write(cadena2 + '\n')
if permisos != "":
lista=permisos.split('|')
for perm in range(len(lista)):
grup=lista[perm].split(',')
permis=grup[0].strip()
user=grup[1].strip()
cadena="espacio.setPermission(" + "\"" + permis + "\"" + "," + "\"" + user + "\"" + ");"
fichero02.write(cadena + '\n')
fichero02.write(fin_estructura_if + '\n')

fichero01.close()
fichero02.close()

Obtener informacion de repositorios a través de los metadatos .git publicados por error

 A raiz de CTF realizado recientemente, me ha parecido interesante publicar este post sobre los errores de seguridad que se encuentran en mu...