rediris-es / simplesamlphp-module-idpinstaller

A SimpleSAMLphp module for pre-installing an IdP
GNU Lesser General Public License v2.1
0 stars 0 forks source link

Instalación con composer #6

Open adriangmemorandum opened 6 years ago

adriangmemorandum commented 6 years ago

Hay que estudiar que la instalación pueda llegar a hacerse por completo con composer, en lugar del script shell actual (este script lo tenéis en su última versión en /var/www/install.sh en la máquina que os he pasado).

adriangmemorandum commented 6 years ago

Tras investigar el tema he visto que es posible implementar una instalación utilizando composer.

En composer se puede parametrizar scripts que se ejecutan como callback de distintos eventos. Aqui está la documentación oficial => Composer scripts.

Dentro de esos scripts podemos ejecutar comandos shell de la siguiente forma:

"php -r \"shell_exec('mkdir test');\"".

He ido haciendo pruebas y avances y le veo futuro. Así que, si no me dices lo contrario, continuo por aqui.

lonoak commented 6 years ago

De momento sí, pero ten en cuenta que parte de la funcionalidad (del script original) puede que no tenga sentido realizarse ejecutando comandos con shell_exec, si no mediante "dependencias composer" bien definidas... me refiero por ejemplo a la instalación del skin, o la intalación de módulos (no sólo idp_installer). Habría que darle una vuelta, mejor quizá fuera de este repositorio (el cual sería una dependencia más)...

adriangmemorandum commented 6 years ago

Correcto. Lo de los comandos del shell_exec es por los comandos que se ejecutan en el script original para mover carpetas o asignar permisos. Las dependencias, como bien dices, se gestionarian con composer.

Sigo en ello para disponer de una primera versión funcional lo antes posible y posteriormente la pongo en comun y la podemos ir ajustando.

adriangmemorandum commented 6 years ago

Esta es la versión actual de mi fichero composer.json.

{
    "name": "simplesamlphp/simplesamlphp",
    "description": "Instalación de SimpleSAMLphp junto con los paquetes de REDIRIS para la instalación del IDP",
    "version": "v0.1",
    "type": "project",
    "keywords": [ "saml2", "shibboleth","oauth","ws-federation","sp","idp" ],
    "homepage": "http://simplesamlphp.org",
    "license": "LGPL-2.1-or-later",
    "authors": [
        {
            "name": "Adrian Gomez",
            "email": "adriangomez@memorandum.net"
        }
    ],
    "autoload": {
        "psr-4": {
            "SimpleSAML\\": "lib/SimpleSAML"
        },
        "files": ["lib/_autoload_modules.php"]
    },
    "require": {
    "rediris-es/simplesamlphp-module-idpinstaller": "1.0.13",
        "mimaen/simplesamlphp-module-hubandspoke": "dev-master",
        "rediris-es/simplesamlphp-module-sir2skin": "v1.0.15"
    },
    "suggests": {
        "predis/predis": "1.1.1"
    },
    "support": {
        "issues": "https://github.com/simplesamlphp/simplesamlphp/issues",
        "source": "https://github.com/simplesamlphp/simplesamlphp"
    },
    "scripts": {
    "post-install-cmd": [
        "php -r \"shell_exec('composer create-project simplesamlphp/simplesamlphp');\"",
        "php -r \"shell_exec('rm -rf vendor');\"",
        "php -r \"shell_exec('mkdir simplesamlphp/cert');\"",
        "php -r \"shell_exec('chmod -R g+w simplesamlphp/cert');\"",
        "php -r \"shell_exec('cp -pr simplesamlphp/metadata-templates/saml20* simplesamlphp/metadata/');\"",
        "php -r \"shell_exec('cp -pr simplesamlphp/config-templates/* simplesamlphp/config/');\"",
        "php -r \"shell_exec('chmod g+w simplesamlphp/metadata/saml20-idp-hosted.php');\"",
        "php -r \"shell_exec('chmod g+w simplesamlphp/metadata/saml20-sp-remote.php');\"",
        "php -r \"shell_exec('cp -pr modules/idpinstaller simplesamlphp/modules/');\"",
        "php -r \"shell_exec('cp -pr modules/hubandspoke simplesamlphp/modules/');\"",
        "php -r \"shell_exec('cp -pr modules/sir2skin simplesamlphp/modules/');\"",
        "php -r \"shell_exec('rm -rf modules');\"",
        "php -r \"shell_exec('chmod -R g+w simplesamlphp/modules');\"",
        "php -r \"shell_exec('find  . -name makeCert.sh -exec chmod ug+x {}  \\;');\"",
        "php -r \"shell_exec('mv modules/hubandspoke/default-disable simplesamlphp/modules/hubandspoke/default-enable');\"",
        "php -r \"shell_exec('touch simplesamlphp/modules/sir2skin/default-enable');\"",
        "php -r \"shell_exec('curl -sS http://www.rediris.es/sir2/IdP/install/config.php.txt -o simplesamlphp/config/config.php');\"",
        "php -r \"shell_exec('chmod g+w simplesamlphp/config/config.php');\"",
        "php -r \"shell_exec('mv simplesamlphp/modules/sir2skin/default.disable simplesamlphp/modules/sir2skin/default-enable');\"",
        "php -r \"shell_exec('export APACHE_USER=`ps axo user | grep apache | grep -v root | uniq`');\"",
        "php -r \"shell_exec('export APACHE_GROUP=`ps axo group | grep apache | grep -v root | uniq`');\"",
        "php -r \"shell_exec('chmod a+x simplesamlphp/cert');\"",
        "php -r \"shell_exec('chown -R $APACHE_USER\\:$APACHE_GROUP simplesamlphp/');\""
    ]
    }

}

Partiendo de este fichero, solamente ejecutando composer install, obtenemos la instalación completa y funcional de simplasaml+idp. He comprobado dicha instalación y funciona correctamente. He podido realizar una instalación del IDP y acceder como admin sin problemas.

Basicamente lo que se hace es:

Al crear el proyecto simplesamlphp utilizando composer create-project simplesamlphp/simplesamlphp automaticamente se descargan todas las dependencias de simplesamlphp.

He probado a configurar el paquete simplesamlphp/simplesamlphp en el propio JSON, en el apartado de required, pero no lo consigo descargar. Solamente se me descargan dos carpetas (saml2 y composer-module-installer). Asi que de momento esta es la solución que he conseguido hacer funcionar.

Se entiende que deberiamos configurar nuestras dependencias para que se utilice siempre la ultima versión ¿no? Esto es lo que nos daria más potencia, ya que simplemente ejecutando composer update dispondriamos de una versión actualizada de todos los componentes.

Quedo a la espera de tus comentarios mientras voy investigando más sobre el tema.

adriangmemorandum commented 6 years ago

Acabo de editar el atributo name. Creo que ayer me funcionaba con el valor simplesamlphp/idpinstaller por temas de cache, pero realmente no funciona. Debe ser simplesamlphp/simplesamlphp

adriangmemorandum commented 6 years ago

Esta es mi última versión del composer.json:


{
    "name": "simplesamlphp/simplesamlphp",
    "description": "Instalación de SimpleSAMLphp junto con los paquetes de REDIRIS para la instalación del IDP",
    "version": "v0.1",
    "type": "project",
    "keywords": [ "saml2", "rediris","oauth","ws-federation","sp","idp", "simplesamlphp" ],
    "homepage": "https://wiki.rediris.es/SIR2/IdP_de_referencia",
    "license": "LGPL-2.1-or-later",
    "authors": [
        {
            "name": "Adrian Gomez",
            "email": "adriangomez@memorandum.net"
        }
    ],
    "autoload": {
        "psr-4": {
            "SimpleSAML\\": "lib/SimpleSAML"
        },
        "files": ["lib/_autoload_modules.php"]
    },
    "require": {
    "rediris-es/simplesamlphp-module-idpinstaller": "1.0.13",
        "mimaen/simplesamlphp-module-hubandspoke": "dev-master",
        "rediris-es/simplesamlphp-module-sir2skin": "v1.0.15"
    },
    "support": {
        "issues": "https://github.com/rediris-es/simplesamlphp-module-idpinstaller/issues",
        "source": "https://github.com/rediris-es/simplesamlphp-module-idpinstaller"
    },
    "scripts": {
    "installAll": [
        "php -r \"shell_exec('composer create-project simplesamlphp/simplesamlphp');\"",
        "php -r \"shell_exec('rm -rf vendor');\"",
        "php -r \"shell_exec('mkdir simplesamlphp/cert');\"",
        "php -r \"shell_exec('chmod -R g+w simplesamlphp/cert');\"",
        "php -r \"shell_exec('cp -pr simplesamlphp/metadata-templates/saml20* simplesamlphp/metadata/');\"",
        "php -r \"shell_exec('cp -pr simplesamlphp/config-templates/* simplesamlphp/config/');\"",
        "php -r \"shell_exec('chmod g+w simplesamlphp/metadata/saml20-idp-hosted.php');\"",
        "php -r \"shell_exec('chmod g+w simplesamlphp/metadata/saml20-sp-remote.php');\"",
        "php -r \"shell_exec('cp -pr modules/idpinstaller simplesamlphp/modules/');\"",
        "php -r \"shell_exec('cp -pr modules/hubandspoke simplesamlphp/modules/');\"",
        "php -r \"shell_exec('cp -pr modules/sir2skin simplesamlphp/modules/');\"",
        "php -r \"shell_exec('rm -rf modules');\"",
        "php -r \"shell_exec('chmod -R g+w simplesamlphp/modules');\"",
        "php -r \"shell_exec('find  . -name makeCert.sh -exec chmod ug+x {}  \\;');\"",
        "php -r \"shell_exec('mv modules/hubandspoke/default-disable simplesamlphp/modules/hubandspoke/default-enable');\"",
        "php -r \"shell_exec('touch simplesamlphp/modules/sir2skin/default-enable');\"",
        "php -r \"shell_exec('curl -sS http://www.rediris.es/sir2/IdP/install/config.php.txt -o simplesamlphp/config/config.php');\"",
        "php -r \"shell_exec('chmod g+w simplesamlphp/config/config.php');\"",
        "php -r \"shell_exec('mv simplesamlphp/modules/sir2skin/default.disable simplesamlphp/modules/sir2skin/default-enable');\"",
        "php -r \"shell_exec('export APACHE_USER=`ps axo user | grep apache | grep -v root | uniq`');\"",
        "php -r \"shell_exec('export APACHE_GROUP=`ps axo group | grep apache | grep -v root | uniq`');\"",
        "php -r \"shell_exec('chmod a+x simplesamlphp/cert');\"",
        "php -r \"shell_exec('chown -R $APACHE_USER\\:$APACHE_GROUP simplesamlphp/');\""
    ],
    "post-install-cmd": "@installAll",
    "post-update-cmd": "@installAll"
    }

}

Los cambios son más que nada de estilo. Tambien he creado un script personalizado para que se ejecute cuando se haga un composer install y un composer update.

He investigado a ver que otras opciones disponía pero sigo sin conseguir una solución funcional diferente a esta.

lonoak commented 6 years ago

Hola, no se si quizá sea buen camino incluirlo todo dentro de una clase... mirando los ejemplos por aquí, creo que se podría poner algo parecido a:

{
    "scripts": {
        "post-update-cmd": "MyVendor\\MyClass::postUpdate",
        "post-package-install": [
            "MyVendor\\MyClass::postPackageInstall"
        ],
        ...
    }
}

...y en las clases que se creen se mete esta funcionalidad. Si no el problema que veo es que el composer.json puede ser demasiado poco legible por lo tocho...

Por otro lado, buena parte de los comandos que se invocan por shell tienen su equivalente (o parecido) en PHP; por ejemplo: delete o rmdir (rm), mkdir, chmod, copy (cp), touch, rename (mv), chown... quizá sí puede ser tricky detectar el usuario bajo el que corre apache (getenv('APACHE_RUN_USER');??), o buscar el script de creación de certificados para darle permiso (aunque debe estar en una ruta predecible).

En fin, lo de arriba si funciona no me parece mal, pero igual hay una manera más "legible" de hacerlo...

adriangmemorandum commented 6 years ago

Perfecto, le doy una vuelta.

adriangmemorandum commented 6 years ago

La alternativa al código composer anterior utilizando un script PHP es la siguiente:

ComposerScript/Intaller.php

<?php

namespace ComposerScript;

use Composer\Script\Event;

class Installer
{

    public static function postInstall(Event $event)
    {
        self::configureSimpleSAMLphp();
    }

    public static function postUpdate(Event $event)
    {
        self::configureSimpleSAMLphp();
    }

    private static function configureSimpleSAMLphp()
    {
        if (file_exists('simplesamlphp')) {
            self::rm_r('simplesamlphp');
        }

        shell_exec('composer create-project simplesamlphp/simplesamlphp');
        self::rm_r('vendor');

        if (!file_exists('simplesamlphp/cert')) {
            mkdir('simplesamlphp/cert');
        }

        $apacheUser = exec('ps axo user | grep apache | grep -v root | uniq');
        $apacheGroup = exec('ps axo group | grep apache | grep -v root | uniq');
        $filePermissions = octdec("0664");
        $folderPermissions = octdec("0775");

        copy("simplesamlphp/metadata-templates/saml20-idp-hosted.php", "simplesamlphp/metadata/saml20-idp-hosted.php");
        copy("simplesamlphp/metadata-templates/saml20-idp-remote.php", "simplesamlphp/metadata/saml20-idp-remote.php");
        copy("simplesamlphp/metadata-templates/saml20-sp-remote.php", "simplesamlphp/metadata/saml20-sp-remote.php");
        copy("simplesamlphp/config-templates/acl.php", "simplesamlphp/config/acl.php");
        copy("simplesamlphp/config-templates/authmemcookie.php", "simplesamlphp/config/authmemcookie.php");
        copy("simplesamlphp/config-templates/authsources.php", "simplesamlphp/config/authsources.php");
        copy("simplesamlphp/config-templates/config.php", "simplesamlphp/config/config.php");
        chmod("simplesamlphp/metadata/saml20-idp-hosted.php", $filePermissions);
        chmod("simplesamlphp/metadata/saml20-sp-remote.php", $filePermissions);
        self::copy_r("modules/idpinstaller", "simplesamlphp/modules/idpinstaller");
        self::copy_r("modules/hubandspoke", "simplesamlphp/modules/hubandspoke");
        self::copy_r("modules/sir2skin", "simplesamlphp/modules/sir2skin");
        self::rm_r('modules');
        self::chmod_r("simplesamlphp/modules", $folderPermissions);

        if (file_exists('simplesamlphp/modules/hubandspoke/default-disable')) {
            rename('simplesamlphp/modules/hubandspoke/default-disable','simplesamlphp/modules/hubandspoke/default-enable');
        }

        touch('simplesamlphp/modules/sir2skin/default-enable');

        if (file_exists('simplesamlphp/modules/sir2skin/default-disable')) {
            rename('simplesamlphp/modules/sir2skin/default-disable','simplesamlphp/modules/sir2skin/default-enable');
        }

        self::downloadAndWriteConfig();
        chmod("simplesamlphp/config/config.php", $filePermissions);
        chmod("simplesamlphp/modules/idpinstaller/lib/makeCert.sh", $folderPermissions);

        if (file_exists('simplesamlphp/modules/sir2skin/default.disable')) {
            rename('simplesamlphp/modules/sir2skin/default.disable','simplesamlphp/modules/sir2skin/default-enable');
        }

        self::chmod_r("simplesamlphp/cert", $folderPermissions);
        self::chown_r('simplesamlphp', $apacheUser, $apacheGroup);
    }

    private static function downloadAndWriteConfig()
    {

        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, "http://www.rediris.es/sir2/IdP/install/config.php.txt");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

        $result = curl_exec($ch);

        curl_close ($ch);

        file_put_contents('simplesamlphp/config/config.php', $result);

    }

    private static function chmod_r($path, $filemode) 
    {
        chmod($path, $filemode);

        $d = opendir($path);

        while (($file = readdir($d)) !== false) {
            if($file != '.' && $file != '..') {
                $typepath = $path.'/'.$file;

                if (filetype ($typepath) == 'dir') {
                    self::chmod_r($typepath, $filemode);
                }
                chmod($typepath, $filemode);
            }
        }

        closedir($d);

    }

    private static function chown_r($path, $uid, $gid)
    {
        chown($path, $uid);
        chgrp($path, $gid);

        $d = opendir ($path) ;

        while(($file = readdir($d)) !== false) {
            if ($file != "." && $file != "..") {

                $typepath = $path . "/" . $file ;

                if (filetype ($typepath) == 'dir') {
                    self::chown_r($typepath, $uid, $gid);
                }

                chown($typepath, $uid);
                chgrp($typepath, $gid);

            }
        }

        closedir($d);

    }

    private static function rm_r($src) 
    {
        $dir = opendir($src);
        while(false !== ( $file = readdir($dir)) ) {
            if (( $file != '.' ) && ( $file != '..' )) {
                $full = $src . '/' . $file;
                if ( is_dir($full) ) {
                    self::rm_r($full);
                }
                else {
                    unlink($full);
                }
            }
        }
        closedir($dir);
        rmdir($src);
    }

    private static function copy_r($src,$dst) 
    {
        $dir = opendir($src);
        @mkdir($dst);
        while(false !== ( $file = readdir($dir)) ) {
            if (( $file != '.' ) && ( $file != '..' )) {
                if ( is_dir($src . '/' . $file) ) {
                    self::copy_r($src . '/' . $file,$dst . '/' . $file);
                }
                else {
                    copy($src . '/' . $file,$dst . '/' . $file);
                }
            }
        }
        closedir($dir);
    }
}

?>

composer.json

{
    "name": "simplesamlphp/simplesamlphp",
    "description": "Instalación de SimpleSAMLphp junto con los paquetes de REDIRIS para la instalación del IDP",
    "version": "v0.1",
    "type": "project",
    "keywords": [ "saml2", "rediris","oauth","ws-federation","sp","idp", "simplesamlphp" ],
    "homepage": "https://wiki.rediris.es/SIR2/IdP_de_referencia",
    "license": "LGPL-2.1-or-later",
    "authors": [
        {
            "name": "Adrian Gomez",
            "email": "adriangomez@memorandum.net"
        }
    ],
    "autoload": {
        "psr-0": {
        "ComposerScript\\Installer" : ""
     }
    },
    "require": {
    "rediris-es/simplesamlphp-module-idpinstaller": "1.0.13",
        "mimaen/simplesamlphp-module-hubandspoke": "dev-master",
        "rediris-es/simplesamlphp-module-sir2skin": "v1.0.15"
    },
    "support": {
        "issues": "https://github.com/rediris-es/simplesamlphp-module-idpinstaller/issues",
        "source": "https://github.com/rediris-es/simplesamlphp-module-idpinstaller"
    },
    "scripts": {
    "post-install-cmd": "ComposerScript\\Installer::postInstall",
    "post-update-cmd": "ComposerScript\\Installer::postUpdate"
    }

}
adriangmemorandum commented 6 years ago

Aunque, efectivamente, PHP dispone de funciones que 'simulan' los comandos shell, no disponen de la potencia de parametrización de los mismos. Así que he creado unas funciones para simular estos comandos parametrizados. Más que nada es el parámetro -r para aplicar el comando de forma recursiva.

etenv('APACHE_RUN_USER'); no me ha funcionado, igual que otras formas de obtener el usuario de apache que he probado. Entiendo que esta forma de obtenerlo funcionará cuando sea el servicio de apache quien este ejecutando PHP, pero este no es el caso.

Por tanto he mantenido la forma de obtenerlo, utilizando la funcion exec:

$apacheUser = exec('ps axo user | grep apache | grep -v root | uniq');
$apacheGroup = exec('ps axo group | grep apache | grep -v root | uniq');

Esta forma de hacerlo se ve más limpia y organizada. La única pega que se puede poner es que es necesario otro fichero. Si eso no es problema, adelante con ella.

Si consideras que es mejor utilizar solamente el fichero composer.json, entonces nos podemos quedar con la versión anterior. Pero en todos los casos en los que he necesitado crear una función para ejecutar el comando de forma recursiva no podremos evitarnos el "php -r \"shell_exec(.

Por otra parte, entiendo que deberiamos obtener siempre la versión más reciente de las dependencias. Deberiamos cambiar:

