Commit 2c893bbd authored by Stephane Bouvry's avatar Stephane Bouvry
Browse files

Up des message d'erreur

Up UI (en cours)
Documentation technique (en cours)
parent 0b5efd29
Loading
Loading
Loading
Loading
Loading
+7 −88
Changes for config/unicaen-signature.local.php.dist: 7 added lines, 88 removed lines.
Original line number Diff line number Diff line
@@ -83,103 +83,19 @@ return [
                } catch (Exception $e) {
                    $logger->error("ERREUR MAIL SIGNATURE : ". $e->getMessage());
                }

            }
        ],

        /**
         * Méthodes personnalisées de récupération des utilisateurs
         *
         * La finalité est d'obtenir une liste de destinataires sous la forme :
         * [
         *   ['email'=>string, 'firstname'=>string, 'lastname'=>string ],
         *   ['email'=>string, 'firstname'=>string, 'lastname'=>string ],
         *  ]
         * Méthodes personnalisées de récupération des utilisateurs (PROCESS)
         *
         * Note : firstname/lastname sont optionnels
         */
        'get_recipients_methods' => [
            [
                ///// key : Clef unique pour identifier le méthode
                'key'               => 'persons_by_role',
                'label'             => 'Personnes par rôle', // Intitulé
                'description'       => 'Selectionne les personnes en fonction de leurs rôles', // Description

                // Les options sont utilisées pour configurer la méthode d'obtention des destinataires
                // Il y'a 2 étapes :
                //  ETAPE 1 - options
                // Les options permettent de définir un ou plusieurs critères
                // Chaque critère a un nom unique (key), et un tableau de valeurs possibles.
                // Ces valeurs sont fixées avec la clef 'values', soit des valeurs fixes, soit une fonction retournant
                // la liste des CLEFS=>VALEURS disponible pour cette option.
                //  ETAPE 2 - getRecipients
                // Un méthode 'getRecipients' est appelé lors du déclenchement d'un étape de signature,
                // cette méthode reçoit les options configurées sous la forme KEY_OPTION => [VALEURA,VALEURSB,...]
                'options'           => [ // Options
                    [
                        'key'          => 'role_person_id',
                        'label'        => "Rôle dans l'activité",
                        // Type d'affichage (checkbox / recipients)
                        'type'         => 'checkbox',

                        ///////////////////////////////////////////////////
                        // array|function
                        // Retourne un tableau VALEUR => LABEL.
                        //
                        // Ex : [
                        //        15 => "Valeur d'option A",
                        //        24 => "Valeur d'option B",
                        //        37 => "Valeur d'option C",
                        //      ]
                        'values'       => function (ContainerInterface $s) {
                            return $s->get(
                                \Oscar\Service\OscarUserContext::class
                            )->getAvailableRolesActivityOrOrganization();
                        },

                        // Valeurs par défaut
                        'defaultValue' => []
                    ],
                    [
                        'key'    => 'role_organisation_id',
                        'label'  => "étendre aux structures",
                        'type'   => 'checkbox',
                        'values' => function ($s) {
                            return $s->get(
                                \Oscar\Service\OscarUserContext::class
                            )->getAvailabledRolesOrganizationActivity();
                        },

                        'defaultValue' => []
                    ]
                ],

                //////////////////////////////////////
                /// TODO / DEPRECATED ?
                'methods_on_create' => null,

                //////////////////////////////////////
                /// Retourne les destinataires sous la forme :
                /// [
                ///    ['email'=>string, 'firstname'=>string, 'lastname'=>string ],
                ///    ['email'=>string, 'firstname'=>string, 'lastname'=>string ],
                /// ]
                ///
                /// $options corresponds au options configurées avant, dans cet exemple, 'getRecipients' va recevoir les
                /// valeurs :
                /// [
                ///   'role_person_id' => [VALEUR1,VALEUR2],
                ///   'role_organisation_id' => [VALEURA,VALEURB]
                /// ]
                ///
                'getRecipients'     => function ($sc, $options) {
                    return $sc->get(\Oscar\Service\ProjectGrantService::class)->getRecipients($options);
                }
            ],
        ],
        'get_recipients_methods' => [],

        // Configuration des parapheurs numérique
        'letterfiles'            => [
            ////////////////////////////////////////////////////////////////////////////////////////////////////////////
            /// ESUP
            [
                // Nom visible côté applicatif
                'label' => 'ESUP',
@@ -213,6 +129,9 @@ return [
                    'createdByEppn' => 'bouvry@unicaen.fr',
                ]
            ],

            ////////////////////////////////////////////////////////////////////////////////////////////////////////////
            /// OSCAR (Viseur interne)
            [
                'label'   => 'OSCAR visa',
                'name'    => 'internal', // internal est une clef dédiée pour identifier le parapheur interne
+234 −0
Changes for config/unicaen-signature.local.php.full-dist: 234 added lines, 0 removed lines.
Original line number Diff line number Diff line
<?php

use Psr\Container\ContainerInterface;

return [
    /**  **/
    'unicaen-signature' => [

        /////////////////////////////////////////////////////////////////////////////////
        // DEVELOPPEMENT
        'vite_mode'              => 'prod', // mode développement de l'UI
        // Mode dev (voir doc)
//        'vite_mode'      => 'dev', // mode développement de l'UI

        // Emplacement où sont archivés les document en cours de signature
        'documents_path' => __DIR__ . '/../../data/documents/signature',


        /////////////////////////////////////////////////////////////////////////////////
        /// SYSTEME de LOG
        'logger'         => [
            ///////////////////////////////////////
            // Activation d'un logger autonome
            'enable'          => true, // Actif
            'level'           => \Monolog\Logger::DEBUG, // Niveau de log
            'file'            => __DIR__ . '/../../logs/signature.log', // Fichier d'écriture
            'file_permission' => 0666,

            ///////////////////////////////////////
            /// Sortie standard (pour le développement le built-in serveur)
            'stdout'          => false,

            ///////////////////////////////////////
            /// Logger complémentaire (celui de l'application utilisant le module)
            /// -> implementation de LoggerInterface (ex: Monolog)
            'customLogger' => null
            //'customLogger'    => 'Logger' // customLogger (LoggerInterface)
        ],
        /////////////////////////////////////////////////////////////////////////////////

        /////////////////////////////////////////////////////////////////////////////////
        /// Logique métier

        /**
         * Retourne l'email de l'utilisateur courant. Cette méthode est utilisé lors d'un VISA INTERNE pour vérifier
         * si l'utilisateur courant est autorisé à Valider/Refuser le document.
         */
        'current_user'   => function (ContainerInterface $sc): string {
            $person = $sc->get(\Oscar\Service\OscarUserContext::class)->getCurrentPerson();
            if ($person) {
                return $person->getEmail();
            }
            return "";
        },

        'notifications_messages' => [
            'base_url' => 'http://localhost',
            'subject' => 'Document à {ACTION} sur OSCAR(dev)',
            'body'    => "Bonjour {FULLNAME},\r\nVous avez un document à {ACTION} sur OSCAR(dev). \r\n
                {URL}
            "
        ],

        /**
         * Liste des procédures déclenchées lors des notifications.
         */
        'notifications'          => [
            function (
                ContainerInterface $sc,
                \UnicaenSignature\Entity\Db\SignatureRecipient $recipient,
                string $subject,
                string $message
            ): void {
                $logger = $sc->get("Logger");
                $logger->debug("CONFIG:Notification $recipient | $subject | $message");
                try {
                    /** @var \Oscar\Service\MailingService $mailer */
                    $mailer = $sc->get(\Oscar\Service\MailingService::class);
                    $mail = $mailer->newMessage($subject);
                    $mail->setTo($recipient->getEmail())
                        ->setBody($message);
                    $mailer->send($mail);
                } catch (Exception $e) {
                    $logger->error("ERREUR MAIL SIGNATURE : ". $e->getMessage());
                }

            }
        ],

        /**
         * Méthodes personnalisées de récupération des utilisateurs
         *
         * La finalité est d'obtenir une liste de destinataires sous la forme :
         * [
         *   ['email'=>string, 'firstname'=>string, 'lastname'=>string ],
         *   ['email'=>string, 'firstname'=>string, 'lastname'=>string ],
         *  ]
         *
         * Note : firstname/lastname sont optionnels
         */
        'get_recipients_methods' => [
            [
                ///// key : Clef unique pour identifier le méthode
                'key'               => 'persons_by_role',
                'label'             => 'Personnes par rôle', // Intitulé
                'description'       => 'Selectionne les personnes en fonction de leurs rôles', // Description

                // Les options sont utilisées pour configurer la méthode d'obtention des destinataires
                // Il y'a 2 étapes :
                //  ETAPE 1 - options
                // Les options permettent de définir un ou plusieurs critères
                // Chaque critère a un nom unique (key), et un tableau de valeurs possibles.
                // Ces valeurs sont fixées avec la clef 'values', soit des valeurs fixes, soit une fonction retournant
                // la liste des CLEFS=>VALEURS disponible pour cette option.
                //  ETAPE 2 - getRecipients
                // Un méthode 'getRecipients' est appelé lors du déclenchement d'un étape de signature,
                // cette méthode reçoit les options configurées sous la forme KEY_OPTION => [VALEURA,VALEURSB,...]
                'options'           => [ // Options
                    [
                        'key'          => 'role_person_id',
                        'label'        => "Rôle dans l'activité",
                        // Type d'affichage (checkbox / recipients)
                        'type'         => 'checkbox',

                        ///////////////////////////////////////////////////
                        // array|function
                        // Retourne un tableau VALEUR => LABEL.
                        //
                        // Ex : [
                        //        15 => "Valeur d'option A",
                        //        24 => "Valeur d'option B",
                        //        37 => "Valeur d'option C",
                        //      ]
                        'values'       => function (ContainerInterface $s) {
                            return $s->get(
                                \Oscar\Service\OscarUserContext::class
                            )->getAvailableRolesActivityOrOrganization();
                        },

                        // Valeurs par défaut
                        'defaultValue' => []
                    ],
                    [
                        'key'    => 'role_organisation_id',
                        'label'  => "étendre aux structures",
                        'type'   => 'checkbox',
                        'values' => function ($s) {
                            return $s->get(
                                \Oscar\Service\OscarUserContext::class
                            )->getAvailabledRolesOrganizationActivity();
                        },

                        'defaultValue' => []
                    ]
                ],

                //////////////////////////////////////
                /// TODO / DEPRECATED ?
                'methods_on_create' => null,

                //////////////////////////////////////
                /// Retourne les destinataires sous la forme :
                /// [
                ///    ['email'=>string, 'firstname'=>string, 'lastname'=>string ],
                ///    ['email'=>string, 'firstname'=>string, 'lastname'=>string ],
                /// ]
                ///
                /// $options corresponds au options configurées avant, dans cet exemple, 'getRecipients' va recevoir les
                /// valeurs :
                /// [
                ///   'role_person_id' => [VALEUR1,VALEUR2],
                ///   'role_organisation_id' => [VALEURA,VALEURB]
                /// ]
                ///
                'getRecipients'     => function ($sc, $options) {
                    return $sc->get(\Oscar\Service\ProjectGrantService::class)->getRecipients($options);
                }
            ],
        ],

        // Configuration des parapheurs numérique
        'letterfiles'            => [
            [
                // Nom visible côté applicatif
                'label' => 'ESUP',

                // Code/Clef (unique)
                'name'  => 'esup',

                'description' => 'Parapheur numérique ESUP',

                // Utilisé par défaut
                'default'     => true,

                // Classe
                'class'       => \UnicaenSignature\Strategy\Letterfile\Esup\EsupLetterfileStrategy::class,

                // Niveaux de signature disponible
                'levels'      => [
                    \UnicaenSignature\Utils\SignatureConstants::VISA_HIDDEN => 'hidden',
                    \UnicaenSignature\Utils\SignatureConstants::VISA_VISUAL => 'visa',
                    \UnicaenSignature\Utils\SignatureConstants::SIGN_VISUAL => 'pdfImageStamp',
                    \UnicaenSignature\Utils\SignatureConstants::SIGN_CERTIF => 'certSign',
                    \UnicaenSignature\Utils\SignatureConstants::SIGN_EIDAS  => 'nexuSign',
                ],

                // Configuration du parapheur
                'config'      => [
                    // ESUP configuration
                    'url'           => "https://signature-pp.unicaen.fr",

                    // Créateur
                    'createdByEppn' => 'bouvry@unicaen.fr',
                ]
            ],
            [
                'label'   => 'OSCAR visa',
                'name'    => 'internal', // internal est une clef dédiée pour identifier le parapheur interne
                'default' => false,
                'class'   => \UnicaenSignature\Strategy\Letterfile\InternalVisa\InternalVisaStrategy::class,
                'levels'  => [
                    'visa_hidden' => 'visa_hidden',
                ],
                'config'  => [
                    'checkUserAcces' => function ($sc, $signatureRecipient) {
                        return true;
                        //return $sc->get(\Oscar\Service\ProjectGrantService::class)->getRecipients($options);
                    }
                ]
            ]
        ]
    ]
    /******/
];
 No newline at end of file
+164 −63
Changes for doc/dev/README.md: 164 added lines, 63 removed lines.
Original line number Diff line number Diff line
# Guide de développement

## Utilisation des commandes
Le module signature permet d'intégrer à votre application un système de signature numérique. Cela se fait via une application tiers (un **parapheur numérique**).

### Console de base
## Installation de base

### Ajouter la dépendence vie composer

```bash
php vendor/bin/unicaen-signature
composer require unicaen/signature
```

### Configuration de base

Des fichiers de configuration `.dist` sont disponibles dans `vendor/unicaen/signature/config` avec quelques exemple de configuration : 

```bash
# Copier le modèle de base de configuration
cp vendor/unicaen/signature/config/unicaen-signature.local.php.dist config/autoload/unicaen-signature.local.php
```

Adapter la configuration selon votre usage, par défaut, le fichier de configuration propose une configuration avec le parapheur ESUP et le parapheur Interne.

 - [Configuration du parapheur ESUP](parapheur-esup.md)
 - [Configuration du parapheur INTERNAL](parapheur-interne.md)

> Vous pouvez également [Développer un nouveau parapheur](parapheur-dev.md)

### Base de données

Vous devez installer les tables utilisées par le module : 

![Tables du module](database.png)

#### Via les entitées Doctrine

Dans le fichier de configuration de votre application (normalement `config/autoload/global.php`), éditez le `paths` des entitées pour y ajouter les entitées du module **signature** : 

```php
<?php
return array(
    // ...
    'doctrine' => array(
        // ...
        'driver' => array(
            'my_entities' => array(
                'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
                'cache' => 'array',
                'paths' => array(
                    // Emplacement des entitées de UnicaenSignature
                    __DIR__ . '/../../vendor/unicaen/signature/src/Entity/Db',
                ),
            ),
        ),
    ),
);
```

### Commandes dans votre application (exemple)
Puis mettez à jour le modèle de la base avec les commandes *Doctrine*.

#### Via SQL

Exemple, créer un fichier `bin/commands.php` : 
Sinon, utiliser directement SQL [Script SQL pour créer les tables](database-install-sql.md) :

### Activer le module

Ajoutez **UnicaenSignature** dans `config/application.config.php` :

```php
<?php
require __DIR__.'/../vendor/autoload.php';

chdir(dirname(__DIR__));
$console = new \Symfony\Component\Console\Application();
$conf = require __DIR__.'/../config/application.config.php';
$app = \Laminas\Mvc\Application::init($conf);

$commands = [];

$paths = $app->getConfig()['console_commands'];

function scanCommandFiles(string $namespace, string $dir){
    global $app;
    $output = [];
    $re = '/.*Command\.php/m';

    if( !file_exists($dir) ){
        echo ("[WARNING] Command path : Le dossier '$dir' n'existe pas\n");
        return [];
    }

    $scan = scandir($dir);
    foreach ($scan as $key => $value) {
        if( !in_array($value, ['.', '..']) ){
            if( is_dir($dir.DIRECTORY_SEPARATOR.$value) ){

            } else {
                if( preg_match($re, $value, $matches) ){
                    $class = substr($value, 0, strlen($value)-4);
                    $reflector = new ReflectionClass($namespace.$class);
                    if( $reflector->isSubclassOf(\Symfony\Component\Console\Command\Command::class) ){
                        $instance = $reflector->newInstanceArgs([$app->getServiceManager()]);
                        if( property_exists($instance, 'disabled') ) {
                            continue;
                        }
                        $result[] = $instance;
                    }
                }
            }
        }
    }
    return $result;
}

foreach ($paths as $namespace=>$path) {
    $commands = scanCommandFiles($namespace, $path);
    $console->addCommands($commands);
}

$console->run();
$config = array(
    'modules' => array(
        // ...
        'UnicaenSignature'
    ),
    // ...
);
// ...
return $config;

```

Et voilà
### Dossier des documents

Vérifiez que le dossier d'écriture des documents à signer est bien accessible en écriture. C'est le dossier indiqué dans la clef `documents_path`

A cette étape, le module est opérationnel et permet d'utiliser ces services pour les documents de votre application.


## Utiliser les interfaces livrées avec le module

### Ajout des privilèges

Pour utiliser les éléments de l'interface, Vous devez disposer des privilèges Unicaen, voici ceux utilisés par le module : 

```sql
INSERT INTO public.privilege (id,categorie_id,code,libelle,ordre,root_id,spot) VALUES
	 (123,10,'SIGNATURE_INDEX','Liste des signatures',NULL,NULL,7),
	 (124,10,'SIGNATURE_DELETE','Suppression des signatures',NULL,NULL,7),
	 (125,10,'SIGNATURE_CREATE','Création de signature',NULL,NULL,7),
	 (126,10,'SIGNATURE_SYNC','Synchronisation de signature',NULL,NULL,7),
	 (127,10,'SIGNATURE_ADMIN','Accès à l''interface d''administration / gestion des signatures et processus en cours',NULL,NULL,7),
	 (128,10,'SIGNATURE_ADMIN_CONFIG','Configuration des processus métier',NULL,NULL,7);
```

> Ces requêtes doivent être adaptées selon votre application

### Activer les ASSETS (UI)

Si vous utilisez les interfaces du modules, vous devez rendre accessible les scripts JS/CSS à l'url `/unicaen/signature`, le plus simple est de passer par un lien symbolique : 

```bash
php bin/commands.php
cd public/unicaen
ln -s ../../vendor/unicaen/signature/public/dist signature
```

## Activer les logs détaillés

## Utilisation avancée pour le développement 

### Activer les logs détaillés

UnicaenSignature permet de tracer les opérations effectuées par le module en utilisant **Monolog**, vous pouvez l'activer dans la configuration : 

```php
<?php
@@ -82,18 +127,72 @@ php bin/commands.php
return [
    'unicaen-signature' => [
        'logger' => [
            ///////////////////////////////////////
            // Activation d'un logger autonome
            'enable'          => true, // Actif
            'level'           => \Monolog\Logger::DEBUG, // Niveau de log
            'enable' => false, // Ecriture des logs dans un fichier
            'file' => '/tmp/unicaen-signature.log', // Emplacement du fichier
            'stdout' => true // Envoi des logs dans les STDOUT pour les serveurs built-in 
            'file'            => __DIR__ . '/../../logs/signature.log', // Fichier d'écriture
            'file_permission' => 0666,

            ///////////////////////////////////////
            /// Sortie standard (pour le développement le built-in serveur)
            'stdout'          => false,

            ///////////////////////////////////////
            /// Logger complémentaire (celui de l'application utilisant le module)
            /// -> implementation de LoggerInterface (ex: Monolog)
            'customLogger' => null
            //'customLogger'    => 'Logger' // customLogger (LoggerInterface) 
        ],
```

## Développer un parapheur
Le fichier `logs/signature.log` doit pouvoir être créé/écrit, il contiendra des logs détaillés selon le niveau de log définit dans `level`.


### Ajouter les log de signature à vos logs (Monolog)

La clef `customLogger` vous permet de renseigner votre service de log si vous en utilisez un (un `Logger` de **Monolog**).


### Archiver les échanges avec le parapheur

## 
Vous pouvez également archiver les échanges avec le parapheur avec l'option `archive_exchange` (Si l'implementation du parapheur la prend en charge).

Exemple pour ESUP : 

```php
<?php
// config/autoload/unicaen-signature.local.php 
use Psr\Container\ContainerInterface;

return [
    /**  **/
    'unicaen-signature' => [
        // ...
        // Configuration des parafeurs numérique
        'letterfiles'            => [
            /************/
            [
                // Nom visible côté applicatif
                'label' => 'ESUP',

                // ...

                // [DEV] Emplacement où sont archivé les échanges de données avec le parapheur
                'archive_exchange' => __DIR__.'/../../logs/signature_exchange',

                // ...
            ]
        ]
    ]
];
```

> Attention, TOUTES les transactions avec ESUP seront archivées avec les données brutes reçu du parapheur. Cette option n'est a utiliser que pour auditer un bug ou pour le développement

## Développer un parapheur

TODO

## Développer l'UI avec VueJS

@@ -102,3 +201,5 @@ return [
 - Node 19

### Serveur Vite

TODO

doc/dev/commands.md

0 → 100644
+71 −0

File added.

Preview size limit exceeded, changes collapsed.

+143 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading