Brouillon de notes
Recherche documentaire sur les différents concepts en jeu pour l'approvisionnement et la synchronisation des référentiels (dans le cadre de l'authentification et de la gestion des identités)

Stage P.Marillonnet

Compilation :

`markdown doc.md > doc.html

# TODOs
* Rédiger au propre les parties suffisamment documentées.
* Scripts création markdown temporaire avec encodage html correct ?? (accents et caractères spéciaux)
* Pour chaque projet libre, présenter un court historique de la naissance du projet (quels besoins ? quels instigateurs ? quelle licence ? quelle année ? quel(s) prédecesseur(s) ?)
* Etudier préface du 'lasso book': concepts intéressants à expliquer + pdf lasso
* Placer au fur et a mesure le contenu dans le rapport (draft_report\d*.md)
* Tests avec tox, cf la structure des fichiers tox.ini

# Avant de commencer

## Acronymes

# Annuaires
//BOOKMARK

## SupAnn

## PABX ?
## NIS ?
## DNS/Whois ? (pas vraiment de la GI ?)

# Mécanismes d'authentification et implémentations
## SASL (RFC2222)
TODO Paul
## OpenID Connect
Utilisé pour la plateforme FranceConnect
Plus facile à mettre en place que des solutions basées SAML ?

# Identity Management

## Principes généraux

## Approvisionnement

## Implémentations
# SSO (et SLO)

# Synchronisation

# Fédération d'identités
## Présentation
Concept très propre à la France
Contraintes imposées par la Loi Informatique et Libertés (cf CNIL)
techno-agnostic côté SP
transparence pour l'utilisateur
utilisation de la page de login de l'organisme de l'utilisateur pour l'identification et l'authentification sur le site ayant mis en place la fédération d'identités

Interdiction d'un croisement implicite des données, sans en avertir l'utilisateur

Plus grande probabilité de véracité des informations utilisateur
Ces infos proviennent de sources sûres et à jour

TODO un SVG à faire ici, expliquant les mécanismes de la FI

TODO Relever les cas d'utilisation dans l'administration, les collectivités, et le monde de l'enseignement supérieur
## Standards
### Shibboleth
Appli Java
Dépendances :
Sprint
OpenSAML
Licence Apache

# Contrôle d'accès

## XACML
//TODIG

## ALFA
//TODIG

# Formulaires

# POC
## Presentation
Dans le cadre du projet Campus Condorcet, les annuaires de différents établissements doivent être regroupés en un méta-annuaire central. Aussi des problématiques d'homogénéisation de données hétérogènes se pose.

La phase de travail préparatoire à la réalisation du projet a consisté en le développement d'un POC (*Proof Of Concept*) permettant d'illustrer la procédure de constitution du méta-annuaire.

Le POC implémente un mécanisme simple d'ajout d'un compte invité ayant recours à un fournisseur de service, qui lui même repose sur un fournisseur d'identité SAML2 configuré pour assurer le SSO (*Single Sign-On*).

## Déroulement de l'ajout du compte invité dans l'application POC

Suivant un scenario classique de SSO, l'authentification depuis le fournisseur de service (SP) est déléguée à un fournisseur d'identité (IdP). Le fournisseur d'identité conclut la phase d'authentification par le renvoi d'une assertion SAML validant l'authentification.

De nombreuses informations présentes dans l'assertion SAML seront ensuites réutilisées par le fournisseur de service. Le contenu complet de l'assertion SAML est consultable en annexes de ce document, à titre indicatif. On remarquera la normalisation de la majorité des entrées (noeuds, attributs, corps) XML des messages SAML.

En particulier, les champs SAML permettant d'identifier l'utilisateur connecté sont récupérés de l'assertion. Ces champs permettent de déterminer si l'utilisateur n'est pas déjà renseigné dans le méta-annuaire OpenLDAP (annuaire d'aggrégation des référentiels d'identités des établissements partenaires du projet).

Si l'utilisateur ne se trouve pas dans le méta-annuaire, la procédure d'ajout du compte doit être effectuée. Un formulaire est alors proposé à l'utilisateur, il lui permet de valider ses données d'identités et de choisir une unité d'affectation.

La création et la gestion du formulaire sont prises en charge par l'outil w.c.s. Cet outil se base sur la notion de workflow, c'est-à-dire la cohabitation de procédures automatisées de traitement de l'information d'un part, et le recours à des actions manuelles de la part d'agents d'autre part.

Une fois le formulaire rempli par l'utilisateur, le workflow est configuré pour l'envoi automatique d'un email à un agent, pour la confirmation de création du compte.

Le plugin Passerelle permet la communication avec l'annuaire, permettant l'ajout de l'entrée LDAP correspondant au compte nouvellement créé.

L'appel au plugin Passerelle doit être effectué en tant que phase du workflow. La validation de la demande doit être rendue possible par la génération d'un lien envoyé par email à l'agent.

TODO modification du contenu du message, pour ajouter la possibilité d'envoyer les données directement dans l'annuaire.
FIXME quelle marge de manoeuvre sur la modification et l'ajout de données utilisateurs par l'agent ?

L'agent reçoit un email dont le contenu est :
```
Bonjour,

Un nouveau formulaire a =C3=A9t=C3=A9 compl=C3=A9t=C3=A9, vous pouvez le co=
nsulter
en suivant ce lien :
  http://wcs.example.com/backoffice/management/traitement/16/


Pour r=C3=A9f=C3=A9rence, voici le d=C3=A9tail du formulaire :

Email=C2=A0:
  toto@nowhere.null

Prenom=C2=A0:
  toto

Nom=C2=A0:
  t

NameID=C2=A0:
  tt

Unite d'affectation=C2=A0:
  hello
```

Par ailleurs, l'ajout d'un connecteur LDAP dans le plugin Passerelle permet de valider le formulaire et decréer l'entrée LDAP correspondante, par une requête GET sur l'URL:
http://<serveur w.c.s>/wcs/<numero id reponse formulaire>

La modification de l'email généré se fait à l'aide de la classe EmailsDirectory défini dans le code source de w.c.s. (cf wcs.git/wcs/forms.py)

Cette classe est chargée d'enregistrer des templates d'email envoyés en fonction de la configuration du workflow associé au formulaire.

Nous retrouvons le template de l'email ci-dessus dans le fichier formdefs, à la ligne 1276.
Le template est complété à l'aide de variables passées en paramètres. Elles sont pour l'instant au nombre de trois: name, url, details.

TODO Nous étudions maintenant la possibilité d'ajouter une quatrième variable correspondant à l'URL de validation exposée par l'API Passerelle.


```
./backoffice/management.py:33:from qommon.admin.emails import EmailsDirectory
./backoffice/management.py:72:        mail_subject = EmailsDirectory.get_subject('tracking-code-reminder')
./backoffice/management.py:73:        mail_body = EmailsDirectory.get_body('tracking-code-reminder')
```

Cf qommon/emails.py
   qommon/admin/emails.py

L'envoi de l'email est effectué dans wcs.git/wcs/workflows.py

Une option rapide a mettre en place: rajouter dans le formulaire l'URL de validation de celui-ci (validation au sens de Passerelle, une URL localhost:8007/wcs/<id>


Problème non réentrance des vues du SP si inférence du numéro d'identifiant de la réponse au formulaire. Cette donnée ne devrait pas être manipulée avant création de l'entrée par WCS.
C'est un ID autoincrémenté, mais cela pose quand même problème.

./lib/python2.7/site-packages/quixote/publish.py:291:def get_publisher():

Localiser la fonction de génération et d'envoi de l'email

dans wcs/qommon/emails.py

L'email est modifiable dans l'onglet Paramètres de l'interface backoffice de w.c.s.

```
Bonjour,

Un nouveau formulaire a été complété, vous pouvez le consulter
en suivant ce lien :
  [form_url_backoffice]

  Si nécessaire, vous pouvez approuver la demande en cliquant sur le lien http://localhost:8007/ldap/test3/wcs/[form_number]/

  [if-any details]
  Pour référence, voici le détail du formulaire :

  [details]
  [end]
```

Pas de modif ?

Pistes d'identifications du bug:
- regeneration des fichiers de traduction .po ? non
- basculer GNOME en anglais pour forcer l'envoi du mail dans la locale adéquate ? non
- installation de wcs à l'aide du paquet Debian ? non
- modification en dur du template de l'email dans le code ? non
- prise en compte de modifs avec l'utilitaire wcsctl.py ? non
- rechargement de la config Apache ? non
- mauvaise conf SMTP ? voir exim4-light ? peu probable ... TODO
- template de l'email persistant, dans la base ? à voir, la commande wcsctl.py shell ne fonctionne pas pour l'instant TODO
- inspection pdb ? à voir TODO
- externalisation des templates d'email dans un repertoire extérieur au projet ? TODO
- exploration de la base, des migrations, pour comprendre la façon dont sont stockées les objets de template d'emails


Debugger : le template de l'email semble avoir été enrgistré:
```
(Pdb) EmailsDirectory.emails_dict
{'new_user': {'category': 'Workflow', 'default_body': 'Hello,\n\n[if-any user]\nThis mail is a reminder about the form you just submitted; you can consult it\nwith this link: [url]\n[else]\nThis mail is a reminder about the form you just submitted.\n[end]\n\n[if-any details]\nFor reference, here are the details:\n\n[details]\n[end]\n', 'description': 'Notification of creation to user', 'hint': None, 'enabled': False, 'default_subject': 'New form ([name])'}, 'change_user': {'category': 'Workflow', 'default_body': 'Hello,\n\n[if-any form_status_changed]\nStatus of the form you submitted just changed (from "[before]" to "[after]").\n[end]\n\n[if-any user]\nYou can consult it with this link:\n[url]\n[end]\n\n[if-any form_comment]New comment: [form_comment][end]\n\n[if-any evolution]\n[evolution]\n[end]\n', 'description': 'Notification of change to user', 'hint': 'Available variables: user, url, before, after, evolution', 'enabled': False, 'default_subject': 'Form status change'}, 'new_receiver': {'category': 'Workflow', 'default_body': 'Hello,\n\nA new form has been submitted, you can see it with this link:\n[form_url_backoffice]\n\nIf necessary, please validate the submission by visiting:\nhttp://localhost:8007/ldap/test3/wcs/[form_number]/\n\n[if-any details]\nFor reference, here are the details:\n\n[details]\n[end]\n', 'description': 'Notification of creation to receiver', 'hint': 'Available variables: name, url, details', 'enabled': False, 'default_subject': 'New form ([name])'}}
(Pdb)
```

Pas d'anomalie detectee au debugger Python pour l'instant

Stack trace:
```
(Pdb) w
  /home/paul/devel/wcs/wcsctl.py(8)<module>()
  -> ctl.run(sys.argv[1:])
    /home/paul/devel/wcs/wcs/qommon/ctl.py(159)run()
    -> return cmd.run(args, options)
      /home/paul/devel/wcs/wcs/qommon/ctl.py(69)run()
      -> return self.execute(base_options, sub_options, args)
        /home/paul/devel/wcs/wcs/ctl/start.py(69)execute()
        -> import publisher
          /home/paul/devel/wcs/wcs/publisher.py(39)<module>()
          -> from root import RootDirectory
            /home/paul/devel/wcs/wcs/root.py(37)<module>()
            -> import forms.root
              /home/paul/devel/wcs/wcs/forms/root.py(49)<module>()
              -> from wcs.formdef import FormDef
              > /home/paul/devel/wcs/wcs/formdef.py(1299)<module>()
              -> EmailsDirectory.register('change_receiver', N_('Notification of change to receiver'),
```

Pas d'anomalie non plus sur la pile; c'est bien les sources qui sont executees et non le paquet Debian annexe

Il faudrait maintenant inspecter la trace de la fonction chargee d'envoyer l'email, pour vérifier la façon dont le template est récupéré.
Comment mentionné précédemment, cette fonction est implementée dans le fichier wcs.git/wcs/common/emails.py

Pour l'instant, impossible de récuperer un shell dans ce fichier à l'aide de pdb.set_trace()

L'exécution de la commande
python setup.py install
ne résout pas non plus le problème.

Finalement, la modification du template est rendue possible par les icônes cliquables dans la section "Fabrique de workflow".
L'action à paramétrer ici est "Juste envoyé" -> "Envoyer un courriel à Destinataire"
Nous paramétrons l'email envoyé pour qu'il soit de la forme:
```
Bonjour,

Un nouveau formulaire a été complété, vous pouvez le consulter
en suivant ce lien :
  [form_url_backoffice]


Si necessaire, veuillez valider la demande en visitant le lien :
http://localhost:8007/ldap/test3/wcs/[form_number]/

[if-any details]
Pour référence, voici le détail du formulaire :

[details]
[end]
```
//TODO template pas très pro
L'idéal serait de définir une variable *validation_url*, pour ne pas avoir à exposer l'URL de l'API de Passerelle directement dans le template de l'email.
Nous pouvons maintenant nous pencher sur le scénario du cas d'utilisation décrit dans le diagramme de séquence.

L'agent doit pouvoir compléter les données relatives à l'utilisateur, ce qui signifique que le formulaire doit contenir des champs non remplissables par un utilisateur non authentifié.
TODO Est-il alors nécessaire de comprendre comment est effectuée la gestion des droits dans wcs ?

Reduction du workflow à deux états, c'est suffisant pour ce que nous souhaitons faire.

Ajout de champs 'Bureau CC' et 'Téléphone CC' : infos inconnues de l'utilisateur inscrivant son compte invité.

Scenario d'utilisation relativement complet pas rapport au diagramme de séquence : passage à l'intégration Gadjo ?

TODO LIST pour l'instant
- Message d'explication et inclusion charte graphique pour le HTTP 403 associé au décorateur @user_not_in_ldap
- Gadjo dans w.c.s
- Paramétrage d'un client mail pour la récupération automatique des emails dans ~/Maildir ?
- Serveur SMTP envoyant des mails à une adresse effective, pas simplement dans ~/Maildir ?
- Traitement du champ EduPersonPrincipalName (EPPN) tel que défini intialement dans le schéma ?
- Compte-rendu POC avec captures d'ecran ?

2
bug  collectstatic pas fonctionnel dans gadjo
Definition de STATIC_ROOT dans le fichier settings.py ?


Note de mise en place du POC

### Préparation déploiement:
7 briques majeures:
- SP
- IDP
- Gestion formulaires (WCS)
- Base de connecteurs (Passerelle)
- Serveur http (Apache)
- Annuaire OpenLDAP (slapd)
- serveur de mail (exim4)

Problème à gérer : pour l'instant pas de virtualenv commun aux 4 applications basées Django

#### SP
Importer le sous directory seulement ? Tout le virtualenv ? OUI
Pas de config https vers authentic, est-ce un problème ?
Homogénéisation des urls ?

Déploiement avec changement de l'URL

Pb: pas de fichier setup.py par défaut, il va falloir le créer


#### IDP
Créer un fichier de peuplement ?
Export des données en local ?

Pb: mauvaise connaissence du format d'archive 'egg' pour les modules pythons
L'utilitaire authentic2-ctl semble vouloir accéder à un dossier suffixé par l'extension 'egg', laquelle symbolise une format de compression...
TODO troubleshooting...

```
System check identified no issues (0 silenced).
Unhandled exception in thread started by <function wrapper at 0x7f038144d848>
Traceback (most recent call last):
File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/utils/autoreload.py", line 229, in wrapper
fn(*args, **kwargs)
File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/core/management/commands/runserver.py", line 116, in inner_run
self.check_migrations()
File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/core/management/commands/runserver.py", line 168, in check_migrations
executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/db/migrations/executor.py", line 19, in __init__
self.loader = MigrationLoader(self.connection)
File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/db/migrations/loader.py", line 47, in __init__
self.build_graph()
File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/db/migrations/loader.py", line 185, in build_graph
self.load_disk()
File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/db/migrations/loader.py", line 93, in load_disk
for name in os.listdir(directory):
OSError: [Errno 20] Not a directory: '/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django_mellon-1.2.29-py2.7.egg/mellon/migrations'
```

Bugfix : suppression du fichier egg, réinstallation de django-mellon à l'aide du pip dans le virtualenv

Issue : plusieurs instances de projets différents déployés sur un même host
TODO Recherche sur la config WSGI dans Apache2

Lecture de la doc dans le dossier Debian des sources Authentic
-> Pas concluant

Essayer nginx et gunicorn ? option --config de authentic2-ctl

Problème de timeout sur gunicorn

Troubleshoot:
Causes possibles:
- Appli Django ? Peu problable : fonctionne en local...
- socket Gunicorn <-> nginx ?  La conf a l'air relativement simple
- mauvaise configuration du serveur nginx ? Le timeout provient du worker gunicorn
- structuration des sources authentic différente d'un projet django standard :
    * les applications sont situés dans src, et non pas directement à la racine du projet django
    * il n'y a pas de fichier manage.py, mais un script shebang authentic2-ctl
- la configuration authentic pour gunicorn présume que authentic
- problème avec le virtualenv ? pourtant le fichier gunicorn.sh laisse pense que la mise en place
d'un environnement virtuel est prise en compte par gunicorn ,
- problème de génération et de collecte des fichiers statiques ?
- le module de gestion des scss est-il installée ?
- incompatibilité lors du transfert du virtlanev local vers la dev ?

Identification des causes:
* Fichier de log gunicorn ? Log en mode debug et pourtant rien de pertinent n'apparait...
* Fichiers de log nginx : laisse à penser que la confi nginx est correct:  rien de suspect...

Update :
```
2017/03/08 16:37:12 [error] 14502#0: *19 upstream prematurely closed connection while reading response header from upstream, client: 192.168.44.1, server: condorcet.dev.entrouvert.org, request: "GET / HTTP/1.0", upstream: "fastcgi://127.0.0.1:8000", host: "condorcet.dev.entrouvert.org"
```
-> L'erreur semble bel et bien provenir de gunicorn, la connexion est stoppée.
Revenir a une config Apache et libabache2_mod_wsgi ? l'erreur se posera aussi, non ?
Apache2 est-il plus souple en terme de front pour une application Django de structure non usuelle (pas de fichier manage.py ?)

Le plus logique serait de lire la documentation gunicorn pour comprendre ce qui ne va pas
Il faut aussi comprendre laquelle des deux installations de gunicorn est la plus adaptée :
- avec pip dans le virtualenv ?
- avec apt-get dans l'environnement glogal du conteneur ?

Y a-t-il des différences dans la gestion des fichiers statiques par Apache et nginx ?
Comprendre l'interface Django <-> Apache par WSGI
peut-être plus simple à comprendre pour une seule appli, par rapport à nginx ?

Avec Apache2  et mod_wsgi : 403 forbidden:
- possibles ? pas de certificats ?

TODO : reactiver le mode de debugging ?

Problème de cohabitation Apache et nginx

Les VirtualHost Apache seuls ne semblent pas adaptables ?

Comment faire dans le cas où les projets sont installés à partir des paquets Debian ?

Déploiement manuel et configuration des VirtualHost pas évidente...

Les deux serveurs écoutent chacun sur des ports TCP différents :
```
Proto Recv-Q Send-Q Adresse locale          Adresse distante        Etat        PID/Program name
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      2973/nginx -g daemo
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1545/apache2
```

Apache WCS OK :
```
wget condorcet.dev.entrouvert.org
--2017-03-10 15:17:14--  http://condorcet.dev.entrouvert.org/
Resolving condorcet.dev.entrouvert.org (condorcet.dev.entrouvert.org)... 188.165.196.219
Connecting to condorcet.dev.entrouvert.org (condorcet.dev.entrouvert.org)|188.165.196.219|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 561 [text/html]
Saving to: ‘index.html’

index.html                                            100%[======================================================================================================================>]     561  --.-KB/s    in 0s

2017-03-10 15:17:14 (101 MB/s) - ‘index.html’ saved [561/561]
```

nginx Authentix nettement moins OK :
```
wget condorcet.dev.entrouvert.org:8080
--2017-03-10 15:17:18--  http://condorcet.dev.entrouvert.org:8080/
Resolving condorcet.dev.entrouvert.org (condorcet.dev.entrouvert.org)... 188.165.196.219
Connecting to condorcet.dev.entrouvert.org (condorcet.dev.entrouvert.org)|188.165.196.219|:8080...
```

On essaie alors en local, sur la dev, de générer des logs :
```
root@condorcet:~# wget localhost:8080
--2017-03-10 15:24:11--  http://localhost:8080/
Résolution de localhost (localhost)… ::1, 127.0.0.1
Connexion à localhost (localhost)|::1|:8080… échec : Connexion refusée.
Connexion à localhost (localhost)|127.0.0.1|:8080… connecté.
requête HTTP transmise, en attente de la réponse… Aucune donnée reçue.
Nouvel essai.

--2017-03-10 15:24:12--  (essai :  2)  http://localhost:8080/
Connexion à localhost (localhost)|127.0.0.1|:8080… connecté.
requête HTTP transmise, en attente de la réponse… Aucune donnée reçue.
Nouvel essai.

--2017-03-10 15:24:14--  (essai :  3)  http://localhost:8080/
Connexion à localhost (localhost)|127.0.0.1|:8080… connecté.
requête HTTP transmise, en attente de la réponse… Aucune donnée reçue.
Nouvel essai.

--2017-03-10 15:24:17--  (essai :  4)  http://localhost:8080/
Connexion à localhost (localhost)|127.0.0.1|:8080… connecté.
requête HTTP transmise, en attente de la réponse… Aucune donnée reçue.
Nouvel essai.

--2017-03-10 15:24:21--  (essai :  5)  http://localhost:8080/
Connexion à localhost (localhost)|127.0.0.1|:8080… connecté.
requête HTTP transmise, en attente de la réponse… Aucune donnée reçue.
Nouvel essai.
```

Le serveur renvoie des HTTP 444 :
```
root@condorcet:/etc/nginx# cat /var/log/nginx/access.log
127.0.0.1 - - [10/Mar/2017:15:24:11 +0100] "GET / HTTP/1.1" 444 0 "-" "Wget/1.16 (linux-gnu)"
127.0.0.1 - - [10/Mar/2017:15:24:12 +0100] "GET / HTTP/1.1" 444 0 "-" "Wget/1.16 (linux-gnu)"
127.0.0.1 - - [10/Mar/2017:15:24:14 +0100] "GET / HTTP/1.1" 444 0 "-" "Wget/1.16 (linux-gnu)"
127.0.0.1 - - [10/Mar/2017:15:24:17 +0100] "GET / HTTP/1.1" 444 0 "-" "Wget/1.16 (linux-gnu)"
127.0.0.1 - - [10/Mar/2017:15:24:21 +0100] "GET / HTTP/1.1" 444 0 "-" "Wget/1.16 (linux-gnu)"
```

Ce qui correspond à la conf de
/etc/nginx/conf.d/return-444-if-no-host-header.conf

Pas de host header
wsgi cassé ?

```
root@condorcet:~# find / -name authentic2
/var/log/authentic2
/var/lib/authentic2
/var/lib/authentic2/collectstatic/authentic2
/var/cache/authentic2
/etc/cron.d/authentic2
/etc/logrotate.d/authentic2
/etc/nginx/sites-enabled/authentic2
/etc/nginx/sites-available/authentic2
/etc/authentic2
/etc/init.d/authentic2
/etc/cron.hourly/authentic2
/usr/share/authentic2
/usr/share/doc/authentic2
/usr/share/dbconfig-common/scripts/authentic2
/usr/lib/authentic2
/usr/lib/python2.7/dist-packages/authentic2
/usr/lib/python2.7/dist-packages/authentic2/manager/templates/authentic2
/usr/lib/python2.7/dist-packages/authentic2/manager/static/authentic2
/usr/lib/python2.7/dist-packages/authentic2/templates/authentic2
/usr/lib/python2.7/dist-packages/authentic2/static/authentic2
/usr/lib/python2.7/dist-packages/hobo/agent/authentic2
/run/authentic2
```

Authentic écoute bien en local sur le port 8000
`tcp        0      0 127.0.0.1:8000          0.0.0.0:*               LISTEN      7654/python`

```
 ps -aux | grep 7654
 root      4239  0.0  0.0  12628  1076 pts/4    R+   15:48   0:00 grep 7654
 authent+  7654  1.5  2.7 253900 56740 pts/2    Sl   mars09  21:41 /usr/bin/python /usr/lib/authentic2/manage.py runserver
 ```

Le code de retour 444 ne semble pas symboliser une erreur côté serveur mais plutôt une incohérence dans la requête du client (champ Host de la requête HTTP laissé vide ?)
Pourtant le champ HOST est bien rempli dans a requête GET:
Host: "condorcet.dev.entrouvert.org:8080"

```
root@condorcet:~# wget condorcet.dev.entrouvert.org:8080
--2017-03-10 16:10:44--  http://condorcet.dev.entrouvert.org:8080/
Résolution de condorcet.dev.entrouvert.org (condorcet.dev.entrouvert.org)… 192.168.43.32
Connexion à condorcet.dev.entrouvert.org (condorcet.dev.entrouvert.org)|192.168.43.32|:8080… connecté.
requête HTTP transmise, en attente de la réponse… 502 Bad Gateway
2017-03-10 16:10:44 erreur 502 : Bad Gateway.
```

```
2017/03/10 16:10:44 [error] 4569#0: *2 upstream sent unsupported FastCGI protocol version: 60 while reading response header from upstream, client: 192.168.43.32, server: condorcet.dev.entrouvert.org, request: "GET / HTTP/1.1", upstream: "fastcgi://127.0.0.1:8000", host: "condorcet.dev.entrouvert.org:8080"
```

Après simplification de la conf:
```
># wget condorcet.dev.entrouvert.org:8080
--2017-03-10 16:15:53--  http://condorcet.dev.entrouvert.org:8080/
Résolution de condorcet.dev.entrouvert.org (condorcet.dev.entrouvert.org)… 192.168.43.32
Connexion à condorcet.dev.entrouvert.org (condorcet.dev.entrouvert.org)|192.168.43.32|:8080… connecté.
requête HTTP transmise, en attente de la réponse… 200 OK
Taille : non indiqué [text/html]
Sauvegarde en : « index.html »

index.html                                                [ <=>                                                                                                                     ]   1,89K  --.-KB/s   ds 0s    

2017-03-10 16:15:54 (166 MB/s) - « index.html » sauvegardé [1939]
```

Prochaine étape:
Distant
D'abord depuis mesclun ?

FIX : utilisation de sous domaines
séparateur '.' ne fonctionne pas, en revanche '-' fonctionne
(pydns)

utilisation de dig :
```
# dig condorcet.dev.entrouvert.org

; <<>> DiG 9.9.5-9+deb8u10-Debian <<>> condorcet.dev.entrouvert.org
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12770
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;condorcet.dev.entrouvert.org.  IN  A

;; ANSWER SECTION:
condorcet.dev.entrouvert.org. 60 IN A   188.165.196.219

;; AUTHORITY SECTION:
dev.entrouvert.org. 228 IN  NS  ns.dev.entrouvert.org.

;; ADDITIONAL SECTION:
ns.dev.entrouvert.org.  228 IN  A   188.165.196.219

;; Query time: 6 msec
;; SERVER: 5.135.221.23#53(5.135.221.23)
;; WHEN: Fri Mar 10 16:40:26 CET 2017
;; MSG SIZE  rcvd: 106

root@condorcet:/var/log/nginx# dig connexion-condorcet.dev.entrouvert.org

; <<>> DiG 9.9.5-9+deb8u10-Debian <<>> connexion-condorcet.dev.entrouvert.org
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12958
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;connexion-condorcet.dev.entrouvert.org.    IN A

;; ANSWER SECTION:
connexion-condorcet.dev.entrouvert.org. 60 IN A 188.165.196.219

;; AUTHORITY SECTION:
dev.entrouvert.org. 204 IN  NS  ns.dev.entrouvert.org.

;; ADDITIONAL SECTION:
ns.dev.entrouvert.org.  204 IN  A   188.165.196.219

;; Query time: 8 msec
;; SERVER: 5.135.221.23#53(5.135.221.23)
;; WHEN: Fri Mar 10 16:40:50 CET 2017
;; MSG SIZE  rcvd: 116
```

ip addr add dev venet0 192.168.43.33/32

Modification de /etc/hosts pour la prise en compte des deux IP:
```
192.168.43.32 condorcet.dev.entrouvert.org  condorcet
192.168.43.33 idp-condorcet.dev.entrouvert.org  condorcet
::1     localhost ip6-localhost ip6-loopback
```

Avec netstat on a alors :
```
Connexions Internet actives (seulement serveurs)
Proto Recv-Q Send-Q Adresse locale          Adresse distante        Etat        PID/Program name
tcp        0      0 192.168.43.33:80        0.0.0.0:*               LISTEN      6232/nginx -g daemo
tcp        0      0 192.168.43.32:80        0.0.0.0:*               LISTEN      6075/apache2   
```

TODO Puppet et repo git pour la conf ?

BLOCKER: installation authentic2 a partir du paquet debian non fonctionnelle, pas de logs
-> Désinstallation
WORKAROUND: faire tourner authentic à partir des sources, avec ./authentic2-ctk runfcgi

Pb de configuration ?
Revenir une énième fois à gunicorn ?

$ ./authentic2-ctl runfcgi method=threaded host=127.0.0.1 port=8080
Creation d'un thread

TODO quelles différences avec le mode prefork ?

Unhandled exception -> Quels logs ?

L'exception provient de flup:
```
2017/03/14 10:14:26 [error] 18062#0: *7 FastCGI sent in stderr: "Traceback (most recent call last):
  File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/flup/server/fcgi_base.py", line 558, in run
    protocolStatus, appStatus = self.server.handler(self)
  File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/flup/server/fcgi_base.py", line 1118, in handler
    result = self.application(environ, start_response)
  File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/core/handlers/wsgi.py", line 189, in __call__
    response = self.get_response(request)
  File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 207, in get_response
    return debug.technical_500_response(request, *sys.exc_info(), status_code=400)
  File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/views/debug.py", line 97, in technical_500_response
    html = reporter.get_traceback_html()
  File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/views/debug.py", line 384, in get_traceback_html
    return t.render(c)
  File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/template/base.py", line 210, in render
    return self._render(context)
  File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/template/base.py", line 202, in _render
    return self.nodelist.render(context)
  File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/template/base.py", line 905, in render
    bit = self.render_node(node, context)
  File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/template/debug.py", line 79, in render_node
    return node.render(context)
  File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/template/defaulttags.py", line 329, in render
    return nodelist.render(context)
  File "/home/pmarillonnet/devel/venv/local/lib/python2.7/site-packages/django/template/base.py", line 905, 

``` 

=> Retour à gunicorn, voir si ça change quoi que ce soit
Ok gunicorn
il faut vieiller à réinstaller le package (/usr/lib/python2.7/dist-packages)
Installation Authentic fonctionnelle


#### WCS
Un simple export global devrait suffire
Pb : WCS configuré avec Apache pour l'instant
Ici seul nginx est utilisé !
Mais WCS necessaire en local seulement ? Oui, c'est passerelle qui doit être accessible depuis l'extérieur
WCS en backend seulement

pb: upload de la conf avec lynx
Ecouter sur le port 80 juste pour l'upload de la conf ?
Oui, une fois la config faite, on le rebascule, en local seulement, sur 8080

2 choix:
- serveur http embarqué dans wcs ?
    -> Ce n'est pas la solution optimale
- config scgi DONE

Timeout sur l'interrogation de l'annuaire
TODO
Dummy git repo
C'est le décorateur qui timeout


#### Passerelle
Export du connecteur créé

Plus complexe que le l'IdP et le gestionnaire de formulaire
Cette fois-ci une installation à partir du paquet Debian paraît difficilement réalisable, pour la raison que les sources doivent être patchées afin de pouvoir incorporer le connecteur LDAP.
Mais le projet présente l'avantage d'un structuration plus 'canonique', ce qui facilite la recherche de documentation générique sur le déploiement des projets Django et de la conf WSGI.

On essaie alors de configurer un nouveau VirtualHost Apache avec l'une des adresse privées attribuées à l'interface virtuelle du conteneur

```
$ ip a s                                                                                                  │
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN                                       │
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00                                                 │
    inet 127.0.0.1/8 scope host lo                                                                        │
    inet6 ::1/128 scope host                                                                              │
       valid_lft forever preferred_lft forever                                                            │
2: venet0: <BROADCAST,POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN                 │
    link/void                                                                                             │
    inet 127.0.0.2/32 scope host venet0                                                                   │
    inet 192.168.43.32/32 brd 192.168.43.32 scope global venet0:0                                         │
    inet 192.168.43.33/32 scope global venet0                                                             │
    inet 192.168.43.34/32 scope global venet0                                                             │
    inet 192.168.43.35/32 scope global venet0                                                             │
```

Première étape : runserver local sur le port 8080
2e étape : Apache + mod_wsgi
3e etape (post déploiement) : réécriture plus propre du connecteur, en exploitant les fonctionnalités *endpoints* de Passerelle

1. Ok sans virtualenv (c'est ce qui est plus approprié pour une machine en dev)
2. Utilisation du mode daemon de wsgi pour l'exécution en parallèle de plusieurs backends Django sur un seul environnement python.

UPDATE: pas besoin d'exposer l'interface Web de Passerelle
utilisation backend seulement.
Conf ok, juste les URLs codées 'en dur' dans les sources, à modifier

BLOCKER: Connecteur LDAP plus reconnu par l'appli
Pourtant les migrations ont été appliquées

FIX?: suppression des restes de l'installation globale passerelle (/usr/lib/python2.7/dis-packages/passerelle) avec apt-get remove --purge passerelle python-passerelle
Non, toujours cette même erreur
Les sources ne sont-elles pas installées ailleurs ?

Pb de conflit avec le virtualenv initial
Reinstallation de l'appli

FIX: ne pas redefinir get_add_url dans le modèle du connecteur hérité de BaseResource, les underscores sont transformés en dashes...
c'est ldap-dir qui apparait dans les URLs, et non pas ldap_dir


#### Serveur HTTP
Config SSL/TLS pas utilisée
Revoir la config wcs, ie la façon dont l'application est repartie entre source, app_dir et data_dir

#### Annuaire OpenLDAP
Retrouver le fichier de peuplement de l'annuaire ? Pas nécessaire, juste besoin de recréer les noeuds intermédiaires de l'arborescence.

Installation slapd
TODO : retrouver les lignes de commande d'interrogation du LDAP
Fichier usual_ldap_commands

ldaputils pour le test en local...

Prise en compte du rebond ssh pour le transfert de fichier
ssh pour le lancement d'une commande sur la machine intermédiair

Creation d'un fichier de peuplement minimal
quelques groupes et une entrée bidon

```
# ldapadd -x -D "cn=admin,dc=condorcet,dc=dev,dc=entrouvert,dc=org" -w test -p 389 -h `hostname -f` -f /home/pmarillonnet/Documents/
misc/Example_shrunk.ldif
adding new entry "dc=org"
ldap_add: Server is unwilling to perform (53)
        additional info: no global superior knowledge


```
Ajout OK, avec comme fichier ldif:
```
idn: ou=Servers,dc=condorcet,dc=dev,dc=entrouvert,dc=org
objectClass: organizationalUnit
objectClass: top
description: Company Servers
ou: Servers

dn: ou=Groups,dc=condorcet,dc=dev,dc=entrouvert,dc=org
objectClass: organizationalunit
objectClass: top
ou: Groups

dn: ou=Managers,ou=groups,dc=condorcet,dc=dev,dc=entrouvert,dc=org
objectClass: top
objectClass: organizationalunit
ou: groups
description: People who can manage accounting entries
ou: Managers

dn: ou=People,dc=condorcet,dc=dev,dc=entrouvert,dc=org
objectClass: organizationalunit
objectClass: top
ou: People

dn: uid=jmorrison,ou=People,dc=condorcet,dc=dev,dc=entrouvert,dc=org
objectClass: person
#objectClass: cos
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: posixAccount
objectClass: top
uid: jmorrison
#classOfService: gold
userpassword: chevron
facsimiletelephonenumber: +1 408 555 4661
givenname: Jim
cn: Jim Morrison
telephonenumber: +1 408 555 9445
sn: Morrison
roomnumber: 2290
homeDirectory: /home/jmorrison
mail: jmorrison@example.com
l: Pere Lachaise
ou: People
uidNumber: 1119
gidNumber: 1000

```

On suppose que le dn global a été créé lors de la (re)configuration dpkg

#### Exim4
dpkg reconfigure pour obtenir la config souhaitée
faire une config pour l'envoi de mails vers une adresse réelle ? Dans /home/<user>/Maildir pour l'instant

Pb exim4 : les ports ne sont pas ouverts
Deux solutions
Workaround en passant tout de même par leucas ?
Configuration d'un serveur SMTP en local ? Préférable ?

Les messages ICMP echo request vers l'extérieur ne passent même pas.
Ok pb config réseau résolu par redémarrage de la VM
Déploiement OK

### Procédure de déploiement
- Tarballs -> OK
- Fonctionnement en local -> En cours

### Améliorations envisagables
Gadjo sur le SP
Gadjo sur la page finale passerelle
Adaptation SupAnn 2009

### Adaptation au schéma SupAnn 2009
classes de l'entrée:
- organization (RFC2798)
- dcObject (RFC2247)
- eduOrg
- supannOrg

avec les attributs:
- o
- supannEtablissement

Nous nous intéresserons ici à l'un des trois branches seulement de cette entrée : ou=People

Nous gérons les comptes invités en tant que personne, indépendamment de l'approvisionnement des groupes (`ou=groups`) et des structures (`ou=structures`)

Les entrées de la branche des personnes se base sur trois classes : inetOrgPerson en tant que classe structurelle et eduPerson et supannPerson en tant que classes auxiliaires.

L'import d'un compte invité dans le méta-annuaire doit tenir compte de l'établissement : l'attribut supannCodeEntite de la personne renvoie au supannCodeEntite de l'établissement et l'attribut supannEtablissement de la personne référence le code UAI.

On peut alors établir les différents champs qui seront présents dans le formulaire d'inscription du compte invité :
- nom
- prenom
- adresse email
- établissement
- affiliation principale (facultatif)
- affectation principale (facultatif)
- corps d'appartenance
- liste rouge

//TODO faut-il collecter aussi les infos concernant la personne invitante ?

Les attributs transmis lors du SSO vont servir au préremplissage du formulaire final.
Attention, certains de ces attributs ne sont pas éditables.
Au total, il y a en 11 :
- eduPersonPrincipalName (non éditable)
- surname
- givenName
- mail
- supannEtablissemnt
- eduPersonPrimaryAffiliation
- eduPersonAffiliation
- supannEntiteAffectationPrincipale
- supannEntiteAffectation
- supanEmpCorps
- supannListeRouge

L'absence d'une information obligatoire stoppe la procédure de soumission du formulaire.

Il faut maintenant gérer la transmission des attributs depuis l'IdP source.
Changements à répercuter :
le SP avec tout le code django-mellon
WCS lors de la définition des formulaires
les utilitaires basés python-ldap3 lors de la création de la requête vers le meta-annuaire

Bien sur la transmission des attributs dans l'IdP, via l'assertion SAML envoyée, doit tenir compte de l'adoption du schéma SupAnn 2009.

TODO comprendre comment est mis en place l'annuaire supann du côté de l'IdP source
=> Lecture de la doc Authentic2 PSL
http://doc.entrouvert.org/supann/

TODO : serveur LDAP avec inclusion du schéma SupAnn 2009
outil web de gestion des entrées LDAP -> LdapSaisie
Un IdP SAML2

TODO quelles normes pour la fédération d'identités Renater ?

Ici méta-annuaire décrit comme l'utilisation de plusieurs branches au sein du même annuaire, synchronisées vers un annuaire distant.

Utilisation de NTP pour la synchro des horloges

LdapSaisie : de quel côté l'installer ? sur condorcet ou en local ?
pb : appli apache php, pas de doc pour nginx

Essayer une installation PHP FPM ?

On passe cette étape pour l'instant : la configuration et la gestion de l'annuaire SUPANN2009 se fera en ligne de commande, comme pour le LDAP générique.

Ici utilisation d'une machine unique, même si en pratique il faudrait installer chaque composant sur des machines séparées... Façon plus propre de déployer une application multi-bloc

Le LDAP est déjà fonctionnel, alors comment inclure proprement le schéma supann ?
Lecture de la doc : installation de authentic2-supann ?

Le repo git ne contient que des conf et un script
La conf nginx est-elle différente de l'install authentic2 classique ?
Modifier le settings.py de authentic pour la configuration du backend db ?

Comment faire remonter les attributs supann dans la politique de transmission SAML d'arguments, dans la GUI Web de authentic2 ?
TODO adapter le config.py situé dans le dépôt ?

Pour l'instant le poc n'est pas en https, il fonctionne sans certificats.
Faut-il les générer pour que fonctionne la configuration supann ?

Récupération du schéme SupAnn 2009 pour OpenLDAP sur le site RENATER.
Ce n'est pas l'objectif principal pour l'instant : la mise en place du schéma côté annuaire semble plutôt légère, mais c'est le support IdP qui pour l'instant semble poser problème.

Y a-t-il forcément besoin d'un backend LDAP pour l'IdP du POC ?
Cela pose problème : l'installation est locale seulement
Faire un LDAP avec deux branches ? l'une pour l'annuaire local de l'IdP (côté invité) et l'autre pour le méta-annuaire (côté CC) ?

Ce qui est faisable pour l'instant : l'installation d'un OpenLDAP SupAnn pour le méta annuaire
Ce qu'on pourrait faire par la suite :
 - Cohabitation de deux slapd sur des ports différents ? Préférable
 - Un seul slapd qui gère deux branches différentes : l'une côté client et l'autre côté CC ? Difficile et pas pertinent...

1ere étape : installation du schéma
Cette install se fait par placement d'un fichier ldif dans les répertoires de configuration de slapd.
Ok, génération d'un LDIF que l'on va passer à ldapadd

```
 # slaptest -f ldap_includes.conf -F ldif_output/
 58e24560 /etc/ldap/schema/collective.schema: line 28 attributeType: AttributeType not found: "l"
 slaptest: bad configuration directory!
```

Pb réglé après modification de l'ordre de déclaration des différents schémas.
On peut alors, après modification de /cn=config/cn=schema/cn={8}misc.ldif telle que décrite dans
http://www.linuxquestions.org/questions/linux-server-73/how-to-add-a-new-schema-to-openldap-2-4-11-a-700452/
procéder à un ldapadd du ldif généré

```
ldapadd -x -D "cn=admin,dc=condorcet,dc=dev,dc=entrouvert,dc=org" -w test -p 389 -h condorcet.dev.entrouvert.org -f /home/pmarillonnet/Documents/misc/ldif_output/cn\=config/cn\=schema/cn\=\{8\}misc.ldif
```

renvoie une erreur 50 (insufficient access)
et pourtant

```
ldapsearch -D "cn=admin,dc=condorcet,dc=dev,dc=entrouvert,dc=org" -w test -p 389 -h condorcet.dev.entrouvert.org -b "ou=People,dc=condorcet,dc=dev,dc=entrouvert,dc=org" -s sub "(ObjectClass=*)" * +
```

s'exécute correctement.

La conf d'accès à l'administration de l'annuaire ne semble pas se trouver dans son emplacement par défaut
```
# grep -nr pw ./
# grep -nr root ./
#
```

On peut essayer d'utiliser slapadd en local au lieu de l'utilitaire client ldapadd ?

Résolution par ajout d'un mot de passe pour la configuration de l'annuaire
```
 # slappasswd -h {MD5}
 New password: 
 Re-enter new password: 
```
Ajout d'une ligne olcRootPW: {md5}...
dans /etc/ldap/slapd.d/cn\=config/olcDatabase\=\{0\}config.ldif
résout le problème :
```
$ ldapadd -x -D "cn=admin,cn=config" -w test -p 389 -h condorcet.dev.entrouvert.org -f /home/pmarillonnet/Documents/misc/ldif_output/cn\=config/cn\=schema/cn\=\{8\}misc.ldif
adding new entry "cn=misc,cn=schema,cn=config"
```

Pas certain cependant que l'adaptation supann ait été effectuée :
```
$ ldapsearch -D "cn=admin,cn=config" -w test -p 389 -h condorcet.dev.entrouvert.org -b "cn=config" -s sub "(ObjectClass=*)" * + | grep -i  supann
```
ne renvoie rien

La commande ldapadd ci-dessus ne semble avoir ajouté qu'une seule entrée (misc)

Revenons au fichier LDIF, quelque chose doit manquer
Une seule entrée en effet, l'opération slaptest s'est mal déroulée

Plantage dans la procédure : lire une documentation un peu plus spécifique à SupAnn : il ne s'agirait pas simplement d'ajouter un schéma.

On part vers quelque chose d'un peu plus détaillé, par exemple 
http://www.it-sudparis.eu/s2ia/user/procacci/ldap/Ldap_int018.html

TODO? eplucher le script perl
NO: il s'agit simplement d'un script de migration d'un annuaire générique au format supann

Maintenant: adaptation de la doc trouvée sur linuxquestions
le schema manipulé est supann_2009
on supprime le `{\d*}` comme indiqué dans la doc, ainsi que le dernier paragraphe

une erreur survient cependant encore:
l'entrée que l'on souhaite ajoutée n'est rattaché nulle part dans l'arborescence de l'annuaire
on change
cn=supann_2009 pour cn=supann_2009,cn=schema,cn=config

L'entrée est ajoutée

TODO devient-il alors possible de lui rattacher des entrées de type supannPerson

Il faut tout d'abord définir une branche supannPeople ?
Reprendre et adapter le example.ldif utilisé précédemment

Les spécifications issue du document d'analyse ('Analyse v0.4.odt') stipulent que les entrées de la branche doivent appartenir aux trois classes d'objets inetOrgPerson, eduPerson et supannPerson

TODO quelles implications de la distrinction entre classe structurelle et classes auxiliaires ?

erreur :
```
$ ldapadd -x -D "cn=admin,dc=condorcet,dc=dev,dc=entrouvert,dc=org" -w test -p 389 -h condorcet.dev.entrouvert.org -f ~/Documents/supann/Example_supann.ldif 
adding new entry "ou=supannPeople,dc=condorcet,dc=dev,dc=entrouvert,dc=org"
ldap_add: Already exists (68)
```

Ok, on a une erreur lors de l'ajout d'une supannPerson
Réessayons un ldapsearch pour vérifier que supannPeople a bien été ajouté:
```
 pmarillonnet@condorcet:~$ ldapsearch -D "cn=admin,dc=condorcet,dc=dev,dc=entrouvert,dc=org" -w test -p 389 -h condorcet.dev.entrouvert.org -b "ou=supannPeople,dc=condorcet,dc=dev,dc=entrouvert,dc=org" -s sub "(ObjectClass=*)" * +
 # extended LDIF
 #
 # LDAPv3
 # base <ou=supannPeople,dc=condorcet,dc=dev,dc=entrouvert,dc=org> with scope subtree
 # filter: (ObjectClass=*)
 # requesting: debs devel Documents Maildir tmp tmp.txt var +
 #

 # supannPeople, condorcet.dev.entrouvert.org
 dn: ou=supannPeople,dc=condorcet,dc=dev,dc=entrouvert,dc=org
 structuralObjectClass: organizationalUnit
 entryUUID: 5ff39d3e-acd4-1036-82c9-7797aedf4f5c
 creatorsName: cn=admin,dc=condorcet,dc=dev,dc=entrouvert,dc=org
 createTimestamp: 20170403161436Z
 entryCSN: 20170403161436.022060Z#000000#000#000000
 modifiersName: cn=admin,dc=condorcet,dc=dev,dc=entrouvert,dc=org
 modifyTimestamp: 20170403161436Z
 entryDN: ou=supannPeople,dc=condorcet,dc=dev,dc=entrouvert,dc=org
 subschemaSubentry: cn=Subschema
 hasSubordinates: FALSE

 # search result
 search: 2
 result: 0 Success

 # numResponses: 2
 # numEntries: 1

```

maintenant : ajout d'une supannPerson
inetOrgPerson ne définit aucun attribut obligatoire

quid de eduPerson et supannPerson ?
le schema de eduPerson n'est disponible que sur des forums, pas de documentation officielle ?

les spéc de Internet2 ne contiennent pas de schéma...

https://www.internet2.edu/products-services/trust-identity/eduperson-eduorg/#service-overview
laisse à croire que l'accès au schéma est disponible sur inscription seulement

update: schéma trouvé sur spaces.internet2.edu
Le schéma est directement au format ldif compatible OpenLDAP
Il suffit de l'ajouter à l'aide de ldapadd, maintenant que le mot de passe d'administration de l'annuaire a été défini

```
pmarillonnet@condorcet:~/Documents/supann$ ldapadd -x -D "cn=admin,cn=config" -w test -p 389 -h condorcet.dev.entrouvert.org -f ./openLdapEduPerson-201602KH.ldif
adding new entry "cn=eduPerson,cn=schema,cn=config"
ldap_add: Undefined attribute type (17)
    additional info: attributeType: attribute type undefined
```

TODO: identifier l'attribut dont le type est indéfini
commenter ligne par ligne le ldif ?
ldapmodified maintenant que l'entrée "cn=eduPerson,cn=schema,cn=config" a été ajoutée ?

Retrait de l'attribut eduPersonPrincipalNamePrior de la définition du schéma

On peut alors essayer d'ajouter une personne appartenant aux trois objectclasses inetOrgPerson, eduPerson, et supannPerson:
```
$ ldapadd -x -D "cn=admin,dc=condorcet,dc=dev,dc=entrouvert,dc=org" -w test -p 389 -h condorcet.dev.entrouvert.org -f ./Example_supann.ldif
adding new entry "uid=jmarrison,ou=supannPeople,dc=condorcet,dc=dev,dc=entrouvert,dc=org"
```
Ok pour la branche People

Maintenant: modifier l'IdP pour que le SSO engendre le transfert d'attributs SupAnn dans l'assertion SAML envoyée

Lecture de la doc:
Tentative d'installer d'un authentic compatible SupAnn
retours à la doc EO pour le projet PSL

d'après la doc, l'Idp se connecte en sortie au LDAP ? existe-t-il directement un connecteur LDAP dans authentic ?
Pourquoi une telle connexion en sortie ?
L'IdP ne communiquera pas directement avec le méta-annuaire
django-mellon doit se charger d'aller récupérer les attributs nécessaires au méta-annuaire.

Piste envisageable
Installer dans une VM l'ISO Debian fourni dans la doc et installer l'IDP Authentic2 compatible SupAnn
=> Déduire, voire exporter partiellement la configuration IDP SupAnn

Installation de VirtualBox et mise en place d'une VM IdP SupAnn
On crée une nouvelle VM avec comme point de boot l'ISO télécharger sur la doc EO

Si cela ne fonctionne pas, configuration de l'OS en tant que *domain 0* Xen -> plus lourd que virtualbox

Ok pour virtualbox, après installation de la dernière version du noyau et des entêtes
On procède à l'installation IdP Authentic pour voir comment est géré le backend

Rien de concluant avec l'installation IdP
nouvel essai avec la base installation

Rien de concluant, les dépôts ne sont plus à jour, les dépendances sont cassées

Comment résoudre le problème ?

Pistes de recherches envisageables :
 - IdP SAML SQLite (donc sans backend LDAP)
 - IdP SAML avec backend LDAP SupAnn

On privilégie d'abord la première solution
sur l'IDP, on définit alors de nouveau attributs

Dans le cas d'un backend LDAP, les attributs utilisés auraient été directement déduits du schéma utilisé pour le stockage des utilisateurs

On crée pour ce faire une nouvelle politique d'options de fournisseur d'identité (SP options policy) prenant en compte les attributs nouvellement créés.

TODO
Création d'un nouvel utilisateur comportant les différents attributs SupAnn
Déploiement des deux autres branches (groupes et structures) de l'annuaire SupAnn, pour l'instant avec la seule branche People c'est un peu léger

répercussions du changement:
django-mellon côté SP
utils LDAP côté SP, à deux endroits (python-ldap et ldap3 ?)
utils LDAP côté passerelle
formulaire côté WCS

on va créer un nouvel utilisateur

Dans WCS, il faut aussi créer un nouveau formulaire qui tienne compte des attributs SupAnn
creation du formulaire traitement_supann

BLOCKER: HTTP500
après merge d'une version à jour des sources
est-ce l'installation globale dans /usr/lib qui a provoqué l'erreur

No module named authentic2.wsgi
avec les sources locales

TODO se renseigner sur l'équivalent du path en python

Bad Gateway car le service ne semble pas écouter sur la socket qui lui est attribuée.
Exploration des logs:

```
 # ls
 #
```
`$ sudo mv authentic2-2.1.20.1134.g1fda65c.dirty-py2.7.egg/ ../.old`

Procédure de rattrapage:
départ à partir d'un nouveau git clone de l'IDP authentic
création d'un branche locale pour la récupération des pulls ?
pb : pas d'update des autres outils jusque là
applications des changements nécessaires au déploiement ?

plusieurs solutions s'offrent à moi :
- garder une version ancienne de l'IDP authentic (circa 12 mars)
- mettre à jour tous les outils, au risque de casser le POC
- ne mettre à jour que authentic, sous risque de péter l'appli

`$ git diff 0de162732ac770 480c2374606 > deployment_changes.backup`


```
pmarillonnet@condorcet:~/devel/authentic$ bash run.sh 
[2017-04-05 14:59:34 +0000] [4453] [INFO] Starting gunicorn 19.6.0
[2017-04-05 14:59:34 +0000] [4453] [INFO] Listening at: unix:/var/run/authentic2/authentic2.sock (4453)
[2017-04-05 14:59:34 +0000] [4453] [INFO] Using worker: sync
[2017-04-05 14:59:34 +0000] [4458] [INFO] Booting worker with pid: 4458
[2017-04-05 14:59:34 +0000] [4453] [INFO] Shutting down: Master
[2017-04-05 14:59:34 +0000] [4453] [INFO] Reason: Worker failed to boot.
```

Problème pour la réalisation des migrations :
./authentic2-ctl ne s'exécute plus correctement

```
pmarillonnet@condorcet:~/devel/authentic.bugged$ ./authentic2-ctl makemigrations
Traceback (most recent call last):
  File "./authentic2-ctl", line 4, in <module>
      import authentic2.logger
      ImportError: No module named authentic2.logger
```

décalage dans la structure des sources :
authentic/src/authentic2
vs
authentic/authentic2 ?

L'IDP fonctionne avec le déploiement à l'aide de
sudo python setup.py install

Retour à la normale, mais persiste encore une erreur de fichiers statiques
IDP en noir sur fond blanc...

```
pmarillonnet@condorcet:~/devel/authentic.deployed/src$ gunicorn authentic2.wsgi --bind unix:/var/run/authentic2/authentic2.sock
[2017-04-05 16:00:20 +0000] [6075] [INFO] Starting gunicorn 19.6.0
[2017-04-05 16:00:20 +0000] [6075] [INFO] Listening at: unix:/var/run/authentic2/authentic2.sock (6075)
[2017-04-05 16:00:20 +0000] [6075] [INFO] Using worker: sync
[2017-04-05 16:00:20 +0000] [6080] [INFO] Booting worker with pid: 6080
[2017-04-05 Wed 16:00:22] - - - WARNING py.warnings.<module>: /home/pmarillonnet/devel/authentic.deployed/src/authentic2_idp_openid/utils.py:8: RemovedInDjango19Warning: django.utils.importlib will be removed in Django 1.9.
  from django.utils.importlib import import_module

[2017-04-05 Wed 16:00:22]  - 06ce3442 ERROR django.request.handle_uncaught_exception: Internal Server Error: /admin/
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/django/core/handlers/base.py", line 108, in get_response
    response = middleware_method(request)
  File "/usr/lib/python2.7/dist-packages/django/middleware/locale.py", line 32, in process_request
    request, check_path=check_path)
  File "/usr/lib/python2.7/dist-packages/django/utils/translation/__init__.py", line 198, in get_language_from_request
    return _trans.get_language_from_request(request, check_path)
  File "/usr/lib/python2.7/dist-packages/django/utils/translation/trans_real.py", line 503, in get_language_from_request
    lang_code = request.session.get(LANGUAGE_SESSION_KEY)
  File "/usr/lib/python2.7/dist-packages/django/contrib/sessions/backends/base.py", line 59, in get
    return self._session.get(key, default)
  File "/usr/lib/python2.7/dist-packages/django/contrib/sessions/backends/base.py", line 181, in _get_session
    self._session_cache = self.load()
  File "/usr/lib/python2.7/dist-packages/django/contrib/sessions/backends/db.py", line 21, in load
    expire_date__gt=timezone.now()
  File "/usr/lib/python2.7/dist-packages/django/db/models/manager.py", line 127, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/usr/lib/python2.7/dist-packages/django/db/models/query.py", line 328, in get
    num = len(clone)
  File "/usr/lib/python2.7/dist-packages/django/db/models/query.py", line 144, in __len__
    self._fetch_all()
  File "/usr/lib/python2.7/dist-packages/django/db/models/query.py", line 965, in _fetch_all
    self._result_cache = list(self.iterator())
  File "/usr/lib/python2.7/dist-packages/django/db/models/query.py", line 238, in iterator
    results = compiler.execute_sql()
  File "/usr/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 840, in execute_sql
    cursor.execute(sql, params)
  File "/usr/lib/python2.7/dist-packages/django/db/backends/utils.py", line 79, in execute
    return super(CursorDebugWrapper, self).execute(sql, params)
  File "/usr/lib/python2.7/dist-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
  File "/usr/lib/python2.7/dist-packages/django/db/utils.py", line 100, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "/usr/lib/python2.7/dist-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
  File "/usr/lib/python2.7/dist-packages/django/db/backends/sqlite3/base.py", line 318, in execute
    return Database.Cursor.execute(self, query, params)
OperationalError: no such table: django_session
```

Pour l'instant on se contentera de cette installation, sans les fichiers statiques...

De nouveau la même erreur apparaît:
```
AssertionError at /admin/custom_user/user/add/

No exception message supplied

Request Method:     POST
Request URL:    http://idp-condorcet.dev.entrouvert.org/admin/custom_user/user/add/
Django Version:     1.8.16
Exception Type:     AssertionError
Exception Location:     /usr/local/lib/python2.7/dist-packages/authentic2-2.1.20.1149.g00bf793-py2.7.egg/authentic2/models.py in set_value, line 205
Python Executable:  /usr/bin/python
```

L'exécution de la commande
sudo python setup.py clean
révèle des incohérences de version

Cette commande ne recompilerait pas tous les bytecodes pyc après modification de la version d'une des dépendances du projet => découverte d'erreur après nettoyage des bytecodes de l'appli...

BLOCKER: mécanismes de migrations éclaté :
```
Running migrations:
  Applying authentic2_idp_oidc.0004_auto_20170405_1659... OK
Traceback (most recent call last):
  File "./authentic2-ctl", line 21, in <module>
    execute_from_command_line(sys.argv[:1] + argv)
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/core/management/__init__.py", line 385, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/core/management/__init__.py", line 377, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/core/management/base.py", line 288, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/core/management/base.py", line 338, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/core/management/commands/migrate.py", line 165, in handle
    emit_post_migrate_signal(created_models, self.verbosity, self.interactive, connection.alias)
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/core/management/sql.py", line 268, in emit_post_migrate_signal
    using=db)
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/dispatch/dispatcher.py", line 198, in send
    response = receiver(signal=self, sender=sender, **named)
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/contrib/contenttypes/management.py", line 39, in update_contenttypes
    for ct in ContentType.objects.using(using).filter(app_label=app_label)
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/db/models/query.py", line 141, in __iter__
    self._fetch_all()
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/db/models/query.py", line 966, in _fetch_all
    self._result_cache = list(self.iterator())
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/db/models/query.py", line 265, in iterator
    for row in compiler.results_iter():
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/db/models/sql/compiler.py", line 701, in results_iter
    for rows in self.execute_sql(MULTI):
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/db/models/sql/compiler.py", line 787, in execute_sql
    cursor.execute(sql, params)
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/db/backends/utils.py", line 81, in execute
    return super(CursorDebugWrapper, self).execute(sql, params)
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/db/backends/utils.py", line 65, in execute
    return self.cursor.execute(sql, params)
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/db/utils.py", line 94, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/db/backends/utils.py", line 65, in execute
    return self.cursor.execute(sql, params)
  File "/usr/local/lib/python2.7/dist-packages/Django-1.8.16-py2.7.egg/django/db/backends/sqlite3/base.py", line 485, in execute
    return Database.Cursor.execute(self, query, params)
django.db.utils.OperationalError: no such column: django_content_type.name
```

TODO:
résoudre le pb de migrations
patcher l'erreur sur le assert isinstance

pb réglé en installant la version 1.8 de Django plutôt qu'une sous-version précise (1.8.16 posait problème en l'occurrence)

Ok le patch sera pour plus tard
incohérence entre la définition d'un attribut multiple et le moyen de saisie pour cet attribut (possibilité d'ajouter des champs), et le rendu final doit être un tuple, pas une chaîne de caractères

retour sur le déploiement:
installation de l'IDP en mode de développement ? setup.py --dev

BLOCKER:ValueError: No JSON object could be decoded

erreur provoquée avec l'utilisateur admin
FIX: création d'un nouvel utilisateur privilégié
sudo ./authentic2-ctl createsuperuser

agent

Les attributs SupAnn ne sont pas transmis lors du SSO

```
(Pdb) p dir(UserSAMLIdentifier.objects.last().user)
['DoesNotExist', 'Meta', 'MultipleObjectsReturned', 'REQUIRED_FIELDS', 'USERNAME_FIELD', '__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__', u'__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__unicode__', '__weakref__', '_base_manager', '_check_column_name_clashes', '_check_field_name_clashes', '_check_fields', '_check_id_field', '_check_index_together', '_check_local_fields', '_check_long_column_names', '_check_m2m_through_same_relationship', '_check_managers', '_check_model', '_check_ordering', '_check_swappable', '_check_unique_together', '_default_manager', '_deferred', '_do_insert', '_do_update', '_get_FIELD_display', '_get_next_or_previous_by_FIELD', '_get_next_or_previous_in_order', '_get_pk_val', '_get_unique_checks', '_meta', '_perform_date_checks', '_perform_unique_checks', '_save_parents', '_save_table', '_set_pk_val', '_state', 'check', 'check_password', 'clean', 'clean_fields', 'date_error_message', 'date_joined', 'delete', 'email', 'email_user', 'first_name', 'from_db', 'full_clean', 'get_all_permissions', 'get_deferred_fields', 'get_full_name', 'get_group_permissions', 'get_next_by_date_joined', 'get_previous_by_date_joined', 'get_session_auth_hash', 'get_short_name', 'get_username', 'groups', 'has_module_perms', 'has_perm', 'has_perms', 'has_usable_password', 'id', 'is_active', 'is_anonymous', 'is_authenticated', 'is_staff', 'is_superuser', 'last_login', 'last_name', 'logentry_set', 'natural_key', 'objects', 'password', 'pk', 'prepare_database_save', 'refresh_from_db', 'saml_identifiers', 'save', 'save_base', 'serializable_value', 'set_password', 'set_unusable_password', 'unique_error_message', 'user_permissions', 'username', 'validate_unique']
```

Pdb côté mellon
login.assertion.exportToXml()

FieldDoesNotExist at /accounts/mellon/login/
User has no field named 's_entite_affectation_principale'
/usr/lib/python2.7/dist-packages/django/db/models/options.py in get_field, line 554

Incohérence dans le modèles de données de django_mellon
le modèle semble cassé :
```
tpl     '{attributes[s_entite_affectation_principale][0]}'

old_value   u''

model_field     <django.db.models.fields.CharField: last_name>
```
Pourquoi last_name dans le model_field ?
Pourtant les migrations du modèle ont été effectuées

Pistes de résolutions:
- débogger le backend mellon ?
- comprendre le déroulement de la fonction authenticate, pourquoi a-t-elle encore besoin d'aller taper dans le backend mellon
- en parler avec Benj et Mik !
- rollback vers un état stable ?


TODO: contourner le filtrage de port ssh chez moi !

Retour sur le bug de non transmission des attributs lors du SSO
En réalité les attributs sont transmis, mais ne peuvent être stockés côté SP par mellon, qui semblent bloqué sur une incohérence du modèle User
Quelle solution autre que le debugging ?

=> Etudier le customizing#extending the user model
Sur conseil de Josué :
mellon semble se contenter d'un modèle utilisateur par défaut, qui ne supporte pas l'ajout d'attributs tels que les attributs SupAnn que nous essayons de stocker côté SP


 /usr/lib/python2.7/dist-packages/mellon/views.py in authenticate :

def authenticate(self, request, login, attributes):
    user = auth.authenticate(saml_attributes=attributes)

Bingo :
class UserSAMLIdentifier(models.Model):
    user = models.ForeignKey(
    verbose_name=_('user'),
    to=settings.AUTH_USER_MODEL,
    related_name='saml_identifiers')


$ python manage.py makemigrations
SystemCheckError: System check identified some issues:

ERRORS:
saml.SupAnnUser.user_ptr: (fields.E301) Field defines a relation with the model 'auth.User', which has been swapped out.
    HINT: Update the relation to point at 'settings.AUTH_USER_MODEL'.

Retour à l'écriture d'un modèle copié sur auth.User ?

le UserSAMLIdentifier possède une clé étrangère vers le modèle défini par `AUTH_USER_MODEL`

TODO: quelles possibilités d'héritage de ce AUTH_USER_MODEL par défaut (càd django.contrib.auth.models.User) ?

Point avec Mik:

- Le lien au modèle utilisateur de django-mellon doit se faire par mise en place d'une relation one-to-one
- S'inspirer du code d'authentic pour la récupération des attributs

De façon plus générale, pour l'instant : mauvaise connaissance du besoin cerné par django-mellon de mon côté, il faudrait d'abord que je parvienne à cerner le besoin couvert par mellon.

Résolution par relation one-to-one après un petit problème de migrations:
http://stackoverflow.com/questions/32991965/you-are-trying-to-add-a-non-nullable-field-id-to-contact-info-without-a-defaul

ok problème ici:
pistes:
Comment sont récupérés les attributs SAML ?
Lire de code de django-mellon
+
Lecture du code d'authentic pour comprendre la transmission des attributs


=> Où ça dans authentic ?
Attribute aggregator

`app_settings.A2_ATTRIBUTE_KINDS` ?


No such column: saml_supannuser.id

L'erreur se trouve dans le mapping d'attributs:
Il se s'agit pas d'un mapping IDP-SP mais plutôt d'un mapping
mellon.User<->SP.User

MELLON_ATTRIBUTE_MAPPING = {
    'first_name': '{attributes[fname_test][0]}',
    'last_name': '{attributes[lname_test][0]}',
    'email' : '{attributes[email_test][0]}',
    'password' : '{attributes[password_test][0]}',
    'ep_principal_name' : '{attributes[ep_principal_name][0]}',
    's_etablissement' : '{attributes[s_etablissement][0]}',
    'ep_primary_affiliation' : '{attributes[ep_primary_affiliation][0]}',
    'ep_affiliation' : '{attributes[ep_affiliation][0]}',
    's_entite_affectation_principale' : '{attributes[s_entite_affectation_principale][0]}',
    's_entite_affectation' : '{attributes[s_entite_affectation][0]}',
    's_emp_corps' : '{attributes[s_emp_corps][0]}',
    's_liste_rouge' : '{attributes[s_liste_rouge][0]}',
}

côté IDP:
src/authentic2/saml/management/commands/mapping.py

```
#Extracted from version 389 Directory Server du schema
#SupAnn version 2009.6
#http://www.cru.fr/_media/documentation/supann/supann_2009.schema.txt
"supannListeRouge": {
    "oid": "1.3.6.1.4.1.7135.1.2.1.1",
    "display_name": "supannListeRouge",
    "type": "http://www.w3.org/2001/XMLSchema#string",
    "syntax": "1.3.6.1.4.1.1466.115.121.1.7",
},
```

`
ERRORS:
saml.SupAnnUser.user: (fields.E301) Field defines a relation with the model 'auth.User', which has been swapped out.
    HINT: Update the relation to point at 'settings.AUTH_USER_MODEL'.
`

Ajout effectué:
```
L'utilisateur a bien ete ajoute a l'annuaire :
{ "criticality_level": 0, "display_id": "3-8", "display_name": "traitement_supann #3-8", "evolution": [ { "status": "just_submitted", "time": "2017-04-10T15:58:49Z" }, { "time": "2017-04-10T16:00:15Z", "who": { "email": "pmarillonnet@condorcet", "id": "3", "name": "agent" } }, { "status": "finished", "time": "2017-04-10T16:00:20Z" } ], "fields": { "bureau": "foo", "email": "pmarillonnet@entrouvert.com", "ep_affiliation": "u", "ep_primary_affiliation": "student", "ep_principal_name": "pmarillo", "nameid": "pmarilloaa", "nom": "marilloaa", "prenom": "paul", "s_emp_corps": "student", "s_entite_affectation": "u", "s_entite_affectation_principale": "student", "s_etablissement": "studentaaa", "s_liste_rouge": "True", "telephone": "foo", "unit": "foo" }, "id": "traitement_supann/8", "last_update_time": "2017-04-10T16:00:20Z", "receipt_time": "2017-04-10T15:58:49Z", "roles": { "_receiver": [ { "allows_backoffice_access": true, "details": "Recoit les demandes de creation d'un compte invite", "emails": [], "emails_to_members": true, "id": "2", "name": "Destinataire", "slug": "destinataire", "text": "Destinataire" } ], "actions": [], "concerned": [ { "allows_backoffice_access": true, "details": "Recoit les demandes de creation d'un compte invite", "emails": [], "emails_to_members": true, "id": "2", "name": "Destinataire", "slug": "destinataire", "text": "Destinataire" } ] }, "submission": { "backoffice": false, "channel": "web" }, "url": "http://forms-condorcet.dev.entrouvert.org/traitement_supann/8/", "workflow": { "status": { "id": "finished", "name": "Termin\u00e9" } } }
```

On peut alors vérifier à l'aide des ldad utils que l'utilisateur se trouve bien dans l'annuaire SupAnn
```
# pmarilloaa, supannPeople, condorcet.dev.entrouvert.org
dn: uid=pmarilloaa,ou=supannPeople,dc=condorcet,dc=dev,dc=entrouvert,dc=org
structuralObjectClass: inetOrgPerson
entryUUID: d035df7a-b252-1036-8bf7-2f7e2fa64143
creatorsName: cn=admin,dc=condorcet,dc=dev,dc=entrouvert,dc=org
createTimestamp: 20170410160216Z
entryCSN: 20170410160216.762889Z#000000#000#000000
modifiersName: cn=admin,dc=condorcet,dc=dev,dc=entrouvert,dc=org
modifyTimestamp: 20170410160216Z
entryDN: uid=pmarilloaa,ou=supannPeople,dc=condorcet,dc=dev,dc=entrouvert,dc=o
 rg
subschemaSubentry: cn=Subschema
hasSubordinates: FALSE
```

TODO: ajout des attributs SupAnn et EduPerson dans la branche de l'annuaire

cote connecteur :
```
(Pdb) id
{u'nom': u'marilloaa', u'prenom': u'paul', u's_etablissement': u'studentaaa', u'telephone': u'foo', u's_entite_affectation_principale': u'student', u's_entite_affectation': u'u', u'email': u'pmarillonnet@entrouvert.com', u'unit': u'foo', u'bureau': u'foo', u's_emp_corps': u'student', u'ep_principal_name': u'pmarillo', u'nameid': u'pmarilloaa', u's_liste_rouge': u'True', u'ep_affiliation': u'u', u'ep_primary_affiliation': u'student'}
```


problème ici : les classes supannPerson et eduPerson ne sont pas ajoutées
dn: uid=foo,ou=supannPeople,dc=condorcet,dc=dev,dc=entrouvert,dc=org
structuralObjectClass: inetOrgPerson


TODO:
faut-il définir une branche parente eduOrg et 'supannOrg' pour l'ajout d'une entrée comportant les deux classes eduPerson et supannPerson ?
plusieurs solutions :
utilisation de ApacheDirectoryServer pour une visualisation plus claire de la structure
recherche de l'info dans les specs supann ? pas trouvée...

ces deux classes ne sont peut-être pas à déclarer de la même façon dans le dictionnaire manipulé par ldap3

ldap3 propose-t-il la même interface pour la déclaration d'une classe structurelle et de classes auxiliaires

OK bug identifié : eduPerson et supanPerson définie en tant que classes structurelles dans chacun des deux schémas associés

Modifier le schéma sans vider l'annuaire ?
essayer d'abord avec une seule classe parmi les deux ?

etapes:
vider la branche supannPeople (ldapdelete)
tenter l'ajout d'une première entrée sans eduPerson

ldapdelete -D "cn=admin,dc=condorcet,dc=dev,dc=entrouvert,dc=org" -w test -p 389 -h condorcet.dev.entrouvert.org "uid=jmarrison,ou=supannPeople,dc=condorcet,dc=dev,dc=entrouvert,dc=org"

Lecture de la RFC 4512 pour comprendre les différences entre classes structurelles et classes auxiliaires.

Il s'agirait d'une question de *DIT content rule* pas précisée pour la classe structurelle de l'entrée ?

Allons voir le schéma de inetOrgPerson:
```
/etc/ldap/schemas/inetOrgPerson.schema