rediris-es/simplesamlphp-module-idpinstaller": "1.0.13",
"mimaen/simplesamlphp-module-hubandspoke": "dev-master",
"rediris-es/simplesamlphp-module-sir2skin": "v1.0.15"

por:

"rediris-es/simplesamlphp-module-idpinstaller": "*",
 "mimaen/simplesamlphp-module-hubandspoke": "dev-master",
 "rediris-es/simplesamlphp-module-sir2skin": "*"

También añadiria "prefer-stable": true,, para que composer siempre busque la opción más estable, aunque no sea la última disponible. Para ello sigue con este orden de prioridad:

– stable 
– RC
– beta
– alpha
– dev 

Con lo cual, mi última propuesta de composer.json es la siguiente:

{
    "name": "simplesamlphp/simplesamlphp",
    "description": "Instalación de SimpleSAMLphp junto con los paquetes de REDIRIS para la instalación del IDP",
    "version": "v0.1",
    "type": "project",
    "keywords": [ "saml2", "rediris","oauth","ws-federation","sp","idp", "simplesamlphp" ],
    "homepage": "https://wiki.rediris.es/SIR2/IdP_de_referencia",
    "license": "LGPL-2.1-or-later",
    "prefer-stable": true,
    "authors": [
        {
            "name": "Adrian Gomez",
            "email": "adriangomez@memorandum.net"
        }
    ],
    "autoload": {
        "psr-0": {
        "ComposerScript\\Installer" : ""
     }
    },
    "require": {
    "rediris-es/simplesamlphp-module-idpinstaller": "*",
        "mimaen/simplesamlphp-module-hubandspoke": "dev-master",
        "rediris-es/simplesamlphp-module-sir2skin": "*"
    },
    "support": {
        "issues": "https://github.com/rediris-es/simplesamlphp-module-idpinstaller/issues",
        "source": "https://github.com/rediris-es/simplesamlphp-module-idpinstaller"
    },
    "scripts": {
    "post-install-cmd": "ComposerScript\\Installer::postInstall",
    "post-update-cmd": "ComposerScript\\Installer::postUpdate"
    }

}
adriangmemorandum commented 6 years ago

Adjunto en un zip los dos ficheros:

composer.zip

adriangmemorandum commented 6 years ago

Otra cosa que se me ocurre es que podriamos subir esa clase al repositorio de packagist.org y añadirla como dependencia en el composer.json.

adriangmemorandum commented 6 years ago

He tenido que añadir unas lineas para activar el soporte a exampleauth, necesario para #7, de la cual muy pronto subiremos la primera versión. Simplemente eliminar el fichero default-disable y crear un fichero enable.

Adjunto nueva versión.

composer.zip

lonoak commented 6 years ago

Al fin he sacado tiempo para probarlo.

He hecho unos pequeños cambios –que no creo que tengan repercusión– pero el caso es que se para en este error:

Generating autoload files
Script InstallerScripts\Installer::postInstall handling the post-install-cmd event terminated with an exception

  [ErrorException]
  rmdir(vendor/simplesamlphp/simplesamlphp/locales/no): Not a directory

Quitando esto, que habría que repasar, creo que estaría listo para crear el repositorio del que hablamos, en github (uno nuevo) y en packagist...

Lo vemos en la siguiente vídeo y cierro el ticket cuando esté y lo hayamos probado.

lonoak commented 6 years ago

Hola, he creado un nuevo repositorio donde irán en principio tanto el composer.json como las acciones... así como cualquier "adjunto" que necesitemos incluir en el futuro. Te dejo el honor de subir el código, documentar cómo se invocaría, y crear una primera release. Cuando me confirmes que puedes acceder, cerramos este issue y vamos trabajando en el otro proyecto...

adriangmemorandum commented 6 years ago

Hola @lonoak, perdona la demora estaba enfrascado con el tema de TWIG.

Acabo de intentar subir los archivos y la documentación actualizada pero obtengo el siguiente error:

remote: Permission to rediris-es/idpref-installer-updater.git denied to adriangmemorandum.
fatal: unable to access 'https://github.com/rediris-es/idpref-installer-updater.git/': The requested URL returned error: 403

Entiendo que no tengo permisos en ese repositorio.