objectclass	( 2.16.840.1.113730.3.2.2
    NAME 'inetOrgPerson'
	DESC 'RFC2798: Internet Organizational Person'
    SUP organizationalPerson
    STRUCTURAL
	MAY (
		audio $ businessCategory $ carLicense $ departmentNumber $
		displayName $ employeeNumber $ employeeType $ givenName $
		homePhone $ homePostalAddress $ initials $ jpegPhoto $
		labeledURI $ mail $ manager $ mobile $ o $ pager $
		photo $ roomNumber $ secretary $ uid $ userCertificate $
		x500uniqueIdentifier $ preferredLanguage $
		userSMIMECertificate $ userPKCS12 )
	)
```
alors que supanPerson
```
UP top AUXILIARY
```

Ok l'ajout a bien été pris en compte, pour le vérifier il suffit de modifier légèrement la requête, afin d'afficher le contenu de l'entrée et non ses méta-données:
```
ldapsearch -D "cn=admin,dc=condorcet,dc=dev,dc=entrouvert,dc=org" -w test -p 389 -h condorcet.dev.entrouvert.org -b "ou=supannPeople,dc=condorcet,dc=dev,dc=entrouvert,dc=org" -s sub "(ObjectClass=*)"
```

Les classes auxiliaires sont bien ajoutées, il suffit alors de rajouter dans le code du connecteur les différents attributs définis dans l'analyse, relevant à la fois des classes supannPerson et eduPerson.
On ajoute une structure conditionnelle dans le code, pour gérer le champ booléen SupannListeRouge.
Les attributs de type BooleanMatch dans OpenLDAP prennent les valeurs 'TRUE' ou 'False', tandis que le fichier JSON renvoyé par WCS contient un champ prenant les valeurs True ou False.

On remarquera aussi que le champ 'surname' n'est pas supporté par aucune des trois classes auxquelles appartiennent les entrées de l'annuaire supannn.
On lui préférera donc l'attribut 'sn'.

TODO:
- nettoyage du code DONE
- fix collectstatic? Plus tard, pas envie de casser le POC pour ça
- export de la config dans settings ? pas urgent en l'état
- ecriture des tests ? quels scénarios de tests ?
- réentrance des fonctions: abandonner le recours multiple à plusieurs lectures de UserSAMLIdentifier
- gestion du SLO inité par l'IDP ? OUI
- résolution des erreurs 404 sur les fichiers statiques
- incorporation d'autres branches supann ?
- génération d'un certicat, ajouté au navigateur, pour l'établissement d'une connexion https ?

SLO initié par l'IDP : pas supporté pour l'instant dans le POC. A-t-on intérêt à le mettre en place ?

SLO initié par le SP : comportement anormal, renvoie vers la vue unit alors qu'il faudrait renvoyer vers une nouvelle mire de login

Autre comportement anormal : la vue unit est accessible sans authentification => Mettre un décorateur
=> Mise en place du SLO

Page 32/69 de la doc pdf de authentic:

Deux façons :SOAP ou Redirect
Redirect plus rapide à mettre en place, juste une query string à rajouter dans l'url de logout coté sp ?

Jetons un oeil aux métadonnées du SP dans l'interface d'admin de l'IDP:
```
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://sp-condorcet.dev.entrouvert.org/accounts/mellon/logout/"/>
```

Il reste à aller chercher dans mellon le code de la vue logout
lecture du fichier views de mellon
interrogations:
thin-session de lasso ?

```
(Pdb) request.GET
<QueryDict: {u'SigAlg': [u'http://www.w3.org/2000/09/xmldsig#rsa-sha1'], u'SAMLResponse': [u'hZJbi8IwEIX/Ssm7ja2ttaEKtRcQ3Jd18WFfJLRBCzVTMlPZn7+xdRcV1n2cy3fmcJIE5bntxBaO0NO7wg40Kufr3GoUw2jJeqMFSGxQaHlWKKgSu/RtK3x3KjoDBBW07A55TUhEZagBzZxNvmSHWRykfriYB1GxKNMyywI/ywMvSONwsUi90K7pH18fYIGgyNIon2dRVHrrfO6VXlCEZewX61no5TML7JVBe2DJ7D2LI/Zqo5GkJtuaetFkGkw8/2MaizASs/iTOblCarSkgToRdYJz7CYV6BpMpcit1cVVmgz0F+veBXPksqqg14T8rNoWNG+HCDlbJdcQxHDWrG5iTf2Pml3gV863ciRrSTLh9zrJ+E47ktTjY5VBrZy9bHv1OnkctsUYZq0M+1vm5lr2dLI+m+rZ7Sh1qCzCN5qU0bLdKWPnhTFgGF+N7u+1n1q/5ePXW30D'], u'Signature': [u'GOOcRJxIwkJQV5l4hJvRtcI/TU5uNwPGNEDsyBL5IJzhKSRiqq7vwvUSerpcCFObjmrTwXlzroFu7z2chfPmoVPb55Y+h/sXlmC7ewpWqtw+kWdUW9xln51jJkW5FgnEmEirKrnXYVhFIsb8xnTJNEkCFfAbi3RP8rSNrzzO1XXmdHFfg819M7/NZWi2kudhok9LCaFt6Scj+7zJJzwkmayf6FZkHb4EqWdBQJLRXfVOhidClESZoSRyzVDzGWqpFMNkqkzSvJ+a1026CzCa/UTFxySf3lCKfELHdrTuKQg+gJi5Ki5oK5Mis2URJA+R0fHVvNA/qbZZATbGwQyLDA==']}>
```
La procédure de logout se fait en deux fois:
récupérer le contexte pour chacun des deux appels à mellon, et déterminer si le comportement est anormal ou non

base64.b64decode ne permet pas de décoder le contenu de l'attribut GET['SAMLResponse']

mellon logout => lire la doc de mellon concernant ce paramètre (renseigné dans le SP)

Ecriture de tests:
erreur pour l'instant

ValueError: Related model 'saml.SupAnnUser' cannot be resolved
Résolu par migration

TODO:
ajout dans l'annuaire depuis passerelle seulement après authentification

### Remarques concernant les tests
La base de données ne semble pas initialisée
tox utilise pgsql par défaut ?
Le déploiement Campus Cordorcet est en sqlite3


différentes solutions pour l'affichage d'un formulaire vide :
simple flag :pas élégant
aller remplir UserSAMLIdentifier avec un compte vide, pas très classe, de toute façon il y a une foreign Key
passage de nom de méthode : overkill ?
on va commencer avec un flag ?
Oui pour l'instant
Revoir le passage d'argument de {% url %} vers une vue

Plus simplement, création d'une nouvelle vue en charge de l'afficahe d'un formulaire vide....

TODO : nettoyage des UserSAMLIdentifier en base, côté django_mellon
TODO : refonte du formulaire, selon les attributs désignés dans les spec
 
 -> Les champs du formulaire
 Le formulaire présente les champs suivants.
 Invité
 Nom (obligatoire)
 Prénom (obligatoire)
 Adresse de messagerie (obligatoire)
 Établissement d'origine
 Affiliation principale (liste déroulante) (facultatif)
 Affectation principale (déterminer un texte d'aide) (facultatif)
 Corps d'appartenance (Dans le cadre du POC, la liste déroulante de la nomenclature NCORPS sera une liste déroulante construite « à la main »)
 Liste rouge (case à cocher)
 Hôte
 Nom de la personne invitante (obligatoire ?)
 Prénom de la personne invitante (obligatoire ?)
 Établissement invitant (liste de valeurs basée sur ou=structures) (choix unique) (facultatif)
 Unité ou service invitant (liste de valeurs basée sur ou=structures) (choix unique) (facultatif)

Le plus simple : modifier un champ à la fois seulement pour limiter la casse potentielle

Renommage
supannListeRouge => Liste Rouge
Mail OK
ADMIN_FOR_ALL wcs Ok


Pbatique des affectations et affiliations :
Choix multiple et désignation

TODO : éliminier la redondance du modèle dans {forms,models}.py
Bug sur la liste déroulante correspondant à l'affectation principale
Seul le dernier élément est présent.

```
dn: uid=amarilloaa,ou=supannPeople,dc=condorcet,dc=dev,dc=entrouvert,dc=org
eduPersonPrimaryAffiliation: []
cn: aul marilloaa
objectClass: inetOrgPerson
objectClass: supannPerson
objectClass: eduPerson
supannEntiteAffectationPrincipale: []
supannEntiteAffectation: u
supannEmpCorps: student
eduPersonAffiliation: u
eduPersonPrincipalName: pmarillo
sn: marilloaa
supannEtablissement: studentaaa
mail: pmarillonnet@entrouvert.com
givenName: aul
supannListeRouge: TRUE
uid: amarilloaa
```

adding new entry "cn=eduorg,cn=schema,cn=config"

Ne pas oublier de supprimer les quelques dernières lignes du ldif, à partir de
structuralObjectClass

Le schema supannorg est déjà dans la conf du ldap
Pas la façon la plus logique de procéder : pourquoi ne pas modifier directement la racine pour qu'elle contienne les deux classes auxiliaires supplémentaires ?

```
 # condorcet.dev.entrouvert.org
 dn: dc=condorcet,dc=dev,dc=entrouvert,dc=org
 objectClass: top
 objectClass: dcObject
 objectClass: organization
 objectClass: eduOrg
 objectClass: supannOrg
 o: condorcet.dev.entrouvert.org
 dc: condorcet
```

il devient alors possible d'ajouter les deux branches manquantes pour respecter les spéc fournies dans la version 0.4 du document d'analyse

adding new entry "ou=entrouvert,ou=structures,dc=condorcet,dc=dev,dc=entrouvert,dc=org"

adding new entry "ou=entitecampuscondorcet,ou=structures,dc=condorcet,dc=dev,dc=entrouvert,dc=org"

TODO modifier Groups pour tenir compte des objectclasses edu et supann

BIGTODO:
peuplement test des etablissement et des unites/services pour les deux listes déroulantes (cf page 12 analyse.v0.4)

adding new entry "ou=etablissement0,ou=structures,dc=condorcet,dc=dev,dc=entrouvert,dc=org"

adding new entry "ou=etablissement1,ou=structures,dc=condorcet,dc=dev,dc=entrouvert,dc=org"

adding new entry "ou=etablissement2,ou=structures,dc=condorcet,dc=dev,dc=entrouvert,dc=org"

adding new entry "ou=unite0,ou=structures,dc=condorcet,dc=dev,dc=entrouvert,dc=org"

TypeError at /
must be string, not builtin_function_or_method


Entretien Mik
TODO
master DONE
ldap_is_in_directory à changer DONE ldap_contains_user
UserSAMLIdentifier -> mellon_session
syslog
settings concornant django pour la config syslog directe

/register
/sso sélection du sélecteur d'identité

verification avec l'eppn des doublons
Affichage d'une page joli 'vous etes deja'
Redirect vers une page '/usernone' templateView statique DONE

FormValid 
Faire la vérification syntaxique de ce qu'il y a sur les champs
Check sur l'EPPN en plus du décorateur : modifier le code, ce n'est pas l'EPPN qui est utilisé actuellement en tant qu'identifiant unique DONE
Si ces deux conditions sont remplies, alors on peut soumettre la demande w.c.s. DONE

post s'il n'est pas correct DONE
renvoyer le formulaire avec les champs invalides, et les autres pré-remplis DONE

UnitFormView -> RegistrationFormView Done
formValid(, form) DONE
form déjà instancié, déjà crée par défaut par la CBV
définir les initial quand la CBV
methode get_initial() DONE
form_valid DONE

dispatch protégée avec le décorateur?
façon plus élégante : login_required
décorateur directement dans urls
not_in_ldap(RegistrationFormView.as_view()) TODO

affiliations affectation secondaires dans le SSO mais pas dans l'annuaire

pages blanche avec juste un header 'campus condorcet - gestion des comptes invités'

ASCII -> Unicode

workflow : passer à un seul statut

ouverture du port ldap
role destinataire

wcs
Une nouvelle demande d'inscription vient d'être saisie...

envoi un courriel à l'usager

clé de signature au web service

Soigner un peu les mails
Bonjour M. ...
Nous avsons bien reçu votre demande le ... à ...
form_var_civilite ? necessaire ?

viewer LDAP
enlever le logo entr'ouvert DONE

priorité 1 :
pérenniser les scripts EN COURS : seulement des scripts systemctl de démarrage pour l'instant
mellon_session DONE

priorité 2 : les skins EN COURS

priorité 1 :
pérenniser les scripts EN COURS : seulement des scripts systemctl de démarrage pour l'instant
mellon_session EN COURS : refactorisation des vues à faire

priorité 2 : les skins EN COURS
aborted : check de l'age des sessions
 42  
 43         #import pdb; pdb.set_trace() 
 44         #session_string_datetime = self.request.session['mellon_session']['authn_instant'] 
 45         #session_datetime = datetime.strptime(session_string_datetime[0:19], 
 46         #                                     "%Y-%m-%dT%H:%M:%S") 
 47 

Logging:
https://docs.djangoproject.com/en/1.11/topics/logging/


Maintenant :
davantage de logs ?
support utf-8 ?

compatibilité supann côté IDP pour minimiser le coût de branchement à une fédération RENATER ?


partie invitation:
génération d'un code avec rappatriement de l'entrée d'email ?
préremplissage côté IDP de l'adresse email ?
Option : utiliser une autre adresse email ?
TODO voir ce qu'offre authentic pour cela ?

Pourquoi ne pas directement utiliser le code de suivi généré par wcs :

1ere version, sans fédération :
génération d'une URL vers le SP avec le numéro de suivi en query string, le SP va récupérer les entrées du formulaire correspondant au code de suivi, et prérempli le formulaire d'inscription avec les trois champs du formulaire d'invitation

2e version, avec fédération :
L'utilisateur dispose de deux options :
* "Je dispose d'un compte utilisateur sur l'une des fédérations ci-dessous"
Ici utiliser le WAYF Renater
MELLON_DISCOVERY_SERVICE_URL = 'https://discovery.renater.fr/renater'
* "Je crée un compte" : simplement la première version

Ok, le plus direct à mettre en place : création d'un compte sans préremplissage autre que ce qui se trouve dans le formulaire d'invitation


Première étape, envoi d'un mail avec le code de suivi, à l'adresse renseignée
Bonjour

2e étape : paramètre supplémentaire optionnel dans la RegistrationFormView, permettant la redirection de l'utilisateur vers un formulaire prérempli

TODIG : le code de suivi peut-il être considéré comme un identifiant opaque ?
Voir comment il est généré dans wcs

ok, cf doc
> Une API existe pour déterminer l'existence d'un code de suivi et, le cas échéant, découvrir la demande associée.

$ curl -H "Accept: application/json" \
       https://www.example.net/api/code/QRFPTSLR
       {"url": "http://www.example.net/demarche/23", "err": 0}

Maintenant :
Gestion de plusieurs emails ?
Invitation à utiliser la fédération ?
Invitation possible que si l'EPPN se trouve déjà dans l'annuaire ? Oui !
La vérification se fait après envoi du formulaire, donc dans form_valid

TODO:
pas de préremplissage de l'email, il s'agit de l'adresse d'invitation
par contre le champ EPPN doit être non éditable

Maintenant : l'invitation doit permettre le SSO
donc regarder les paramètres possibles en query_string vers la vue de login dans
django mellon

TODO
Possibilités offertes par l'API authentic pour la création d'un utilisateur?
forger directement une requête post en https avec user/password ? NON, il s'agit des établissements de test
Commencer à créer l'entrée dès maintenant ? NON, on doit pouvoir laisser le choix à l'utilisateur d'avoir recours à la fédération RENATER
Créer un utilisateur alors que l'invité a recours à la fédération RENATER revient à créer deux comptes à l'insu de l'utilisateur
Donc comment faire ?
Pas de création, juste un préremplissage et on laisse ou non le choix à l'utilisateur de recourir à la fédération RENATER


Maintenant : envoi d'une liste d'emails
Il y aura autant d'entrée w.c.s que de mails fournis ?
Oui dans un premier temps, voir ensuite si dans les templates de mail w.c.s on peut envoyer à une liste de participant en bcc ?


DONE

ParrainDN :
Ok il s'agit bien de l'attribut LDAP supannParrainDN
On va chercher l'EPPN dans la formulaire w.c.s. puis on créé le DN correspondant
rien à faire côté SP, jusque côté passerelle
Récuperer la variable hote_identite du formulaire pour créer l'attribut
supannParrainDN


Problème : lors d'un formulaire libre la personne peut remplir librement l'identité de du référent
Le référent ne recevra jamais d'email la réponse fournie n'es pas un EPPN valide présent dans le méta-annuaire
WORKAROUND possible un peu rude : envoyer par défaut un email au référent CC, ou bien vérifier par regex qu'il ne s'agit pas d'une adresse email
s'il s'agit d'une adresse email, refaire la procédure de recherche des inscrits à l'annuaire avec cette fois-ci un filtre sur l'email

BLOCKER : l'ajout dans le LDAP n'est pas effectué
Regarder les contraintes sur le format des entrées supannParrainDN
Oui il semblerait bien qu'il y ait un contrôle sur les distinguishedName de l'annuaire
Dans ce cas il faut un meilleur contrôle d'erreur puisque pour l'instant celle-ci n'est pas détectée

FIX: avec un DN valide ça marche !
Il y bien un contrôle implicite

Maintenant : implémentation d'un contrôle d'erreur ?
A quel moment doit-on vérifier si le référent se trouve dans le méta-annuaire ?
L'invité doit-il être mis au courant de la présence ou non du référent déclaré dans le méta-annuaire ?
Peut-on mentionner un référent par son adresse email ?
Oui, ce serait bien, puisque celle-ci doit de toute façon être retrouvée pour l'envoi du mail de notification au référent

Mik:
 on va se servir affectation pour garder une trace de l'établissement d'origine
 Et donc on va le référencer même s'il n'est pas dans l'annuaire

En l'état, confusion avec le parrainDN et celui qui va recevoir l'email de notification
Donc il faut deux champs:
La personne invitante, càd le parrainDN, et l'agent qui va recevoir l'email de notification
Finalement très peu de code dans le SP, presque toutes les fonctionnalités requises peuvent être prises en charge par w.c.s.

u'supannparraindn': u'pmarillo241'

Le champ de traitement w.c.s. est bien rempli, on peut ajouter le DN au ldap

Jeu de données pour la seconde démo
4 Entités : OK
4 Etablissements : OK

Retablissement du push LDAP dans le connecteur passerelle
OK la procédure d'inscription via invitation fonctionne

Connecteur LDAP: BLOCKER
TypeError: {u'objectClass': [u'organizationalUnit', u'top'], u'ou': [u'people']} is not JSON serializable

(Pdb) type(o)
<class 'ldap3.utils.ciDict.CaseInsensitiveDict'>

Solutions ?
    round-trip cast
    dictionnaire de CaseInsensitiveDicts-> str -> dictionnaire de dictionnaires ?
    OK

//CURRENT

## Tournai
mail de Benj:
```
SSO desktop pour Tournai sur authentic et alfresco
permettre un SSO qui s'étende à l'accès DAV vers Alfresco.

Sur authentic c'est Kerberos qu'il faut utiliser la doc est là:

	https://dev.entrouvert.org/projects/sysadmin/wiki/Kerberos#Active-Directory
	http://git.entrouvert.org/authentic2-auth-kerberos.git/tree/README

générer la keytab en exécutant les commandes setspn et ktpass sur le serveur AD.
Le fichier keytab ainsi générer devra être copié sur le serveur authentic et
indiqué à Authentic via la variable d'environnement KRB5_KTNAME.

Pour Alfresco je pense que le plus simple est d'activer le mode NTLM
pass-through (ça n'essaiera que si aucune autre authentification n'a pris la
main) car ça ne nécessite pas de configuration particulière au niveau de l'AD:

	http://docs.alfresco.com/5.0/concepts/auth-alfrescontlm-ntlm.html

Pour se connecter à la machine alfresco de Tournai voir (Paul tu pourras demander les
détails à Fred, notamment qu'il ajoute ou fasse ajouter ta clé aux accès vers
staging.imio.be)[1]:

	https://dev.entrouvert.org/projects/interne/wiki/Mots_de_passe_utilis%C3%A9s_dans_des_projets_client#Tournai

La documentation actuelle de la configuration Alfresco est sur:

	https://dev.entrouvert.org/projects/tournai/wiki/Param%C3%A9trage_SSO_alfresco

Actuellement on a du pass-trough sur une authentification sur un entête
HTTP (X-Alfresco-Remote-User) sinon on passe en authentification LDAP.

Pour se connecter au serveur publik de Tournai:

	ssh root@staging.imio.be

puis

	docker exec -ti tournaiteleservices_tournaiteleservices_1 bash

Paul, selon nos disponibilités n'hésite pas à déranger Fred ou moi.

[1]: j'ai cette config dans mon ssh-config

	Host alfresco-tournai
	HostName 194.78.28.150
	Port 5022
	User root
	ProxyCommand ssh -W %h:%p staging.imio.be
	LocalForward 2080 127.0.0.1:2080
```

Prérequis pour le tuto Achim Grolms :
A-record, a PTR-record, a CNAME record

Lecture RFC Kerberos OK

Installation de Alfresco
dépendances:
ImageMagick

Alfresco OK

Installation d'Authentic avec le support Kerberos :

Erreur en décrivant la procédire usuelle:
```
./authentic2-ctl runserver
Performing system checks...

System check identified no issues (0 silenced).
Unhandled exception in thread started by <function wrapper at 0x7f6d5fc095f0>
Traceback (most recent call last):
  File "/home/paul/tournai/venv/local/lib/python2.7/site-packages/Django-1.8.18-py2.7.egg/django/utils/autoreload.py", line 229, in wrapper
    fn(*args, **kwargs)
  File "/home/paul/tournai/venv/local/lib/python2.7/site-packages/Django-1.8.18-py2.7.egg/django/core/management/commands/runserver.py", line 116, in inner_run
    self.check_migrations()
  File "/home/paul/tournai/venv/local/lib/python2.7/site-packages/Django-1.8.18-py2.7.egg/django/core/management/commands/runserver.py", line 168, in check_migrations
    executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
  File "/home/paul/tournai/venv/local/lib/python2.7/site-packages/Django-1.8.18-py2.7.egg/django/db/migrations/executor.py", line 19, in __init__
    self.loader = MigrationLoader(self.connection)
  File "/home/paul/tournai/venv/local/lib/python2.7/site-packages/Django-1.8.18-py2.7.egg/django/db/migrations/loader.py", line 47, in __init__
    self.build_graph()
  File "/home/paul/tournai/venv/local/lib/python2.7/site-packages/Django-1.8.18-py2.7.egg/django/db/migrations/loader.py", line 185, in build_graph
    self.load_disk()
  File "/home/paul/tournai/venv/local/lib/python2.7/site-packages/Django-1.8.18-py2.7.egg/django/db/migrations/loader.py", line 93, in load_disk
    for name in os.listdir(directory):
OSError: [Errno 20] Not a directory: '/home/paul/tournai/venv/local/lib/python2.7/site-packages/authentic2_auth_kerberos-1.1.0.5+g198d782-py2.7.egg/authentic2_auth_kerberos/migrations'
```

```
(venv) paul@spare:~/tournai/apps/authentic$ ./authentic2-ctl runserver
Performing system checks...

System check identified no issues (0 silenced).
Unhandled exception in thread started by <function wrapper at 0x7fd88fc7d1b8>
Traceback (most recent call last):
  File "/home/paul/tournai/venv/local/lib/python2.7/site-packages/Django-1.8.18-py2.7.egg/django/utils/autoreload.py", line 229, in wrapper
    fn(*args, **kwargs)
  File "/home/paul/tournai/venv/local/lib/python2.7/site-packages/Django-1.8.18-py2.7.egg/django/core/management/commands/runserver.py", line 116, in inner_run
    self.check_migrations()
  File "/home/paul/tournai/venv/local/lib/python2.7/site-packages/Django-1.8.18-py2.7.egg/django/core/management/commands/runserver.py", line 168, in check_migrations
    executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
  File "/home/paul/tournai/venv/local/lib/python2.7/site-packages/Django-1.8.18-py2.7.egg/django/db/migrations/executor.py", line 19, in __init__
    self.loader = MigrationLoader(self.connection)
  File "/home/paul/tournai/venv/local/lib/python2.7/site-packages/Django-1.8.18-py2.7.egg/django/db/migrations/loader.py", line 47, in __init__
    self.build_graph()
  File "/home/paul/tournai/venv/local/lib/python2.7/site-packages/Django-1.8.18-py2.7.egg/django/db/migrations/loader.py", line 185, in build_graph
    self.load_disk()
  File "/home/paul/tournai/venv/local/lib/python2.7/site-packages/Django-1.8.18-py2.7.egg/django/db/migrations/loader.py", line 93, in load_disk
    for name in os.listdir(directory):
OSError: [Errno 20] Not a directory: '/home/paul/tournai/venv/local/lib/python2.7/site-packages/django_mellon-1.2.29-py2.7.egg/mellon/migrations'
```

après un `python setup.py develop`, puis une application des migrations, authentic fonctionne

Installation des paquets
krb5-config krb5-clients krb5-user

DB master  key : testdb

```
Now that your realm is set up you may wish to create an administrative
principal using the addprinc subcommand of the kadmin.local program.
Then, this principal can be added to /etc/krb5kdc/kadm5.acl so that
you can use the kadmin program on other computers.  Kerberos admin
principals usually belong to a single user and end in /admin.  For
example, if jruser is a Kerberos administrator, then in addition to
the normal jruser principal, a jruser/admin principal should be
created.
```

```
[domain_realm]
	.mit.edu = ATHENA.MIT.EDU
	mit.edu = ATHENA.MIT.EDU
	.media.mit.edu = MEDIA-LAB.MIT.EDU
	media.mit.edu = MEDIA-LAB.MIT.EDU
	.csail.mit.edu = CSAIL.MIT.EDU
	csail.mit.edu = CSAIL.MIT.EDU
	.whoi.edu = ATHENA.MIT.EDU
	whoi.edu = ATHENA.MIT.EDU
	.stanford.edu = stanford.edu
	.slac.stanford.edu = SLAC.STANFORD.EDU
        .toronto.edu = UTORONTO.CA
        .utoronto.ca = UTORONTO.CA

	localhost = ENTROUVERT.LAN
	entrouvert.lan = ENTROUVERT.LAN
	.entrouvert.lan = ENTROUVERT.LAN

```

```
kadmin.local:  listprincs
K/M@ENTROUVERT.LAN
kadmin/admin@ENTROUVERT.LAN
kadmin/changepw@ENTROUVERT.LAN
kadmin/spare.entrouvert.lan@ENTROUVERT.LAN
kiprop/spare.entrouvert.lan@ENTROUVERT.LAN
krbtgt/ENTROUVERT.LAN@ENTROUVERT.LAN
kadmin.local:  quit
```


```
kadmin.local:  addprinc root/admin
WARNING: no policy specified for root/admin@ENTROUVERT.LAN; defaulting to no policy
Enter password for principal "root/admin@ENTROUVERT.LAN": 
Re-enter password for principal "root/admin@ENTROUVERT.LAN": 
Principal "root/admin@ENTROUVERT.LAN" created.
kadmin.local:  quit
```

```
kadmin:  addprinc paul
WARNING: no policy specified for paul@ENTROUVERT.LAN; defaulting to no policy
Enter password for principal "paul@ENTROUVERT.LAN": 
Re-enter password for principal "paul@ENTROUVERT.LAN": 
Principal "paul@ENTROUVERT.LAN" created.
```

setspn pour
set servive principal name
quel paquet debian ?

reprise POC CC
```
Souhaitez-vous continuer ? [O/n] 
(Lecture de la base de données... 58576 fichiers et répertoires déjà installés.)
Suppression de courier-authlib (0.66.1-1+b1) ...
Suppression de dbconfig-common (1.8.47+nmu3+deb8u1) ...
Suppression de expect (5.45-6) ...
Suppression de graphviz (2.38.0-7) ...
Suppression de libgvc6 (2.38.0-7) ...
Suppression de libpangocairo-1.0-0:amd64 (1.36.8-3) ...
Suppression de libpangoft2-1.0-0:amd64 (1.36.8-3) ...
Suppression de libpango-1.0-0:amd64 (1.36.8-3) ...
Suppression de fontconfig (2.11.0-6.3+deb8u1) ...
Suppression de libcairo2:amd64 (1.14.0-2.1+deb8u2) ...
Suppression de libgvpr2 (2.38.0-7) ...
Suppression de libcgraph6 (2.38.0-7) ...
Suppression de libcdt5 (2.38.0-7) ...
Suppression de libthai0:amd64 (0.1.21-1) ...
Suppression de libdatrie1:amd64 (0.2.8-1) ...
Suppression de libgl1-mesa-dri:amd64 (10.3.2-1+deb8u1) ...
Suppression de libdrm-intel1:amd64 (2.4.58-2) ...
Suppression de libdrm-nouveau2:amd64 (2.4.58-2) ...
Suppression de libdrm-radeon1:amd64 (2.4.58-2) ...
Suppression de x11-utils (7.7+2) ...
Suppression de libgl1-mesa-glx:amd64 (10.3.2-1+deb8u1) ...
Suppression de libdrm2:amd64 (2.4.58-2) ...
Suppression de libelf1:amd64 (0.159-4.2) ...
Suppression de libfontenc1:amd64 (1:1.1.2-1+b2) ...
Suppression de libglapi-mesa:amd64 (10.3.2-1+deb8u1) ...
Suppression de libharfbuzz0b:amd64 (0.9.35-2) ...
Suppression de libgraphite2-3:amd64 (1.3.6-1~deb8u1) ...
Suppression de xterm (312-2) ...
Suppression de libxaw7:amd64 (2:1.0.12-2+b1) ...
Suppression de libxmu6:amd64 (2:1.1.2-1) ...
Suppression de libxt6:amd64 (1:1.1.4-1+b1) ...
Suppression de libjs-leaflet (0.7.3~dfsg-1) ...
Suppression de libllvm3.5:amd64 (1:3.5-10) ...
Suppression de libpathplan4 (2.38.0-7) ...
Suppression de libpciaccess0:amd64 (0.13.2-3+b1) ...
Suppression de libpixman-1-0:amd64 (0.32.6-3) ...
Suppression de libsm6:amd64 (2:1.2.2-1+b1) ...
Suppression de libthai-data (0.1.21-1) ...
Suppression de python-utidylib (0.2-9) ...
Suppression de libtidy-0.99-0 (20091223cvs-1.4+deb8u1) ...
Suppression de tk8.6 (8.6.2-1) ...
Suppression de libtk8.6:amd64 (8.6.2-1) ...
Suppression de libtxc-dxtn-s2tc0:amd64 (0~git20131104-1.1) ...
Suppression de libutempter0 (1.1.5-4) ...
Suppression de libx11-xcb1:amd64 (2:1.6.2-3) ...
Suppression de libxcb-dri2-0:amd64 (1.10-3+b1) ...
Suppression de libxcb-dri3-0:amd64 (1.10-3+b1) ...
Suppression de libxcb-glx0:amd64 (1.10-3+b1) ...
Suppression de libxcb-present0:amd64 (1.10-3+b1) ...
Suppression de libxcb-render0:amd64 (1.10-3+b1) ...
Suppression de libxcb-shape0:amd64 (1.10-3+b1) ...
Suppression de libxcb-shm0:amd64 (1.10-3+b1) ...
Suppression de libxcb-sync1:amd64 (1.10-3+b1) ...
Suppression de libxcomposite1:amd64 (1:0.4.4-1) ...
Suppression de libxdamage1:amd64 (1:1.1.4-2+b1) ...
Suppression de libxdot4 (2.38.0-7) ...
Suppression de libxxf86vm1:amd64 (1:1.1.3-1+b1) ...
Suppression de libxxf86dga1:amd64 (2:1.1.4-1+b1) ...
Suppression de libxfixes3:amd64 (1:5.0.1-2+b2) ...
Suppression de libxft2:amd64 (2.3.2-1) ...
Suppression de libxtst6:amd64 (2:1.2.2-1+b1) ...
Suppression de libxi6:amd64 (2:1.7.4-1+b2) ...
Suppression de libxinerama1:amd64 (2:1.1.3-1+b1) ...
Suppression de libxmuu1:amd64 (2:1.1.2-1) ...
Suppression de libxrandr2:amd64 (2:1.4.2-1+b1) ...
Suppression de libxrender1:amd64 (1:0.9.8-1+b1) ...
Suppression de libxshmfence1:amd64 (1.1-4) ...
Suppression de libxss1:amd64 (1:1.2.2-1) ...
Suppression de libxv1:amd64 (2:1.0.10-1+b1) ...
Suppression de python-hobo (0.63-1~eob80+2) ...
Suppression de python-celery (3.1.13-3) ...
Suppression de python-kombu (3.0.21-2) ...
Suppression de python-amqp (1.4.5-2) ...
Suppression de python-pyexcel-xls (0.2.2-1~eob80+2) ...
Suppression de python-xlwt (0.7.5+debian1-1) ...
Suppression de python-antlr (2.7.7+dfsg-6) ...
Suppression de python-anyjson (0.3.3-1) ...
Suppression de python-billiard (3.3.0.18-2) ...
Suppression de python-celery-common (3.1.13-3) ...
Suppression de python-cffi (1.4.2-2~bpo8+1) ...
Suppression de python-raven (5.11.1-1~eob80+1) ...
Suppression de python-contextlib2 (0.4.0-2) ...
Suppression de python-crypto (2.6.1-5+deb8u1) ...
Suppression de python-dateutil (2.2-2) ...
Suppression de python-soappy (0.12.22-1) ...
Suppression de python-defusedxml (0.4.1-2) ...
Suppression de python-django-import-export (0.2.7.6.1~eob80+1) ...
Suppression de python-diff-match-patch (20121119-1) ...
Suppression de python-django-admin-tools (0.6.0-1~eob80+2) ...
Suppression de python-django-filters (0.11.0.1-1~eob80+1) ...
Suppression de python-django-jsonfield (0.9.19.10-1~eob80+1) ...
Suppression de python-django-model-utils (2.5.2-1~eob80+2) ...
Suppression de python-django-select2 (4.3.1.6-1~eob80+1) ...
Suppression de python-django-tables2 (1.0.4.6-1~eob80+1) ...
Suppression de python-django-tenant-schemas (1.5.2.1.61+g35b4473-1~eob80+1) ...
Suppression de python-djangorestframework (3.3.2-1~eob80+1) ...
Suppression de python-dns (2.3.6-3) ...
Suppression de python-dnspython (1.12.0-1) ...
Suppression de python-feedparser (5.1.3-3) ...
Suppression de python-graypy (0.2.11-1) ...
Suppression de python-qrcode (5.0.1-1) ...
Suppression de python-imaging (2.6.1-2+deb8u3) ...
Suppression de python-jwcrypto (0.3.2-2) ...
Suppression de python-ldaptools (0.14.5+gd32d58a-1~eob80+1) ...
Suppression de python-ldap (2.4.10-1) ...
Suppression de python-libxml2 (2.9.1+dfsg1-5+deb8u4) ...
Suppression de python-magic (1:5.22+15-2+deb8u3) ...
Suppression de python-mailer (0.7-1) ...
Suppression de python-markdown (2.5.1-2) ...
Suppression de python-pyexcel-ods (0.2.1-1~eob80+3) ...
Suppression de python-odf (1.2.0-2) ...
Suppression de python-phpserialize (1.3-1~eob80+1) ...
Suppression de python-pycparser (2.10+dfsg-3) ...
Suppression de python-ply (3.4-5) ...
Suppression de python-pyexcel-io (0.2.3-1~eob80+3) ...
Suppression de python-pyparsing (2.0.3+dfsg1-1) ...
Suppression de python-scgi (1.13-1.1) ...
Suppression de python-suds (0.4.1-15) ...
Suppression de python-tablib (0.9.11-2) ...
Suppression de python-wstools (0.4.3-2) ...
Suppression de python-xlrd (0.9.2-1) ...
Suppression de tcl-expect:amd64 (5.45-6) ...
Suppression de xbitmaps (1.1.1-2) ...
Suppression de libice6:amd64 (2:1.0.9-1+b1) ...
Suppression de libxext6:amd64 (2:1.3.3-1) ...
Suppression de x11-common (1:7.7+7) ...
Traitement des actions différées (« triggers ») pour man-db (2.7.0.2-5) ...
Traitement des actions différées (« triggers ») pour libc-bin (2.19-18+deb8u7) ...
Traitement des actions différées (« triggers ») pour mime-support (3.58) ...
Traitement des actions différées (« triggers ») pour python-support (1.0.15) ...
[master c7e7233] committing changes in /etc after apt run
 Author: Paul Marillonnet <pmarillonnet@entrouvert.com>
  3 files changed, 3 deletions(-)
   delete mode 120000 alternatives/libtxc-dxtn-x86_64-linux-gnu
    delete mode 120000 alternatives/x-terminal-emulator
     delete mode 120000 alternatives/x-terminal-emulator.1.gz

```


```
(venv) paul@spare:~/tournai/apps/authentic$ ./authentic2-ctl findstatic css/gadjo.css
No matching file found for 'css/gadjo.css'.
```
Pourtant l'appli gadjo est installée.
Le scss est bien là mais pas le css
Comment générer le second à partir du premier ?

Ok gadjo à partir des sources

Maintenant : essayer de transmettre les principals Kerberos à Authentic

Pour ce faire (cf mail Benj) : une des variables est à paramétrer

D'abord, création d'un keytab pour l'utilisateur paul
```
$ ktutil 
ktutil:  add
addent     add_entry  
ktutil:  addent -password -p paul@ENTROUVERT.LAN -k 1 -e rc4-hmac
Password for paul@ENTROUVERT.LAN: 
ktutil:  addent -password -p paul@ENTROUVERT.LAN -k 1 -e aes256-cts
Password for paul@ENTROUVERT.LAN: 
ktutil:  wkt paul.keytab
ktutil:  quit
```

> kinit paul@ENTROUVERT.LAN -k -t paul.keytab; myscript

Quelle syntaxe pour le support de l'authentification dans les scripts ?

```
$ klist -k paul.keytab
Keytab name: FILE:paul.keytab
KVNO Principal
---- --------------------------------------------------------------------------
   1 paul@ENTROUVERT.LAN
   1 paul@ENTROUVERT.LAN
```

La création de la keytab sembe s'être bien déroulée
Voyons ce qu'en dit Authentic
Ajout de KRB5_KTNAME dans les *settings*


BLOCKER : OpenLDAP à la place de l'AD
Pas possible de se connecter au serveur publik de Tournai pour comparer l'installation

L'annuaire tourne-t-il en backend de Publik ou bien en back-end de Kerberos ?

passphrase pour la cle privée RSA : testauth

TODO openssl req -new -key server.key -out server.csr

TODO vue d'ensemble du support LDAP dans authentic
voir ce qui se trouve dans authentic2_provisionning_ldap

phases :
- LDAP backend A2
- LDAP backend KRB
- Alfresco avec config Kerberos
dans quelles mesures peut-on substituer la partie spécifique à MS (utilitaires AD, NTLM)

backend A2:
LDAP_AUTH_SETTINGS à remplir
LDAP_BACKEND : boolean
possibilité de récupérer un exemple de fichier de conf ?

./authentic/doc/auth_ldap.rst

installaton de djando_auth_ldap

```
ldapsearch -D "cn=admin,dc=entrouvert,dc=lan" -w test -p 389 -h spare.entrouvert.lan -b "ou=People,dc=entrouvert,dc=lan" -s sub "(ObjectClass=*)" * +
```

Comment s'assurer que la config LDAP est bien fonctionnelle ?
Les utilisateurs ajoutés dans le group des utilisateurs ne devraient-ils pas apparaître ?

Lecture de /src/authentic2/backends/ldap_backend.py

le fichier /etc/ssl/certs/ca-certificates.crt, contenant les certificats de la machine, est bien présent.

SESSION_LDAP_DATA_KEY à creuser

Par ailleurs, il semblerait que les config décrite dans la doc aient été remplacées par un dictionnaire LDAP_AUTH_SETTINGS
TOD debugger pour voir si ce n'est pas authentic que se charger de remplir le dict
Pas rempli, LDAP_AUTH_SETTINGS à remplir

la documentation n'est pas à jour, les tests par contre le sont :

```
@pytest.mark.django_db
def test_simple_with_binddn(slapd, settings, client):
    settings.LDAP_AUTH_SETTINGS = [{
        'url': [slapd.ldap_url],
        'binddn': DN,
        'bindpw': PASS,
        'basedn': 'o=orga',
        'use_tls': False,
    }]
```

On dirait qu'un peu de kerberos est mêlé à tout ça
Est-ce que le lien entre Kerberos et Authentic se fait aussi via l'annuaire ?


Reprise :
erreurs a2 frontend dans l'interface /manage, alors que /admin fonctionne
```
TemplateDoesNotExist at /manage/users/
django_tables2/table.html
désinstallation et réinstallation de django_tables2
seul le .egg était présent dans le venv, et pourtant pip le considérait comme installé
TODO voir s'il n'y aurait pas une modif à effectuer dans la déclaration des dépendances d'authentic ?

incompatibilité django_tables2?
```

```
File "/home/paul/tournai/venv/local/lib/python2.7/site-packages/django_tables2/columns/base.py", line 249, in from_field
    verbose_name = field.verbose_name
    AttributeError: 'GenericForeignKey' object has no attribute 'verbose_name'
```

Pb : les tests a2_auth_krb echouent
`E           ImproperlyConfigured: settings.DATABASES is improperly configured. Please supply the NAME value.`

export DB_ENGINE='sqlite3' ne change rien
NAME pas configuré correctement ?
Pourtant cet argument est fourni et devrait engendrer la création de la base

FIX: django_tables2==1.0*.7*

approvisionnement de la base authentic depuis LDAP:
Benj:
les comptes sont créés dans authentic dans deux cas:
au moment du login un compte est créé automatiquement en base authentic
ou alors lors de l'exécution de la commande sync-ldap-users

./authentic2-ctl sync-ldap-users fonctionne, la base authentic est bien provisionnée à partir des entrées ou=People de l'anuaire

TODO backend LDAP pour Kerberos
Via sasl2 ?
Ou bien par PAM/NSS ?
Solution 2 parait plus simple pour une installation en local ?
Quelle utilité des *principals* si les utilisateurs sont fournis par le LDAP ?
Voir quel scenarion d'approvisionnement est pris en charge ?

Installation du paquet krb5-kdc-ldap

ldap_kerberos_container_dn
ldap_kdc_sasl_authcid
ldap_kdc_sasl_authzid
ldap_kdc_sasl_mech
ldap_kdc_sasl_realm
ldap_kadmind_dn
ldap_kadmind_sasl_authcid
ldap_kadmind_sasl_authzid
ldap_kadmind_sasl_mech
ldap_kadmind_sasl_realm
ldap_service_password_file
ldap_servers
ldap_conns_per_server

On essaie de préciser les trois paramètres nécessaires:

ldap_kdc_dn=

Erreur sur le fichier de conf du kdc
```
# kdb5_ldap_util stashsrvpw "ou=Special Users,dc=entrouvert,dc=lan"
kdb5_ldap_util: ldap_service_password_file not configured while getting service password filename
```
et pourtant si !
ok mauvaise utilisation de la commande

Pourquoi un conteneur à part ?
Enter DN of Kerberos container: cn=krbcontainer,dc=entrouvert,dc=lan


On peut retrouver à tout moment la config, 'auto-contenue' dans l'annuaire :
```
ldapsearch -D "cn=admin,cn=config" -w test -p 389 -h spare.entrouvert.lan -b "cn=config" -s sub "(ObjectClass=*)"
```

divergence entre doc et installation
olcDatabase{1}hdb vs olcDatabase{1}mdb
C'est simplement le backend de stockage de la base qui change


Ajout d'un index de base:
```
ldapmodify  -x -D cn=admin,cn=config -W
Enter LDAP Password: 
dn: olcDatabase={1}mdb,cn=config
add: olcDbIndex
olcDbIndex: krbPrincipalName eq,pres,sub
modifying entry "olcDatabase={1}mdb,cn=config"
```

quelles différences entre pres et eq ? la doc n'est pas très claire à ce sujet.

Erreur lors de la définition des ACL
```
# ldapmodify -x -D cn=admin,cn=config -W
Enter LDAP Password: 
dn: olcDatabase={1}mdb,cn=config
replace: olcAccess
olcAccess: to attrs=userPassword,shadowLastChange,krbPrincipalKey by
dn="cn=admin,dc=entrouvert,dc=lan" write by anonymous auth by self write by * none
-
add: olcAccess
olcAccess: to dn.base="" by * read
-
add: olcAccess
olcAccess: to * by dn="cn=admin,dc=entrouvert,dc=lan" write by * read
modifying entry "olcDatabase={1}mdb,cn=config"
ldap_modify: Other (e.g., implementation specific) error (80)
   additional info: <olcAccess> handler exited with 1
```

```
$ sudo kdb5_ldap_util -D cn=admin,dc=entrouvert,dc=lan create -subtrees dc=entrouvert,dc=lan -r ENTROUVERT.LAN -s -H ldap://spare.entrouvert.lan
Password for "cn=admin,dc=entrouvert,dc=lan": 
Initializing database for realm 'ENTROUVERT.LAN'
You will be prompted for the database Master Password.
It is important that you NOT FORGET this password.
Enter KDC database master key: 
Re-enter KDC database master key to verify: 
kdb5_ldap_util: Kerberos Container create FAILED: Object class violation while creating realm 'ENTROUVERT.LAN'
```
bug dans kdb5_ldap_util ?

```
o# sudo kdb5_ldap_util -D cn=admin,dc=entrouvert,dc=lan stashsrvpw -f /etc/krb5kdc/service.keyfile cn=admin,dc=entrouvert,dc=lan
Password for "cn=admin,dc=entrouvert,dc=lan": 
Password for "cn=admin,dc=entrouvert,dc=lan": 
Re-enter password for "cn=admin,dc=entrouvert,dc=lan": 

cat /etc/krb5kdc/service.keyfile 
cn=admin,dc=entrouvert,dc=lan#{HEX}74657374
```

addprinc -x dn="uid=paulo,ou=people,dc=example,dc=com" paulo

TODO:
synthese de la piece en 4 actes expliquant le protocole Kerberos (doc MIT)

http://www.openldap.org/lists/openldap-technical/201107/msg00180.html
Suggestion d'un dev OpenLDAP

See:

contrib/slapd-modules/smbk5pwd/

within the source.

Donc il faudrait du Samba en plus ?
Non : installation d'une machine virtuelle windows server 2012 avec un Active Directory dessus

Problème : la VM Windows va-t-elle avoir à la fois les rôles de serveur AD et de poste client ?
mdp admin :
t3st4uth!!

Directory Service Restore Mode (DSRM) password
t3st4uth!!


SYSVOL et NTDS
system volume et NT domain server ?

Le script en powershell est
```
 #
 # Windows PowerShell script for AD DS Deployment
 #
 
 Import-Module ADDSDeployment
 Install-ADDSForest `
 -CreateDnsDelegation:$false `
 -DatabasePath "C:\Windows\NTDS" `
 -DomainMode "Win2012R2" `
 -DomainName "entrouvert.local" `
 -DomainNetbiosName "ENTROUVERT" `
 -ForestMode "Win2012R2" `
 -InstallDns:$true `
 -LogPath "C:\Windows\NTDS" `
 -NoRebootOnCompletion:$false `
 -SysvolPath "C:\Windows\SYSVOL" `
 -Force:$true
```

```
> netdom query fsmo
Schema master               WIN-4HOASL41N0M.entrouvert.local
Domain naming master        WIN-4HOASL41N0M.entrouvert.local
PDC                         WIN-4HOASL41N0M.entrouvert.local
RID pool manager            WIN-4HOASL41N0M.entrouvert.local
Infrastructure master       WIN-4HOASL41N0M.entrouvert.local
The command completed successfully.
```

Maintenant :


=> L'AD tourne sur la VM

Maintenant :
Communication Kerberos et AD ?
kinit pour récupérer un ticket
MIT Kerberos ou AD ?

le host-only adapter semble fonctionner :
```
> ping 192.168.56.1
Pinging 192.168.56.1 with 32 bytes of data:
Reply from 192.168.56.1: bytes=32 time<1ms TTL=64
Reply from 192.168.56.1: bytes=32 time<1ms TTL=64
Reply from 192.168.56.1: bytes=32 time<1ms TTL=64
Reply from 192.168.56.1: bytes=32 time<1ms TTL=64

Ping statistics for 192.168.56.1:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
    Approximate round trip times in milli-seconds:
        Minimum = 0ms, Maximum = 0ms, Average = 0ms
```

Authentic accessible depuis le serveur AD
pour ce faire : ajout d'un nom de domaine sur l'IP rattachée au host-only adapter côté hôte, par exemple phyhost
./authentic2-ctl runserver phyhost:8080

On va pouvoir récupérer le ticket côté client et utiliser le KDC pour l'authent

Ecoute sur le port 80 :  c'est sale mais ça marche :
$ sudo `which python` ./authentic2-ctl runserver phyhost:80

```
> setspn -A HTTP/192.168.56.1 serveuridp
Checking domain DC=entrouvert,DC=local
setspn : FindDomainForAccount: Call to DsGetDcNameWithAccountW failed with return value 0x00000525
At line:1 char:1
+ setspn -A HTTP/192.168.56.1 serveuridp 2> setspn.txt
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (FindDomainForAc...alue 0x00000525:String) [], RemoteException
        + FullyQualifiedErrorId : NativeCommandError
         
         Unable to locate account serveuridp
```
OK création du compte d'abord...

Tools -> Active Directory Users and Computers
mdp de l'utilisateur
t3stp!!

a2idp@entrouvert.local / t3stauth!!

Il faut sans doute créer un compte valide pour les credentials du principal, non ?

```
PS C:\Users\Administrator> ktpass -princ HTTP/192.168.56.1@ENTROUVERT.LOCAL -mapuser a2idp@ENTROUVERT.LOCAL -crypto ALL
-ptype KRB5_NT_PRINCIPAL -pass t3stauth!! -out c:\idp.keytab
Targeting domain controller: WIN-4HOASL41N0M.entrouvert.local
Using legacy password setting method
Successfully mapped HTTP/192.168.56.1 to a2idp.
Key created.
Key created.
Key created.
Key created.
Key created.
Output keytab to c:\idp.keytab:
Keytab version: 0x502
keysize 61 HTTP/192.168.56.1@ENTROUVERT.LOCAL ptype 1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x1 (DES-CBC-CRC) keylength 8 (0x3
d98dff7a701b0c4)
keysize 61 HTTP/192.168.56.1@ENTROUVERT.LOCAL ptype 1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x3 (DES-CBC-MD5) keylength 8 (0x3
d98dff7a701b0c4)
keysize 69 HTTP/192.168.56.1@ENTROUVERT.LOCAL ptype 1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x17 (RC4-HMAC) keylength 16 (0xaf
a5a93065041df2e1687d9d9ad1445d)
keysize 85 HTTP/192.168.56.1@ENTROUVERT.LOCAL ptype 1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x12 (AES256-SHA1) keylength 32 (0
xeb6c5d4af02c76d6c4104cbc8ab117e04c5a18c865c39df30bec97d50defc72b)
keysize 69 HTTP/192.168.56.1@ENTROUVERT.LOCAL ptype 1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x11 (AES128-SHA1) keylength 16 (0
x3902936547e7d0cd4706c38a4a4fc0fb)
```

On copie la keytab côté IdP,
on la renseigne dans la conf authentic via KRB5_KTNAME

Est-ce suffisant ?

Installation d'une VM client
root / admin

paul / testp

apt-install krb5-user
default realm configuration : ENTROUVERT@LOCAL

client mis dans le host-only adapter

Prochaine etape : Se logger à partir du Kerberos

installation côté client
krb5-user
libpam-krb5
libpam-ccreds
auth-client-config ? paquet ubuntu seulement ?

récuperation d'un ticket valide pour 10h, comme le montre la commande klist

On essaie de se connecter à l'IDP 
sudo `which python` ./authentic2-ctl runserver phyhost:80


$ kinit pmarillonnet@ENTROUVERT.LOCAL
Password for pmarillonnet@ENTROUVERT.LOCAL: 

$ klist
Ticket cache: FILE:/tmp/krb5cc_1003
Default principal: pmarillonnet@ENTROUVERT.LOCAL

Valid starting       Expires              Service principal
05/15/2017 15:16:37  05/16/2017 01:16:37  krbtgt/ENTROUVERT.LOCAL@ENTROUVERT.LOCAL
    renew until 05/16/2017 15:16:34


installation de django_kerberos ?

BLOCKER :
TemplateDoesNotExist at /accounts/kerberos/login/

django_kerberos/unauthorized.html

FIX: reinstallation de django_kerberos


Authentic2 - phyhost
Homepage
Access unauthorized please refresh your ticket cache 

LA RFC est claire à ce sujet:
The 401 (Unauthorized) response message is used by an origin server
   to challenge the authorization of a user agent.

lire les specs de django_kerberos pour comprendre ce qui ne va pas...
KERBEROS_HOSTNAME
KERBEROS_BACKEND_CREATE
KERBEROS_BACKEND_ADMIN_REGEXP
KERBEROS_SERVICE_PRINCIPAL
KERBEROS_KEEP_PASSWORD
à paramétrer
DONE


https://technet.microsoft.com/en-us/library/cc759550%28v=ws.10%29.aspx

Ajout d'une entrée DNS A record pour le serveur IdP

On peut alors éditer le /etc/resolv.conf pour l'ajout du DNS tournant sur la VM windows 2012:
nameserver 192.168.56.101


PS C:\Users\Administrator> ping phyhost.entrouvert.local

Pinging phyhost.entrouvert.local [192.168.56.1] with 32 bytes of data:
Reply from 192.168.56.1: bytes=32 time<1ms TTL=64
Reply from 192.168.56.1: bytes=32 time<1ms TTL=64
Reply from 192.168.56.1: bytes=32 time<1ms TTL=64
Reply from 192.168.56.1: bytes=32 time<1ms TTL=64

Ping statistics for 192.168.56.1:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
    Approximate round trip times in milli-seconds:
        Minimum = 0ms, Maximum = 0ms, Average = 0ms

pb de DNS résolu
maintenant :
×Principal pmarillonnet@ENTROUVERT.LOCAL could not be authenticated

$ klist
Ticket cache: FILE:/tmp/krb5cc_1003
Default principal: pmarillonnet@ENTROUVERT.LOCAL

Valid starting       Expires              Service principal
05/18/2017 11:01:38  05/18/2017 21:01:38  krbtgt/ENTROUVERT.LOCAL@ENTROUVERT.LOCAL
    renew until 05/19/2017 11:01:35
    05/18/2017 11:05:22  05/18/2017 21:01:38  HTTP/phyhost.entrouvert.local@
        renew until 05/19/2017 11:01:35
        05/18/2017 11:05:22  05/18/2017 21:01:38  HTTP/phyhost.entrouvert.local@ENTROUVERT.LOCAL
            renew until 05/19/2017 11:01:35


essai pour la creation manuelle de l'utilisateur dans la base de l'IDP : ne change rien au problème

autre piste : virer complètement la conf dans /etc/hosts
utiliser le DHCP sur la VM à la place ?

$ dig WIN-4HOASL41N0M.entrouvert.local

; <<>> DiG 9.10.3-P4-Debian <<>> WIN-4HOASL41N0M.entrouvert.local
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54521
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4000
;; QUESTION SECTION:
;WIN-4HOASL41N0M.entrouvert.local. IN   A

;; ANSWER SECTION:
WIN-4HOASL41N0M.entrouvert.local. 3600 IN A 192.168.56.101
WIN-4HOASL41N0M.entrouvert.local. 3600 IN A 10.0.2.15

;; Query time: 0 msec
;; SERVER: 192.168.56.101#53(192.168.56.101)
;; WHEN: Thu May 18 17:04:46 CEST 2017
;; MSG SIZE  rcvd: 93

et

$ dig phyhost.entrouvert.local

; <<>> DiG 9.10.3-P4-Debian <<>> phyhost.entrouvert.local
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 27587
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4000
;; QUESTION SECTION:
;phyhost.entrouvert.local.  IN  A

;; ANSWER SECTION:
phyhost.entrouvert.local. 3600  IN  A   192.168.56.1

;; Query time: 0 msec
;; SERVER: 192.168.56.101#53(192.168.56.101)
;; WHEN: Thu May 18 17:12:42 CEST 2017
;; MSG SIZE  rcvd: 69


Feedback benj : 

Le sso fonctionne, c'est dans le backend de l'Idp que quelque chose ne va pas.

=> on dirait une erreur dans le backend de l'IDP
le ldap a été retiré, c'est pas la peine
Comment configurer le DJANGO_BACKEND de authentic2-auth-kerberos ?


TODO : début de rédaction de la note DONE

Maintenant : déterminer d'où vient cette erreur de homepage utilisateur une fois le SSO effectué
Ok, fausse alerte, pas de debug, c'est simplement l'option `CREATE_USER` à True qui remplit un utilisateur vide.

//CURRENT2

## Etude synchro brouillon

### Apache Syncope
blocker : GUI bloquée à
http://localhost:8080/syncope-console/wicket/bookmarkable/org.apache.syncope.client.console.pages.Realms?5&selectedIndex=1

quels logs ?
Est-ce la config de la base qui n'est pas correcte ?
Comment débugger un jar ... ?

Impossible d'utiliser l'outil pour l'instant, et les logs de tomcat restent assez flous (/var/log/tomcat8/idm.log):
2017-04-27 10:02:20,886 [] [midPointScheduler_Worker-6] INFO (org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl): HHH000010: On release of batch it still contained JDBC statements

Interface de type cronjob pour les reconciliations
ou plutôt 'at' : possibilité de planifier des taĉhes ponctuelles de réconciliation/synchro

TODO: lecture du code si le tuto n'est pas assez précis sur les mécanismes de synchro
Le connecteur LDAP a été configuré pour aller chercher les posixAccount présents sur l'une des branches de l'annuaire, mais pourtant impossible d'approvisionner syncope.

Puisque l'interface frontend semble boguée, peut-être vaut-il mieux utiliser l'API Rest ?

Impossible de créer des utilisateurs pour l'instant, mais l'API REST semble toutefois fonctionelle:
http://localhost:8080/syncope/rest/users
-> mire de login
renvoie un fichier XML
```
<?xml version="1.0"?>
<syncope2:pagedResult>
  <page>1</page>
  <result/>
  <size>0</size>
  <totalCount>0</totalCount>
</syncope2:pagedResult>
```

Ok il faudrait maintenant réussir à faire fonctionner le mécanisme de création d'un utilisateur
Pistes possibles
par d'accès en base, les données saisies par l'admin dans l'interface web backoffice ne peuvent être prises en compte
existe-t-il un mode de débug de la base ?

Le code de Syncope laisse à penser que l'ORM est hibernate...
Et pourtant très peu de références dans le code source :
```
$ grep -nri hibernate ./
Binary file ./.git/objects/pack/pack-e851cb26d72f0bc5c04cbcf88b6fb4514fcf1c1d.pack matches
./fit/core-reference/src/main/webapp/WEB-INF/jboss-deployment-structure.xml:33:      <module name="org.hibernate"/>
./CHANGES:362:    * [SYNCOPE-764] - Replace Hibernate Validator with Apache BVal
```

Pourquoi Jboss alors que toute la doc parle de tomcat ?
Pas très clair tout ça

``` xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
  <deployment>
    <exclude-subsystems>
      <subsystem name="webservices"/>
      <subsystem name="jaxrs"/>
    </exclude-subsystems>
    <dependencies>
      <module name="org.apache.xalan"/>
    </dependencies>
    <exclusions>
      <module name="javax.ws.rs.api"/>
      <module name="org.apache.cxf"/>
      <module name="org.apache.cxf.impl"/>
      <module name="org.hibernate"/>
      <module name="org.slf4j"/>
      <module name="org.slf4j.impl"/>
      <module name="org.apache.log4j"/>
      <module name="org.jboss.log4j.logmanager"/>
    </exclusions>
  </deployment>
</jboss-deployment-structure>
```
dans la section exclusions
red herring

Réessayons de lancer l'appli avec un cronjob de synchronisation

On dirait que le chargement des archives java se fait de façon 'lazy' par tomcat : la première initialisation prend plusieurs dizaines de secondes

pas seulement un blocage de l'action, même le bouton 'Cancel' ne fonctionne pas

défin

bug :
inversion des classes auxiliaires proposées pour la création des entités,
baseUser pour la création d'un groupe
vs
baseGroup pour la création d'un utilisateur

FIX : bug frontend de la Web UI seulement

Ok la prochaine étape est réellement la synchronisation en tant que telle
L'interface n'est pas clair du tout, il va falloir aller chercher de la doc

Support des workflows dans apache syncope, avec possibilité d'éditer directement le fichier XML

``` xml
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
-->
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xmlns:activiti="http://activiti.org/bpmn" 
             xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" 
             xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" 
             xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" 
             typeLanguage="http://www.w3.org/2001/XMLSchema" 
             expressionLanguage="http://www.w3.org/1999/XPath" 
             targetNamespace="http://activiti.org/bpmn20">
  
  <process id="userWorkflow" name="User Workflow" isExecutable="true">
    <startEvent id="theStart"/>
    <sequenceFlow id="flow1" sourceRef="theStart" targetRef="create"/>
    <serviceTask id="create" name="Create" activiti:expression="#{create.execute(execution.processInstanceId)}"/>
    <sequenceFlow id="flow2" sourceRef="create" targetRef="activate"/>
    <scriptTask id="activate" name="Activate" scriptFormat="groovy" activiti:autoStoreVariables="false">
      <script>execution.setVariable("propagateEnable", Boolean.TRUE);</script>
    </scriptTask>
    <sequenceFlow id="flow3" sourceRef="activate" targetRef="active"/>
    <userTask id="active" name="Active"/>
    <sequenceFlow id="flow8" sourceRef="active" targetRef="activeGw"/>
    <exclusiveGateway id="activeGw"/>
    <sequenceFlow id="active2Update" sourceRef="activeGw" targetRef="update">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${task == 'update'}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="active2Suspend" sourceRef="activeGw" targetRef="suspend">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${task == 'suspend'}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="active2Delete" sourceRef="activeGw" targetRef="delete">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${task == 'delete'}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="active2RequestPasswordReset" sourceRef="activeGw" targetRef="generateToken4PasswordReset">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${task == 'requestPasswordReset'}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="active2ConfirmPasswordReset" sourceRef="activeGw" targetRef="checkToken4ConfirmPasswordReset">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${task == 'confirmPasswordReset'}]]></conditionExpression>
    </sequenceFlow>
    <serviceTask id="update" name="Update" activiti:expression="#{update.execute(execution.processInstanceId)}"/>
    <sequenceFlow id="sid-EA22026A-25F0-4ED0-AB6E-9CE9CE74623C" sourceRef="update" targetRef="active"/>
    <serviceTask id="suspend" name="Suspend" activiti:expression="#{suspend.execute(execution.processInstanceId)}"/>
    <sequenceFlow id="flow10" sourceRef="suspend" targetRef="suspended"/>
    <userTask id="suspended" name="Suspended"/>
    <sequenceFlow id="flow11" sourceRef="suspended" targetRef="suspendedGw"/>
    <exclusiveGateway id="suspendedGw"/>
    <sequenceFlow id="suspended2Reactivate" sourceRef="suspendedGw" targetRef="reactivate">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${task == 'reactivate'}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="suspended2Delete" sourceRef="suspendedGw" targetRef="delete">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${task == 'delete'}]]></conditionExpression>
    </sequenceFlow>
    <serviceTask id="reactivate" name="Reactivate" activiti:expression="#{reactivate.execute(execution.processInstanceId)}"/>
    <sequenceFlow id="flow12" sourceRef="reactivate" targetRef="active"/>
    
    <serviceTask id="generateToken4PasswordReset" name="Generate Token" activiti:expression="#{generateToken.execute(execution.processInstanceId)}"/>
    <sequenceFlow id="sid-7F78CE07-A7A1-467F-BB4B-40FB234AEFF7" sourceRef="generateToken4PasswordReset" targetRef="notify4RequestPasswordReset"/>
    <serviceTask id="notify4RequestPasswordReset" name="Notification" activiti:expression="#{notify.execute(execution.processInstanceId)}"/>
    <sequenceFlow id="sid-CF9ACA40-7750-47C3-A508-7250D24D4F1F" sourceRef="notify4RequestPasswordReset" targetRef="active"/>

    <serviceTask id="checkToken4ConfirmPasswordReset" name="Check token, remove and update password" activiti:expression="#{passwordReset.execute(execution.processInstanceId)}"/>
    <sequenceFlow id="sid-3E9FE01D-CC60-4A95-B356-CA0DC000FAD6" sourceRef="checkToken4ConfirmPasswordReset" targetRef="notify4ConfirmPasswordReset"/>
    <serviceTask id="notify4ConfirmPasswordReset" name="Notification" activiti:expression="#{notify.execute(execution.processInstanceId)}"/>
    <sequenceFlow id="sid-A37806A7-6B7B-48A2-BB37-DAE640231144" sourceRef="notify4ConfirmPasswordReset" targetRef="active"/>
    
    <serviceTask id="delete" name="Delete" activiti:expression="#{delete.execute(execution.processInstanceId)}"/>
    <sequenceFlow id="flow99" sourceRef="delete" targetRef="theEnd"/>
    <!-- Recertification tasks -->
    <userTask id="recertificationRequest" name="Recertification Request" activiti:formKey="recertify">
      <extensionElements>
        <activiti:formProperty id="fullname" name="Identity" type="string" expression="${user.getPlainAttr('fullname').getUniqueValue().getStringValue()}" writable="false"/> 
        <activiti:formProperty id="username" name="Username" type="string" expression="${user.username}" writable="false"/>
        <activiti:formProperty id="approve" name="Recertify?" type="boolean" required="true"/>
        <activiti:formProperty id="rejectReason" name="Reason for not recertifying" type="string" variable="rejectReason"/>
      </extensionElements>
    </userTask>
    <serviceTask id="recertify-task" name="Recertify" activiti:expression="#{recertify.execute(execution.processInstanceId)}"/>
    <sequenceFlow id="recert-request-start-flow" sourceRef="activeGw" targetRef="recertificationRequest">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${task == 'request-certify'}]]></conditionExpression>
    </sequenceFlow>
    <exclusiveGateway id="recert-condition"/>
    <sequenceFlow id="recert-flow1" sourceRef="recertificationRequest" targetRef="recertify-task"/>
    <sequenceFlow id="recert-flow2" sourceRef="recertify-task" targetRef="recert-condition"/>
    <sequenceFlow id="recert-approved-flow" sourceRef="recert-condition" targetRef="active">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${approve}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="recert-denied-flow" sourceRef="recert-condition" targetRef="suspend">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${!approve}]]></conditionExpression>
    </sequenceFlow>
    <!-- End Recertification flow -->
    <endEvent id="theEnd"/>
  </process>
  
  <bpmndi:BPMNDiagram id="BPMNDiagram_userWorkflow">
    <bpmndi:BPMNPlane bpmnElement="userWorkflow" id="BPMNPlane_userWorkflow">
      <bpmndi:BPMNShape bpmnElement="recertificationRequest" id="BPMNShape_recertificationRequest">
        <omgdc:Bounds height="80.0" width="100.0" x="1370.0" y="375.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="recertify-task" id="BPMNShape_recertify-task">
        <omgdc:Bounds height="80.0" width="100.0" x="1230.0" y="375.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="recert-condition" id="BPMNShape_recert-condition">
        <omgdc:Bounds height="39.99999999999994" width="40.0" x="1178.1817939458806" y="475.4545368832992"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="theStart" id="BPMNShape_theStart">
        <omgdc:Bounds height="30.0" width="30.0" x="540.0" y="525.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="create" id="BPMNShape_create">
        <omgdc:Bounds height="60.00000000000006" width="100.0" x="620.0" y="509.99999999999994"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="active" id="BPMNShape_active">
        <omgdc:Bounds height="60.0" width="100.0" x="1030.0" y="511.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="activeGw" id="BPMNShape_activeGw">
        <omgdc:Bounds height="40.0" width="40.0" x="1400.0" y="520.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="update" id="BPMNShape_update">
        <omgdc:Bounds height="60.0" width="100.0" x="1370.0" y="615.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="suspend" id="BPMNShape_suspend">
        <omgdc:Bounds height="60.0" width="100.0" x="1490.0" y="370.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="suspended" id="BPMNShape_suspended">
        <omgdc:Bounds height="60.0" width="100.0" x="1640.0" y="370.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="suspendedGw" id="BPMNShape_suspendedGw">
        <omgdc:Bounds height="40.0" width="40.0" x="1820.0" y="380.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="reactivate" id="BPMNShape_reactivate">
        <omgdc:Bounds height="60.0" width="100.0" x="1940.0" y="290.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="generateToken4PasswordReset" id="BPMNShape_generateToken4PasswordReset">
        <omgdc:Bounds height="81.0" width="121.0" x="1515.0" y="604.5"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="notify4RequestPasswordReset" id="BPMNShape_notify4RequestPasswordReset">
        <omgdc:Bounds height="81.0" width="121.0" x="1515.0" y="750.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="checkToken4ConfirmPasswordReset" id="BPMNShape_checkToken4ConfirmPasswordReset">
        <omgdc:Bounds height="81.0" width="121.0" x="1725.0" y="664.5"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="notify4ConfirmPasswordReset" id="BPMNShape_notify4ConfirmPasswordReset">
        <omgdc:Bounds height="81.0" width="121.0" x="1725.0" y="810.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="delete" id="BPMNShape_delete">
        <omgdc:Bounds height="60.0" width="100.0" x="1940.0" y="438.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="theEnd" id="BPMNShape_theEnd">
        <omgdc:Bounds height="28.0" width="28.0" x="2080.0" y="451.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="activate" id="BPMNShape_activate">
        <omgdc:Bounds height="80.0" width="100.0" x="828.286878319943" y="500.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="recert-approved-flow" id="BPMNEdge_recert-approved-flow">
        <omgdi:waypoint x="1194.0489013105641" y="511.3216442479827"/>
        <omgdi:waypoint x="1194.0489013105641" y="541.0"/>
        <omgdi:waypoint x="1130.0" y="541.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="recert-flow2" id="BPMNEdge_recert-flow2">
        <omgdi:waypoint x="1280.0" y="455.0"/>
        <omgdi:waypoint x="1280.0" y="495.45453688329917"/>
        <omgdi:waypoint x="1218.1817939458806" y="495.45453688329917"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="recert-flow1" id="BPMNEdge_recert-flow1">
        <omgdi:waypoint x="1370.0" y="415.0"/>
        <omgdi:waypoint x="1330.0" y="415.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="recert-denied-flow" id="BPMNEdge_recert-denied-flow">
        <omgdi:waypoint x="1198.1817939458806" y="475.4545368832992"/>
        <omgdi:waypoint x="1198.1817939458806" y="313.6363529725508"/>
        <omgdi:waypoint x="1540.0" y="313.6363529725508"/>
        <omgdi:waypoint x="1540.0" y="370.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow12" id="BPMNEdge_flow12">
        <omgdi:waypoint x="1990.0" y="290.0"/>
        <omgdi:waypoint x="1990.0" y="261.0"/>
        <omgdi:waypoint x="1080.0" y="261.0"/>
        <omgdi:waypoint x="1080.0" y="511.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow11" id="BPMNEdge_flow11">
        <omgdi:waypoint x="1740.0" y="400.0"/>
        <omgdi:waypoint x="1820.0" y="400.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow10" id="BPMNEdge_flow10">
        <omgdi:waypoint x="1590.0" y="400.0"/>
        <omgdi:waypoint x="1640.0" y="400.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="active2Suspend" id="BPMNEdge_active2Suspend">
        <omgdi:waypoint x="1440.0" y="540.0"/>
        <omgdi:waypoint x="1540.0" y="540.0"/>
        <omgdi:waypoint x="1540.0" y="430.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-A37806A7-6B7B-48A2-BB37-DAE640231144" id="BPMNEdge_sid-A37806A7-6B7B-48A2-BB37-DAE640231144">
        <omgdi:waypoint x="1725.0" y="850.4571226080794"/>
        <omgdi:waypoint x="1080.0" y="850.0"/>
        <omgdi:waypoint x="1080.0" y="571.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="suspended2Delete" id="BPMNEdge_suspended2Delete">
        <omgdi:waypoint x="1860.0" y="400.0"/>
        <omgdi:waypoint x="1990.0" y="400.0"/>
        <omgdi:waypoint x="1990.0" y="438.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="active2RequestPasswordReset" id="BPMNEdge_active2RequestPasswordReset">
        <omgdi:waypoint x="1440.0" y="540.0"/>
        <omgdi:waypoint x="1575.0" y="540.0"/>
        <omgdi:waypoint x="1575.307142857143" y="604.5"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="active2Delete" id="BPMNEdge_active2Delete">
        <omgdi:waypoint x="1440.0" y="540.0"/>
        <omgdi:waypoint x="1990.0" y="540.0"/>
        <omgdi:waypoint x="1990.0" y="498.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="720.0" y="540.0"/>
        <omgdi:waypoint x="828.286878319943" y="540.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-3E9FE01D-CC60-4A95-B356-CA0DC000FAD6" id="BPMNEdge_sid-3E9FE01D-CC60-4A95-B356-CA0DC000FAD6">
        <omgdi:waypoint x="1785.5" y="745.5"/>
        <omgdi:waypoint x="1785.5" y="810.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-7F78CE07-A7A1-467F-BB4B-40FB234AEFF7" id="BPMNEdge_sid-7F78CE07-A7A1-467F-BB4B-40FB234AEFF7">
        <omgdi:waypoint x="1575.5" y="685.5"/>
        <omgdi:waypoint x="1575.5" y="750.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-EA22026A-25F0-4ED0-AB6E-9CE9CE74623C" id="BPMNEdge_sid-EA22026A-25F0-4ED0-AB6E-9CE9CE74623C">
        <omgdi:waypoint x="1370.0" y="645.0"/>
        <omgdi:waypoint x="1080.0" y="645.0"/>
        <omgdi:waypoint x="1080.0" y="571.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="570.0" y="540.0"/>
        <omgdi:waypoint x="620.0" y="540.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="active2ConfirmPasswordReset" id="BPMNEdge_active2ConfirmPasswordReset">
        <omgdi:waypoint x="1440.0" y="540.0"/>
        <omgdi:waypoint x="1785.0" y="540.0"/>
        <omgdi:waypoint x="1785.3772727272726" y="664.5"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="928.286878319943" y="540.2478767845322"/>
        <omgdi:waypoint x="1030.0" y="540.7521232154678"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-CF9ACA40-7750-47C3-A508-7250D24D4F1F" id="BPMNEdge_sid-CF9ACA40-7750-47C3-A508-7250D24D4F1F">
        <omgdi:waypoint x="1515.0" y="790.4389505549949"/>
        <omgdi:waypoint x="1080.0" y="790.0"/>
        <omgdi:waypoint x="1080.0" y="571.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="suspended2Reactivate" id="BPMNEdge_suspended2Reactivate">
        <omgdi:waypoint x="1860.0" y="400.0"/>
        <omgdi:waypoint x="1902.0" y="400.0"/>
        <omgdi:waypoint x="1902.0" y="320.0"/>
        <omgdi:waypoint x="1940.0" y="320.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow99" id="BPMNEdge_flow99">
        <omgdi:waypoint x="2040.0" y="466.5576923076923"/>
        <omgdi:waypoint x="2080.005821071606" y="465.40367823831906"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8">
        <omgdi:waypoint x="1130.0" y="540.8529411764706"/>
        <omgdi:waypoint x="1400.058651026393" y="540.0586510263929"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="active2Update" id="BPMNEdge_active2Update">
        <omgdi:waypoint x="1420.0" y="560.0"/>
        <omgdi:waypoint x="1420.0" y="615.0"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>
```

Utilisation du namespace XML BPMN (Business Process Model Notation)

### ForgeRock
Creuser les revendications de www.timeforafork.com -> les licenses des outils ForgeRock auraient changé

### LSC

$ ./lsc-sample --run
Running "/home/paul/devel/lsc-2.1.3/sample/hsqldb/bin/./../../../bin/lsc" --config "/home/paul/devel/lsc-2.1.3/sample/hsqldb/bin/./../etc" --synchronize all --clean all
May 02 17:55:37 - INFO  - Logging configuration successfully loaded from /home/paul/devel/lsc-2.1.3/sample/hsqldb/bin/./../etc/logback.xml 
May 02 17:55:37 - INFO  - LSC configuration successfully loaded from /home/paul/devel/lsc-2.1.3/sample/hsqldb/bin/./../etc/
May 02 17:55:37 - INFO  - Connecting to LDAP server ldap://localhost:33389/dc=lsc-project,dc=org as cn=Directory Manager
May 02 17:55:37 - ERROR - Error opening the LDAP connection to the destination! (javax.naming.CommunicationException: localhost:33389 [Root exception is java.net.ConnectException: Connection refused (Connection refused)])
May 02 17:55:37 - ERROR - org.lsc.exception.LscConfigurationException: Configuration exception: javax.naming.CommunicationException: localhost:33389 [Root exception is java.net.ConnectException: Connection refused (Connection refused)]

Pourquoi d'autres commandes fonctionnent ?
Options de débug ?

Pour l'instant, HSQLDB n'est pas installé

 java -jar sqltool.jar

On peut lancer une GUI d'administration de la base par 

 java -cp ../lib/hsqldb.jar org.hsqldb.util.DatabaseManagerSwing

ou

 java -cp ../lib/hsqldb.jar org.hsqldb.util.DatabaseManager

(Swing obsolete)

type de stockage
mem, file, res

la lib ne semble pas nécessaire à l'utilisation de LSC

chercher ailleurs l'origine du 'Connection refused'

Essai avec un serveur postgres:
Etape 1) Installation PostgreSQL JDBC servcer : aller chercher le JAR et le déployer 

tcpip_socket = true in the postgresql.conf file.

C'est parti : première connexion à un annuaire LDAP :

<lsc>
  <connections>
    <ldapConnection>
      <name>ldap-dst-conn</name>
      <url>ldap://spare.entrouvert.lan:389/dc=entrouvert,dc=lan</url>
      <username>cn=admin</username>
      <password>test</password>
      <authentication>SIMPLE</authentication>
      <referral>IGNORE</referral>
      <derefAliases>NEVER</derefAliases>
      <version>VERSION_3</version>
      <pageSize>-1</pageSize>
      <factory>com.sun.jndi.ldap.LdapCtxFactory</factory>
      <tlsActivated>false</tlsActivated>
    </ldapConnection>
  </connections>
</lsc>

TODO écriture d'un JavaBean pour la prise en charge des tâches de synchro ?
SimpleBean, OrderedValuesBean ?


Qu'est-ce qui est déjà mis à disposition de l'utilisateur ?
Mécanisme de paramétrage de tâches pour la synchro

cleanHook pour l'opération de nettoyage
syncHook pour l'opération de synchro

Il faut par ailleurs définir une ou plusieurs règles de synchro

lsc>tasks>task>propertiesBasedSyncOptions
Par exemple, possibilité d'imposer des conditions à remplir pour que la sycnhronisation ait lieu :
<conditions>
<create>1 &gt; 0</create>
<update>srcBean.getDatasetFirstValueById
('updateTimeStamp') &gt; dstBean.getDatasetFirstValueById('updateTimeStamp')</update>
<delete>false</delete>
<changeId>false</changeId>
</conditions>

Fonctionnalité d'audit, au format CSV ou LDIF (? pour garder les logs dans le LDAP ?)

Fonctionnalité de chiffrement
keyfile
AES 128 par défaut

Notion intéressante d'appender pour les logs
Utilisation de logback, serveur de logs en Java                                 
                                                                                
§ Script                                                                        
langages JS ou Groovy                                                           
                                                                                
modes synchrones, asynchrones ou de nettoyage                                   
                                                                                
création d'un script initd                                                      
pidfile                                                                         
JMX (Java Management Extensions) 

Création d'une 'sandbox' sur virtualbox pour s'amuser un peu avec LSC ?
OK
Installation de LSC et de slapd
Synchro de plusieurs branches entre elles

Ok VM en place avec serveur ssh

Déploiement d'un nginx/gunicorn pour l'IDP sur la sandbox
Maintenant : déployer deux implémentations différentes de LDAP ?
L'une avec OpenLDAP et l'autre ApacheDS ?
Ecriture d'un petit script de sychro avec le daemon lsc ?

```
ldapsearch -D "cn=admin,dc=eosandbox,dc=amok" -w test -p 389 -h localhost -b "dc=eosandbox,dc=amok" -s sub "(ObjectClass=*)" * +
```

Maintenant, essayons
'Synchronize from CSV to LDAP directory'

6192 [main] ERROR org.lsc.opendj.LdapServer - org.opends.server.util.LDIFException: Entry dc=lsc-project,dc=org read from LDIF starting at line 1 is not valid because it violates the server's schema configuration:  Entry dc=lsc-project,dc=org violates the Directory Server schema configuration because it does not include a structural objectclass.  All entries must contain a structural objectclass

Voir si le serveur LDAP tourne en dépit de l'erreur ?
Sinon aller chercher dans le script le LDIF et corriger l'erreur ?

Ok on relance le serveur, et on essaie avec les credentials:
    URL: ldap://localhost:33389/
    Base DN: dc=lsc-project,dc=org
    Bind DN: cn=Directory Manager
    Password: secret

$ bin/lsc-sample --show
ID  UID            ENDOFVALIDITY  SN           CN                            GIVENNAME        MAIL                         O          ADDRESS                                  TELEPHONENUMBER       CARLICENSE
--  -------------  -------------  -----------  ----------------------------  ---------------  ---------------------------  ---------  ---------------------------------------  --------------------  -------------
1   j.clarke       31/12/2015     Clarke       Clarke, Jonathan              Jonathan         jonathan@philipoux.net       Normation  [null]                                   +33 (0)1 83 62 26 96  BHU772|DED899
2   r.schermesser  31/12/2015     Schermesser  Schermesser, Remy-Christophe  Remy-Christophe  remy@schermesser.com         Octo       [null]                                   [null]                [null]
3   t.chemineau    31/12/2015     Chemineau    Chemineau, Thomas             Thomas           thomas@aepik.net             AFNOR      [null]                                   [null]                [null]
4   s.bahloul      31/12/2015     Bahloul      Bahloul, Sebastien            Sebastien        sebastien.bahloul@gmail.com  Dictao     156 av. de Malakof, 75116 PARIS, France  [null]                [null]
5   c.oudot        31/12/2015     Oudot        Oudot, Clement                Clement          clem.oudot@gmail.com         Linagora   [null]                                   33(0)810251251        [null]
6   r.ouazana      31/12/2015     Ouazana      Ouazana, Raphael              Raphael          rouazana@linagora.com        Linagora   [null]                                   33(0)810251251        [null]
7   d.coutadeur    31/12/2015     Coutadeur    Coutadeur, David              David            dcoutadeur@linagora.com      Linagora   [null]                                   33(0)810251251        [null]
8   e.pereira      31/12/2015     Pereira      Pereira, Esteban              Esteban          epereira@linagora.com        Linagora   [null]                                   33(0)810251251        [null]

Fetched 8 rows.


Mais l'appli plante
l'entrée
`tcp6       0      0 :::33389                :::*                    LISTEN      17557/java`
termine bel et bien


Ok: le LDIF doit être modifié
On ajoute 'objectClass: organization'  dans la définition de l'entrée racine (fichier test.ldif)

Ok ça tourne maintenant
Reessayons


L'une des particularités de LDAP : la configuration est auto-contenue
OpenDJ semble aussi stocker dans l'annuaire les règles à appliquer sur les
requêtes :
1155 dn: cn=Directory String First Component Equality Matching Rule,cn=Matching Rules,cn=config
1156 objectClass: top
1157 objectClass: ds-cfg-matching-rule
1158 objectClass: ds-cfg-equality-matching-rule
1159 cn: Directory String First Component Equality Matching Rule
1160 ds-cfg-java-class: org.opends.server.schema.DirectoryStringFirstComponentEqualityMatchingRuleFactory
1161 ds-cfg-enabled: true


```
  $ ldapsearch -D "cn=Directory Manager" -w secret -p 33389 -h localhost -b "dc=lsc-projet,dc=org" -s sub "(ObjectClass=*)"
  # extended LDIF
  #
  # LDAPv3
  # base <dc=lsc-projet,dc=org> with scope subtree
  # filter: (ObjectClass=*)
  # requesting: ALL
  #

  # search result
  search: 2
  result: 32 No such object
  text: The entry dc=lsc-projet,dc=org specified as the search base does not exis
   t in the Directory Server

   # numResponses: 1
```

lsc-sample --run pour lancer la synchro

OK
l'exemple de test fonctionne en trois étapes :
Première phase : $ bin/lsc-sample --import sample.csv
la création d'une base de données relationnelle contenant une
table remplie à partir d'un fichier csv.
Le SGBDR utilisé ici est HSQLDB
Le fichier csv présente une première ligne permettant le nommage des attributs
dans la base.

Seconde phase : $ bin/lsc-sample --start-ldap-server
Lancement du server LDAPv3 (implémentation OpenDJ)
Le serveur ne contient pour l'instant aucune données

Troisième phase : $ bin/lsc-sample --run
Réconciliation des données entre la base de données relationnelle et l'annuaire
LDAP.
Pour comprendre comment est effectuée cette synchronisation, il faut étudier
le fichier de synchronisation fourni avec l'exemple.
Il s'agit du fichier lsc.xml
La configuration commence par une liste des différentes connexions que LSC doit
prendre en charge.
Ici, logiquement, seule deux connexions sont assurées par LSC:

```
    <ldapConnection>
      <name>dst-ldap</name>
      <url>ldap://localhost:33389/dc=lsc-project,dc=org</url>
      <username>cn=Directory Manager</username>
      <password>secret</password>
      <authentication>SIMPLE</authentication>
      <referral>IGNORE</referral>
      <derefAliases>NEVER</derefAliases>
      <version>VERSION_3</version>
      <pageSize>-1</pageSize>
      <factory>com.sun.jndi.ldap.LdapCtxFactory</factory>
      <tlsActivated>false</tlsActivated>
      <saslMutualAuthentication>false</saslMutualAuthentication>
    </ldapConnection>
```
On constate qu'il s'agit des principaux paramètres à fournir lors d'une tentative de connexion à un serveur LDAPv3.
Pas d'authentification mutuelle, pas de chiffrement TLS.
L'option factory pour la déclaration du module de gestion des contextes de connexion.
Pas de déréférencement des alias.
Protocole, hôte, port et DN de base précisés en une seule ligne:
ldap://localhost:33389/dc=lsc-project,dc=org

Côté base de données, une configuration plus succincte permet l'établissement de la connexion.
    <databaseConnection>
      <name>src-jdbc</name>
      <url>jdbc:hsqldb:file:/tmp/lsc/hsqldb/lsc</url>
      <username>sa</username>
      <password></password>
      <driver>org.hsqldb.jdbcDriver</driver>
    </databaseConnection>
Pas de mot de passe requis

La partie suivante est plus intéressante, il s'agit des règles de synchronisation.
LSC peut prendre en charge plusieurs tâches de synchronisation à la fois, bien qu'une seule tâche est définie dans cet exemple.
Une fois la tâche nommée (MySyncTask), le chemin vers la classe Java en charge de l'opération de synchronisation est déclaré.
TODO: explication du code source du JavaBean
S'en suivent la déclaration des différentes méthodes pour la partie 'base source' de la synchronisation.

Côté cible, une fois déclaré le DN d'approvisionnement des entrées de la base, on peut définir la liste des attributs synchronisés.
Parmis eux doivent se trouver un ou plusieur attributs pivots, c'est-à-dire, un ou des attributs qui vont servir à LSC pour effectuer la correspondance entre les entrées de chacun de référentiels synchronisés.
L'ensemble de ces attributs pivot sert ainsi d'identifiant pour retrouver une même entité dans plusieurs référentiels.
//Pb: LSC permet-il des mécanismes de détection et de propagation des modifications de ces attributs pivots.
Jusque là il semble être clair que c'est l'unicité des attributs pivots à travers les différentes bases qui permet de détecter et propager les modifications sur les attributs secondaires, non pivots.

propertiesBasedSyncOptions définit la façon dont sera créé le DN d'une entrée LDAP à partir de l'adresse email dans la base de données relationnelle.

//CURRENT3

## Explications techniques Mik

Se concentrer sur l'étude de synchro et d'approvisionnement en tant que telle
La partie de mapping entre un événement déclenché et l'action WCS à effectuer mérite réflexions.

Dans quelle mesure le connecteur pourra être générique ?
Les mêmes règles ne s'appliqueront-elles pas à tous les connecteurs ?

Le connecteur est en fait utilisé en écriture seulement : il sert à des fins d'approvisionnement et de synchronisation

Pour la lecture, un simple script ou outil de polling sera utilisé

Une des problématiques centrales à cette étude est la façon d'éviter les boucles/effets de bords provoqués par les changements en cascade sur les différents référentiels. Une modification effectuée par le système d'approvisionnement et de synchro ne doit pas être confondue avec une modification externe, nécessitant une éventuelle synchro avec d'autre référentiels

Plusieurs solutions s'offrent à nous pour remédier à ce problème :
- garder une historique des actions (timestamp/horodatage) dans WCS pour ne pas les faire remonter lors du polling
    Cette solution nécessite une synchronisation de l'horloge des différentes briques logicielles de l'infra
- utiliser un marqueur/flag du type `performedByWCS` pour chacunes des actions appliquées à un référentiel
    Le système de polling ne concernera que les actions ne portant pas le marqueur

=> TODO aller chercher un ou plusieurs scenario de processus en entreprise justifiant un outil de synchro
Dans une deuxième phase, il faudra étudier ce qui serait déjà possible avec les outils EO tels quel

Recherche de scenarii de recours à la synchronisation :
- documentation Alfresco
- PapercutNG
- LSC
Pb : pas assez générique, pas d'expression des besoins de synchro

Lecture RFC4533 : LDAP Content Synchronization Operation
Considérations justifiant une spéc du mécanisme de synchro avec LDAP :
- pbatique de la convergence : comment s'assurer que les référentiels vont converger vers un état stable, sans données incohérentes
    Différents problèmes se posent avec les solutions déjà existantes :
    * le niveau de convergence souhaité ne peut être atteint
    * l'impossibilité d'une convergence reste indétectable
    * soit les hypothèses préalables à la synchro sont trop contraignantes
    * nécessite le stockage d'un historique des changements côté serveur
    * est à l'origine d'une quantité excessive d'informations échangées entre client et serveur

LDAP Sync simplement basé sur l'état actuel des serveurs, pas besoin d'un historique pour la reconstitution des modifications sur un des référentiels.

Cette RFC préconise aussi une optimisation en termes de quantité d'information nécessaire pour mener à bien la synchronisation.

Deux modes pour la détection des changements :
- le *polling mode* : caractère ponctuel, granulaire de l'écoute
    Le serveur fournit un champ Sync State Control
- le mode *listening for changes* (refreshAndPersist)

Operation selon un mode client-serveur ?
TODO explication archi
+ explication des deux modes de connexion

**Usages possibles :**
- application de type 'pages blanches' qui souhaitent synchronisation une sous-partie (un DIT partiel, pour *Directory Information Tree*) d'un annuaire plus global.
- Moteur de méta-données récoltées par opérations de synchro sur des référentiels
- Service de proxy-cache nécessitant la synchronisation pour construction du cache
- Simple process esclave/maître pour la réplication cachée d'un DIT partiel (*DIT fragment*). Alternative à X.500 DISP (Directory 

### Elements entrant en jeu pour l'opération de synchronisation
- syncUUID (codé sur 128 bits)
- syncCookie
- Sync Request Control
- Sync State Control
- Sync Done Control
- Sync Information Message
- Sync Result Code
TODO explication fonctionnelle ou technique ? En quoi cela peut-il aider à la phase 2 de l'étude ?

### Synchronisation des contenus
TODO
Déclenchement de l'opération de synchronisation:
Le serveur reçoit un message de type SearchRequest contenant un Search Request Control (cf plus haut TODO)

La détection d'une session déjà existante se fait par vérification de la présence d'un cookie dans la SearchRequest.

==========

//EN COURS
TODO
Plan
Schémas
Mise au propre
Puis retour au travail technique

Découper en deux
Description de l'existant
Décrire fonctionnellement ce que l'on souhaite faire au niveau de la synchro Schémas du même type que ce qu'on a fait avec le campus condorcet (Diagramme de séquence, de flux : (RH, AD, téléphonie, méta-annuaire, outil de synchro, agent => 6 lignes)
Puis on parlera des pbatiques techniques

=> Découpage en trois


Plan : partie 3 : "cas d'usages " - pbatique du stage, répondre à un besoin"""
Commencer par une partie cas d'usage => aspect fonctionnel
Etat de l'art : étude des différents techno
    Préliminaires de la GI
    = synthèse d'une page maxi sur les différents aspects, renvoyer vers les annexes pour les études détaillées

Partie 4 ajoutée Etat de l'art de la GI
Etat de l'art sur la snchro et l'approvisionnement

Partie 5  Prise en main des technos
DJango
wcs passerelle
les API rest

Partie 6 : Mise en oeuvre et realisations
Chap 1 démonstrateur avec LSC
Chap 2 développement spécifique cas campus condorcet, gestion des comptes invités
chap 3 analyse de la couverture du besoin par les outils entr'ouvert
    architecture cible
    développements nécessaires

Synthèse technique (deux pages)

Conclusion (plus sur l'aspect stage)

Annnexes
acronymes
bibliographie (titre, source, date, auteurs)
extrait de code source (il faut que ce soit pertinent, ou URLs vers un dépot public)
diagramme et schémas ? Non dans les corps de chap

### Exemple "haut-niveau"
//TODO doc commerciale des concurrents
Cas d'usage pour la doc fonctionnelle
A titre comparatif, la doc openidm est pas assez 'haut niveau'

Retour documentation midPoint :
page 26: exemple complet IDM avec cas de synchro sur plusieurs bases d'identités
creation
modif avec répercussions dans les différentes bases
suppression
changement des droits

L'exemple, bien que non retenu pour l'étude expérimentale, présente certains point intéressants, méritants d'être détaillés ici :

**Phase 1 :**
La procédure de synchro est effectuée par ajout d'une entrée dans le référentiel des ressources humaines. Cet ajout résulte de l'embauche d'une nouvelle employée.
Le système est en charge de la génération d'attributs pour d'autres référentiels : la création d'un identifiant unique global est effectuée à partir du nom d'utilisateur local à la base RH.
A la création de l'identifiant est associé l'ajout d'une entrée dans la base du gestionnaire d'identités (IDM).
A ensuite lieu la récupération de l'identifiant organisationnel tel que renseigné lors de la création de l'entrée RH. Le gestionnaire d'identité peut ensuite déduire du service dans lequel se trouve la nouvelle employée.
Il est aussi en charge de l'association des privilèges/droits associés au rôle du service auquel appartient maintenant l'employée. Ces rôles sont déduits du service qu'a rejoint l'employée. L'annuaire du service est un Active Directory (AD).
L'ajout du compte est aussi accompagné de la création d'une boîte mail pour l'employée.

//Notion de groupes et de listes à étudier -> pour les mails seulement ?
Finalement, la procédure est complétée par la création d'une entrée dans le CRM, en accord avec la politiques de permissions associées au rôle obtenu par l'employée.

**Phase 2 :**
Dans cette phase, il est précisé que le gestionnaire d'identités peut effectuer une quelconque modification de privilèges indépendamment des rôles auxquels appartient un compte. Cette modification des privilèges résulte en la création d'un statut spécial, ne pouvant plus être défini comme la somme des rôles auxquels l'employée appartient.
On retrouve une fois de plus la notion de workflow pour le mécanisme de gestion des identités : la montée en privilège n'est effectuée qu'une fois les approbations du manager et du responsable technique obtenues.
De façon plus générale, l'aboutissement d'une demande de modifications de droits d'accèss nécessite l'action d'un opérateur (humain). Il s'agira ici d'un supérieur hiérarchique, lequel pourra accepter ou refuser la demande.

**Phase 3 :**
Nous partons ici du cas d'un changement de nom de l'employée. Ce changement a des répercussions, de façon automatique et en cascade, sur les différents référentiels locaux à chaque service ou à chaque application.
Par exemple, un changement de nom résulte logiquement en la création d'un nouvel alias d'adresse email. Cette création sera accompagnée de l'envoi d'un message de notification à l'employée concernant l'ajout de l'alias.

**Phase 4 :**
L'expiration du mot de passe du compte utilisateur est aussi un mécanisme pouvant être pris en charge par le gestionnaire d'identité. Après choix d'un nouveau mot de passe par l'employée, le gestionnaire d'identité est en charge d'appliquer les modifications dans toutes les bases.
Nous remarquons toutefois qu'à aucun moment on parle de SSO dans le cadre de cet exemple.

**Phase 5 :**
Nous pouvons maintenant nous intéresser à des cas particuliers survenant dans le domaine de la gestion des identités numériques. Les besoins en sécurité dans le système d'informations requièrent la détection d'incohérences dans l'attribution des droits. Cette détection, pour l'exemple donné dans le document //REF// est mise en place à la suite de soupçons concernant un vol de données.
Entre alors en compte la notion de traçabilité, en tant que possibilité d'identifier l'employé ayant attribué des droits excessifs à son subordonné à l'origine du vol de données.
Le système de gestion des permissions doit ainsi être en mesure de répondre aux question suivantes :
* Qui a attribué les droits ?
* A quelle date ces droits ont-ils été données ?
* Quelles actions ont été effectuées ensuite ?

La désactivation des droits est ensuite effectuée à l'aide du gestionnaire d'identités de façon centralisée, et est répercutée sur l'ensemble des référentiels d'identités.

**Phase 6 :**
Nous passons maintenant au cas de la suppression de l'un des deux comptes, et de la diminution des droits associés à l'autre compte. Encore une fois des contraintes de synchronisation sont nécessaires pour assurer la cohérence des référentiels d'identités entre eux. Il ne faut pas, par exemple, que le rétablissement d'une sauvegarde, sur un référentiel local, et antérieure à la modification, ait pour conséquence d'annuler la suppression du compte banni.

**Phase 7 :**
Il convient maintenant de décrire la procédure de mise en place d'une politique de sécurité, et ceci concernant en particulier la détection des droits incohérents, des comptes obsolètes ou n'ayant pas de propriétaire clairement identifié. Les besoins de sécurité impliquent la mise d'un processus de détection _périodique_ de ces incohérences.
//TODO essayer d'obtenir une estimation du temps nécessaire pour une telle détection, avec des paramètres standard ?

**Phase 8 :**
Ce mécanisme de sécurité aboutit à la création d'une politique de recertification des droits de employés par chacun de leurs managers. Pour chaque employé, les droits activés jusqu'à présent doivent être validés à nouveau par un des responsables de l'employé (habilité à octroyer les droits en question).

**Conclusion :**
Cet exemple met en scène différents concepts et processus pertinents dans le cadre de l'étude réalisée pour ce stage, notamment l'utilisation de *workflows*, et la synchronisation et l'approvisionnement à divers étapes du cycle de vie des données d'identités
L'étape suivante consistera à identifier les contraintes techniques nécessaire au déploiement de telles fonctionnalités de gestion des identités, et de déterminer les éventuelles modifications nécessaires aux outils EO pour la mise en place d'un tel scenario.

### Détails techniques GI
Un des concepts récurrents dans la gestion des identités est le RBAC (pour *Role-Based Access Control*). Ce terme est né des mécanismes souhaitant remédier aux inconvénients d'un contrôle d'accès par permissions simples.
Dans un modèle régi par une politique de sécurité de type RBAC, on n'attribue pas des permissions aux utilisateurs, seulement des rôles.
Une sécurisation des accès par RBAC présente deux principaux avantages :
- il est nettement plus simple de manipuler des privilèges en termes de *rôles*, cette granularité reflète naturellement la séparation des responsabilités au sein d'une organisation (au sens où les permissions sont rarement données individuellement, mais sont plutôt associées au poste occupé par chaque individu).
- ce modèle RBAC permet la factorisation des permissions à travers les rôles : c'est plus maintenable qu'un éclatement des permissions sur chaque utilisateur.

Nous précisons toutefois que des alternatives au modèle RBAC existent, l'une d'elle étant ABAC (*Attribute-Based Access Control*).
Ce mécanisme manipule un ensemble d'attributs permettant de générer à la volée les permissions ?
Les auteurs du document //REF// affirment toutefois qu'un contrôle de type ABAC n'est pas adapté dans le cadre d'un SI comportant un gestionnaire d'identités.

# Planification du stage
La planification du stage est réalisée à l'aide d'un ensemble de diagrammes de Gantt :
Ils sont au total au nombre de quatre, c'est-à-dire les diagrammes :
- prévisionnel, _i.e._ réalisé au début du stage
- réel. C'est celui qui est maintenu à jour tout au long du projet
- prévisionnel mi-parcours, réalisé à la moitié de la durée du projet
- final. C'est planning réel une fois le projet réalisé.

# Ecriture d'un connecteur Passerelle
La création d'un nouveau connecteur doit d'abord être précédée d'une phase de lecture du code de Passerelle, afin de comprendre la structuration des connecteurs fournis par l'application.

Choix d'un connecteur  à dériver pour l'écriture du connecteur LDAP

Update :
L'outil Passerelle est pensé pour faciliter le développement de nouveaux connecteurs.
Seule l'écriture du fichier models.py est nécessaire. Il suffit de préciser les *endpoints* mis à disposition par le connecteur, et passerele se charge du code 'logique' (non métier) du connecteur.

# Mise au point technique
## Python
### Packing et unpacking
### Coding style
* keywork `linebreaks`
* `cleaned_data` pour les formulaires
* surcharge de `__unicode__` et `__str__` pour la description textuelle des données et objets Django.

"A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is the most important." (PEP8)

### Lecture du code source Authentic

### Sessions et pseudo-terminaux
#### tmux
Pb: bugs
#### screen
Possibilité de détacher les sessions
Plus puissant que tmux mais plus difficile d'utilisation

## Virtualisation
### Avantages
TODO Securité, portabilité, 'scalabilité' etc
### OpenVZ
Techno utilisée pour la virtualisation des serveurs sur la plateforme de développement (hébergée chez OVH)
OpenVZ implémenté sa propre version du noyau (on parle de noyau 'patché'), lui-même basé sur le noyau RHEL
Notion de VPS (*Virtual Private Server*) ou de VE (*Virtual Environment*)
Identification des conteneurs par attribution d'un VEID
VE IDentifier

Notion de "conteneurisation" : hôte et invités partagent un même noyau

Gestion des conteneurs par CLI (*Command-Line Interface*)
Utilitaire `vzctl {start, create, stop, exec, enter, set} <params>`

Bâti sur LXC ? cgroups ?

Création d'un conteneur suivant la technique de template cache :
Il s'agit d'un patron de système d'exploitation, nécessaire au déploiemend'un nouveau conteneur

TODO
OS template metadata

TODO en quoi cela diffère-t-il des 'recipes' Docker et Rocket ?

### Packaging
Cette section couvrira la méthode de packaging utilisée chez EO: création de paquets Debian pour les projets Django développés par l'équipe
l'outil utilisé pour la création de ces paquets .deb (cf deb.entrouvert.org) est dpkg-deb
Il a recours à l'utilitaire fakeroot (pourquoi ?)

Pour chaque projet, les scripts et metadonnées nécessaires à la création du paquet sont situées dans le fichier /debian

Les sources des projets EO fournissent aussi des exemples de fichier de configuration pour les services associées (Apache, nginx, gunicorn, systemd, postgresql) //TODO a verifier

#### Structure d'un paquet Debian
Le fichier *control* rassemble les méta-données du paquet

Scripts preinst, poistinst, prerm, postrm

commande dpkg-deb --build


## Linux screen
Abstraction des shells en session ssh, avec possibilité de séparer la fenêtre active en plusieurs shell, de détacher les screen de la session, afin de pouvoir les récupérer lors de la prochaine connexion.

## Connecteurs
### ConnId
Javadoc at http://connid.tirasa.net/apidocs/1.4/index.html
TODIG ?

## Puppet
Il s'agit d'un outil de gestion de config logicielle, utilisé pour l'automatisation du déploiement des serveurs.
Reprend les principes du contrôle de version du code pour l'adapter à la config serveur
Voir comment EO l'utilise pour le déploiement de ses serveurs de dev et de prod

## GnuPG (Privacy Guard)

Implémentation GNU de PGP (Pretty Good Privacy)

Lecture de la RFC : points remarquables :
```
compression has the added side effect that some types of
   attacks can be thwarted by the fact that slightly altered, compressed
      data rarely uncompresses without severe errors.  This is hardly
         rigorous, but it is operationally useful.
```

Radix-64 ~ ASCII Armor

Le chiffrement mais surtout la signature : fonctionnalité clé de GnuPG

Fonctionnalités de conversion S2K
(String to Key)

TODO Paramétrer la signature des messages avec GnuPG
La notion de fiabilité de la clé est définie par le nombre de certifications par des tiers.



## Config client VoIP
TODO

# Sources
https://shibboleth.net/
## Lues
## A lire
https://docs.djangoproject.com/fr/1.10/topics/settings/
http://support.novell.com/techcenter/articles/ana20011101.html
http://www.journaldunet.com/developpeur/xml/analyse/la-federation-d-identite-au-travers-de-saml.shtml
https://www.ietf.org/rfc/rfc4512.txt
https://docs.djangoproject.com/en/1.10/howto/custom-template-tags/#thread-safety-considerations
http://syncope.apache.org/docs/reference-guide.html
https://en.wikipedia.org/wiki/ALFA_%28XACML%29
https://en.wikipedia.org/wiki/List_of_LDAP_software

# Annexes
## Assertion SAML d'authentification (SAMLResponse IdP -> SP)
`<?xml version="1.0"?>`
`<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_CD4CC621A02FDE6054649B69E6DBCC41" InResponseTo="_A79D6A8E4A93A92D65EE570ECA571BDB" Version="2.0" IssueInstant="2017-02-15T14:14:04Z" Destination="http://localhost:8008/accounts/mellon/login/" Consent="urn:oasis:names:tc:SAML:2.0:consent:prior">`
`  <saml:Issuer>http://localhost:8000/idp/saml2/metadata</saml:Issuer>`
`  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">\n<SignedInfo>\n<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>\n<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>\n<Reference URI="#_CD4CC621A02FDE6054649B69E6DBCC41">\n<Transforms>\n<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>\n<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>\n</Transforms>\n<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>\n<DigestValue>ozqrnvN5HCJ8jxnPK8oT8U/+sHk=</DigestValue>\n</Reference>\n</SignedInfo>\n<SignatureValue>Ccne2rcykuXTgGW8QSNCANcliOvvl854uyiwPXGTGYbKfDNrFq87HNmDDizLslDY\nZVETw6TdVRi6vKlHebW0uWO9F4XRy7o4qEGiIEhLLGe1MIz+PWKqd1G1FcoQcEVG\nojZioS1BfWxcySZrU/OXftIsW929wYeXV+FsHauihGolAp5tnf+e0es5Gk2kuawy\nLBSEoWtriXRVMpEDfl1BHM61Y1uzkpmeA221e07WLDLr/KuAf4PS0px7l8YUEDz4\nA87GHekQ8ar2OiS+9unJ9DhZ+qFzLeddb6IdxHqUfWkdmAdajGffAyvfefEcm5wp\njE2b6cQPv0xh530CEFRXUw==</SignatureValue>\n<KeyInfo>\n<X509Data>\n<X509Certificate>MIIDIzCCAgugAwIBAgIJANUBoick1pDpMA0GCSqGSIb3DQEBBQUAMBUxEzARBgNV\nBAoTCkVudHJvdXZlcnQwHhcNMTAxMjE0MTUzMzAyWhcNMTEwMTEzMTUzMzAyWjAV\nMRMwEQYDVQQKEwpFbnRyb3V2ZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEAvxFkfPdndlGgQPDZgFGXbrNAc/79PULZBuNdWFHDD9P5hNhZn9Kqm4Cp\n06Pe/A6u+g5wLnYvbZQcFCgfQAEzziJtb3J55OOlB7iMEI/T2AX2WzrUH8QT8NGh\nABONKU2Gg4XiyeXNhH5R7zdHlUwcWq3ZwNbtbY0TVc+n665EbrfV/59xihSqsoFr\nkmBLH0CoepUXtAzA7WDYn8AzusIuMx3n8844pJwgxhTB7Gjuboptlz9Hri8JRdXi\nVT9OS9Wt69ubcNoM6zuKASmtm48UuGnhj8v6XwvbjKZrL9kA+xf8ziazZfvvw/VG\nTm+IVFYB7d1x457jY5zjjXJvNysoowIDAQABo3YwdDAdBgNVHQ4EFgQUeF8ePnu0\nfcAK50iBQDgAhHkOu8kwRQYDVR0jBD4wPIAUeF8ePnu0fcAK50iBQDgAhHkOu8mh\nGaQXMBUxEzARBgNVBAoTCkVudHJvdXZlcnSCCQDVAaInJNaQ6TAMBgNVHRMEBTAD\nAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAy8l3GhUtpPHx0FxzbRHVaaUSgMwYKGPhE\nIdGhqekKUJIx8et4xpEMFBl5XQjBNq/mp5vO3SPb2h2PVSks7xWnG3cvEkqJSOeo\nfEEhkqnM45b2MH1S5uxp4i8UilPG6kmQiXU2rEUBdRk9xnRWos7epVivTSIv1Ncp\nlG6l41SXp6YgIb2ToT+rOKdIGIQuGDlzeR88fDxWEU0vEujZv/v1PE1YOV0xKjTT\nJumlBc6IViKhJeo1wiBBrVRIIkKKevHKQzteK8pWm9CYWculxT26TZ4VWzGbo06j\no2zbumirrLLqnt1gmBDvDvlOwC/zAAyL4chbz66eQHTiIYZZvYgy</X509Certificate>\n</X509Data>\n</KeyInfo>\n</Signature>`
`  <samlp:Status>`
`    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>`
`  </samlp:Status>`
`  <saml:Assertion Version="2.0" ID="_A86605DF1F9D51A8BD5EFB5F9B02A712" IssueInstant="2017-02-15T14:14:10Z">`
`    <saml:Issuer>http://localhost:8000/idp/saml2/metadata</saml:Issuer>`
`    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">\n<SignedInfo>\n<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>\n<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>\n<Reference URI="#_A86605DF1F9D51A8BD5EFB5F9B02A712">\n<Transforms>\n<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>\n<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>\n</Transforms>\n<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>\n<DigestValue>F9UyV7kkrdL45HhZmIG+qR8hqE8=</DigestValue>\n</Reference>\n</SignedInfo>\n<SignatureValue>cmFamTX6vzK0evsehNv8U7Mjz1JXdz60ZGpzEuner+xEdB8I4rISzWZBLmLQYMiU\n8IlCZYvOeaeHIwf/xStowUZy+dfjzYKMmN5OD9z9ifD5Kr6rNNS9a0Tsmu55HUvv\nD83CkGS2c8HsdpWTR3Og7ED2lVT6rsXTx+VgTJ1mzl0ONVKPSnTp8x09VgHbMFXh\nLq5Pg+5im+G0jJIcpVN3VesVzLdfP6w3CjJz5f+aMllfvRdYYSad0vyXlLUx59Al\nAsIeqFTFq3uKsVHH4yd/JZoFwrhFE4Q6Ve9UeTRw9qR+y6M+fd/cJTcvFzHjKo2A\nTEt9QYM4RpfPPy4conjGqg==</SignatureValue>\n<KeyInfo>\n<X509Data>\n<X509Certificate>MIIDIzCCAgugAwIBAgIJANUBoick1pDpMA0GCSqGSIb3DQEBBQUAMBUxEzARBgNV\nBAoTCkVudHJvdXZlcnQwHhcNMTAxMjE0MTUzMzAyWhcNMTEwMTEzMTUzMzAyWjAV\nMRMwEQYDVQQKEwpFbnRyb3V2ZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEAvxFkfPdndlGgQPDZgFGXbrNAc/79PULZBuNdWFHDD9P5hNhZn9Kqm4Cp\n06Pe/A6u+g5wLnYvbZQcFCgfQAEzziJtb3J55OOlB7iMEI/T2AX2WzrUH8QT8NGh\nABONKU2Gg4XiyeXNhH5R7zdHlUwcWq3ZwNbtbY0TVc+n665EbrfV/59xihSqsoFr\nkmBLH0CoepUXtAzA7WDYn8AzusIuMx3n8844pJwgxhTB7Gjuboptlz9Hri8JRdXi\nVT9OS9Wt69ubcNoM6zuKASmtm48UuGnhj8v6XwvbjKZrL9kA+xf8ziazZfvvw/VG\nTm+IVFYB7d1x457jY5zjjXJvNysoowIDAQABo3YwdDAdBgNVHQ4EFgQUeF8ePnu0\nfcAK50iBQDgAhHkOu8kwRQYDVR0jBD4wPIAUeF8ePnu0fcAK50iBQDgAhHkOu8mh\nGaQXMBUxEzARBgNVBAoTCkVudHJvdXZlcnSCCQDVAaInJNaQ6TAMBgNVHRMEBTAD\nAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAy8l3GhUtpPHx0FxzbRHVaaUSgMwYKGPhE\nIdGhqekKUJIx8et4xpEMFBl5XQjBNq/mp5vO3SPb2h2PVSks7xWnG3cvEkqJSOeo\nfEEhkqnM45b2MH1S5uxp4i8UilPG6kmQiXU2rEUBdRk9xnRWos7epVivTSIv1Ncp\nlG6l41SXp6YgIb2ToT+rOKdIGIQuGDlzeR88fDxWEU0vEujZv/v1PE1YOV0xKjTT\nJumlBc6IViKhJeo1wiBBrVRIIkKKevHKQzteK8pWm9CYWculxT26TZ4VWzGbo06j\no2zbumirrLLqnt1gmBDvDvlOwC/zAAyL4chbz66eQHTiIYZZvYgy</X509Certificate>\n</X509Data>\n</KeyInfo>\n</Signature>`
`    <saml:Subject>`
`      <saml:NameID NameQualifier="http://localhost:8000/idp/saml2/metadata">_1E83322C6A38FF13CF515D299647E04E</saml:NameID>`
`      <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">`
`        <saml:SubjectConfirmationData NotOnOrAfter="2017-02-15T14:15:10.325204Z" Recipient="http://localhost:8008/accounts/mellon/login/" InResponseTo="_A79D6A8E4A93A92D65EE570ECA571BDB"/>`
`      </saml:SubjectConfirmation>`
`    </saml:Subject>`
`    <saml:Conditions NotBefore="2017-02-15T14:13:10.325204Z" NotOnOrAfter="2017-02-15T14:15:10.325204Z">`
`      <saml:AudienceRestriction>`
`        <saml:Audience>http://localhost:8008/accounts/mellon/metadata/</saml:Audience>`
`      </saml:AudienceRestriction>`
`    </saml:Conditions>`
`    <saml:AuthnStatement AuthnInstant="2017-02-15T14:14:10.325204Z" SessionIndex="_A86605DF1F9D51A8BD5EFB5F9B02A712" SessionNotOnOrAfter="2017-03-01T14:14:10Z">`
`      <saml:AuthnContext>`
`        <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>`
`      </saml:AuthnContext>`
`    </saml:AuthnStatement>`
`    <saml:AttributeStatement>`
`      <saml:Attribute Name="id_test" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" FriendlyName="Test ID">`
`        <saml:AttributeValue>2</saml:AttributeValue>`
`      </saml:Attribute>`
`      <saml:Attribute Name="lname_test" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" FriendlyName="Test Last Name">`
`        <saml:AttributeValue>Marillonnet</saml:AttributeValue>`
`      </saml:Attribute>`
`      <saml:Attribute Name="fname_test" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" FriendlyName="Test First Name">`
`        <saml:AttributeValue>Paul</saml:AttributeValue>`
`      </saml:Attribute>`
`    </saml:AttributeStatement>`
`  </saml:Assertion>`
`</samlp:Response>`

## Diagramme de séquence conceptuel du POC
TODO
## Diagramme de séquence technique du POC
TODO
## Architecture fonctionnelle du POC
TODO
## Architecture logicielle du POC
TODO

# Prises de notes vidéos
## Workshop: Identity standards OIDC
### OAuth
TODO
### OpenID Connect
Notion de Authentication Context
4 degrés de sécurité ?
acr : notion de risque, de seuil de risque de acceptable
amr : TODO

Réauthentification forcée
Durée de la vie la session pour une machine

Lier seuil de risque et activité liée à l'authentification (1:14)
Même contraintes et problèmes qu'avec SAML2

Requête d'autorisation dynamique (1:15)


JSON Web Tokens (RFC7519) -> Introduit la notion de confirmation (utilisateur ?)
JWS RFC7515 JSON Web Signature
JWE RFC7516 JSON Web Encryption

NAPPS
PKCE ('pixy')
AC/DC

Archi en 3 parties:
Client
Resource server
Authorization Server

Pour le scenario de livraison d'un jeton (token)

Cf Google Access Tokens
Nécessite de PKCE pour remédier à ce type d'attaque dans les applis natives frauduleuses

IOS9: Mutual authentication ?
Access aux cookies partagés dans le navigateur pour le SSO
Sans agent natif de jetons (Native tokan agent)

ACDC est un type de JSON Web Token //TODIG
code verifier (hash)
code challenge (salt ?) Génération d'un nombre aléatoire

RT & AT
Access Token
Replay Token ? Resource Token ? Revocation token ?
TLS Token Auth
{SaaS, Enterprise} Authentication Server

concepts clé :
- NAPPS
Token agent directement sur le terminal client
- PKCE
- Authorization Cross Domain Code (ACDC)

### SAML
1.0
1.1
2.0 -> 2005

XML
SAOP
XML Signature

techno pre OIC et OAuth

les spécifications de la RFC sont ambigües
variations lors de l'implémentation

Quelles implémentations ?
8 documents différents forment la totalité de l'implémentation
Fonctionnalités optionnelles peuvent être imposées par les bindings SAML

SAOP, XML : mastodontes

Problèmes sémantiques de XML : pas interopérable

binding le plus populaire: SAML Redirect & POST
Facile à implémenter, et à déployer
Chiffrement des messages SAML optionnel seulement

SAML Artifact
en deux étapes
envoi d'une référence sur un premier canal
le message passe sur un second canal
plus difficile à déployer
apparence de la sécurité seulement ?
authentification du second canal devient nécessaire
déplacement du problème ?

SSO:
initié par l'IDP ou par le SP

Si initié par l'IDP
URL, visitée par le SP, qui génère une assertion SAML

initié par le SP
Pb de la découverte du fournisseur d'identité

Signed Authent request
pbatique de l'échange des certificats, sans retarder le sso

DoS potentiel vers le fournisseur de service
2 sessions : d'application
+ une session pour l'IDP

Profils
d'implémentation
de déploiement (contenu dans le profil précédent)

SAML2Int

Notion avancées :
Découverte de fournisseur d'identité
laisser le choix parmi une liste d'IDP à l'utilisateur

NASCAR Screen

wayf-less URLs

ECP
Enhanced Client or Proxy

SLO plus difficile que SSO
Comment garantir le SLO ? l'ACK du SP quant au SLO ?
Faille de sécurité potentielle si échec du SLO
Best practice : ne pas implémenter SLO
Pas le même problème avec le SSO : s'il échoue ce n'est pas grave du point de vue de la sécurité

La fermeture du navigateur Web détruit les cookies de session

Confiance entre IDPs et SPs
Quelle politique de sécurité de l'IDP auquel le SP fait confiance ?
Vice versa : comment s'assurer, du point de vue de l'IDP, que le SP ne fait pas n'importe quoi avec les infos d'identité envoyées ?

Aller voir les spec de l'échange de métadonnées
Origine des métadonnées à vérifier
utilisation l'option d'expiration de la période de validité des métadonnéesn

Signature XML : d'abord le décodage du fichier XML => DOS

Echecs de SAML:
simpleSAML php demo cert : à fuir

wrapping attack

Au contraire
Cross domain sso très utilisé //TODIG

TODO
téléchargement des deux autres vidéos DONE

Faire une synthèse des deux
