Le blog a été déplacé
Vous pouvez me retrouver sur IT-Signal

mercredi 31 août 2011

Capitaine Spock et les tests unitaires

Capitain SpockSpock est framework open source de test qui permet de définir une spécification de test. L'approche est plus structurée que Junit. Je vous laisse découvrir :


Article rédigé pour la plateforme de capitalisation technique de Sword

lundi 11 juillet 2011

Authentification sur LDAP avec SpringSecurity

Spring Security est un projet de Spring Source anciennement appelé Acegi Security. Le framework a beaucoup évolué depuis en s'étoffant de nombreux connecteurs et en améliorant son intégration avec Spring framework.

Dans cet article, nous allons aborder la méthode d'authentification LDAP (Windows Active Directory, Open LDAP...) à travers Spring Security. Les détails de l'utilisation de l'autorisation (droit d'effectuer certaines opérations) ne seront pas évoqués ici, le sujet étant déjà largement documenté. La référence du framework comprend tout un chapitre dédié à l'interaction avec un LDAP.

Les annuaires LDAP

Un annuaire LDAP est un système de gestion des informations associées aux acteurs d'un SI (utilisateurs, machines, équipements électroniques...). Le principal rôle de l'annuaire est de stocker les informations des utilisateurs et de permettre grâce à des mécanismes simples de rechercher, tri et organiser l'information.
Les annuaires LDAP proposent une organisation hiérarchique de l'information. Chaque élément est contenu dans les éléments parents (groupes, unités organisationnelles ou physiques...).
La fonctionnalité qui nous intéresse est la possibilité d'authentifier les utilisateurs. L'annuaire LDAP possède le login de l'utilisateur et le password (hashé), il peut ainsi authentifier de manière sûre un utilisateur. Les annuaires ont aussi la possibilité d'enregistrer une liste de rôles attribués aux utilisateurs. Ces rôles permettent de différencier des profils d'utilisateurs pour les offrir des autorisations différentes.
LDAP n'est pas une implémentation d'annuaire, mais uniquement une norme respectée par de nombreux grands systèmes d'annuaires (Windows Active Directory, Open LDAP, Apache Directory ServerNovell eDirectoryIBM Lotus Domino...).
Dernier point d'importance, LDAP dispose d'une règle de nommage permettant de gérer les organisations hiérarchiques. Par exemple, le Distinguished Name (DN) cn=Jean,ou=gens,dc=EXEMPLE,dc=FR est composé des éléments suivants :
  • CN (common name) = jean ⇒ Le nom de l'utilisateur
  • OU (Organisation Unit) = gens ⇒ Le groupe contenant les utilisateurs (ou=machines pour les PC par exemple...)
  • DC (Domain component) dc=EXEMPLE,dc=FR  ⇒ exemple.fr, le domaine du ldap
Le DN d'un utilisateur permet de l'identifier de façon unique dans l'annuaire.

Le principe de l'authentification sur LDAP

Le processus d'authentification sur un LDAP s'appelle un BIND. Il permet de fournir au système LDAP un DN d'utilisateur et un mot de passe (généralement chiffré).
Avant de pouvoir faire un bind, il est nécessaire de récupérer le DN de l'utilisateur. Il n'est pas réaliste de demander à l'utilisateur son DN complet. La première étape est donc généralement une recherche dans l'annuaire sur un attribut (login de l'utilisateur) dans un répertoire de l'annuaire (les utilisateurs généralement).
Une fois le bind réalisé, il est possible de demander des informations supplémentaires à l'annuaire comme par exemple les rôles de l'utilisateur (administrateur, membre d'une liste de diffusion...). Ces éléments pourront être réutilisés dans Spring Security pour automatiser l'attribution d'autorisations.

Exemple d'utilisation

L'exemple suivant montre comment intégrer l'authentification ldap dans un projet comprenant déjà une authentification simple (BDD par exemple) basée sur Spring Security.

L'AuthenticationManager de Spring Security

Ici on configure un authentication-manager grâce à un schéma xml spring security spécialisé. La balise authentication-manager crée automatiquement l'object authentication manager avec une configuration standard efficace. Les enfants de cette balise sont les différentes sources d'authentification utilisée :
  • L'authentification provider ldap (ldapAuthProvider)
  • Un autre authentification provider définissant un sel et un service spécialisé (proxyUserDetailsService2)
<authentication-manager alias="authenticationManager">
   
         <authentication-provider ref="ldapAuthProvider">
          
         <authentication-provider user-service-ref="proxyUserDetailsService2">
               <password-encoder ref="passwordEncoder">
                     <salt-source ref="saltSource">
               </salt-source></password-encoder>
         </authentication-provider>
    </authentication-provider>
</authentication-manager>

Le LDAP AuthenticationProvider

L'objet authenticationProvider est un service SpringSecurity prenant en en entrée un token (par exemple couple login/password) et répondant avec un utilisateur authentifié (ou null s'il n'existe pas). Dans notre cas, l'authentificationProvider à utiliser est créé avec la configuration xml suivante :
<bean class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider" id="ldapAuthProvider">
 <constructor-arg>
  <bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
   <constructor-arg ref="contextSource" />
   <property name="userSearch">
    <bean class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
     <constructor-arg value="OU=MonGroup">
     <constructor-arg value="(sAMAccountName={0})">
     <constructor-arg ref="contextSource">
    </bean>
   </property>
  </bean>
 </constructor-arg>
 <constructor-arg>
  <bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
   <constructor-arg ref="contextSource">
   <constructor-arg value="OU=Groups,OU=Mycompany,OU=CompanyUsers">
   <property name="rolePrefix" value="LDAP_ROLE_" />
  </bean>
 </constructor-arg>
 <property name="userDetailsContextMapper">
  <bean class="com.company.AcmeContextMapper">
   <property name="AcmeSecurityProviderService" ref="acmeSecurityProviderService" />
  </bean>
 </property>
</bean>
<bean class="org.springframework.security.ldap.DefaultSpringSecurityContextSource" id="contextSource">
 <constructor-arg value="ldap://1.2.3.4:389/dc=exemple,dc=com" />
 <property name="userDn" value="CN=browser,OU=testAccount,DC=exemple,DC=com" />
 <property name="password" value="P@ssw0rd" />
</bean>
Le premier bean définit le service d'authentification. Le premier argument (le bind authenticator) définit les paramètres de la recherche de l'utilisateur dans le LDAP (quel groupe cherche, quelle requête...).
La seconde partie du bean (DefaultLdapAuthoritiesPopulator) définit la recherche des groupes LDAP pour les transformer en rôles Spring security.
La dernière partie (userDetailsContextMapper) est une classe qui permet de convertir l'objet User retournée par le LDAP en un objet User de l'application pour l'enrichir, effectuer des modifications...
Le second bean présente le contexte LDAP avec l'url de connexion et la définition d'un utilisateur permettant de lire les informations du LDAP.

A retenir

La connexion LDAP est une authentification en deux étapes (recherche puis bind) qui permet d'authentifier l'utilisateur et de lui attribuer des rôles. Cette solution permet de mettre en oeuvre rapidement une authentification centralisée.

Les informations utiles pour configurer une connexion LDAP

  • L'URL de connexion au LDAP
  • Un utilisateur ayant les droits pour browser les comptes LDAP :
    • son DN
    • son password
  • L'OU contenant les utilisateurs
  • La requêtes de recherche de l'utilisateur [sur un active directory (sAMAccountName={0}) ]
  • Pour la transformation des groupes en rôles :
    • l'OU contenant les groupes
    • Un préfixe pour les rôles

Article rédigé pour la plateforme de capitalisation technique de Sword

mercredi 15 juin 2011

Hg Init

Un très bon guide pour commencer avec Mercurial : hginit.com

Les chapitres du guide 

vendredi 22 avril 2011

Sécurisation d'une application Flex/Granite DS sur SpringSecurity et Tide

Une application Flex/Java peut utiliser Granite DS/Tide pour simplifier la communication entre les deux tiers de l'application. Spring Security permet la gestion de l'autorisation et de l'authentification. Dans cet article nous allons voir comment utiliser ces deux mécanismes (authentification et autorisation) dans Flex comme si nous étions dans un code Java.

Etablissement du lien entre Flex et Java par Granite DS

La configuration de la communication entre Flex et Java est effectuée grâce à deux fichiers de configuration dans le projet Java :
  • src/main/webapp/WEB-INF/flex/services-config.xml pour la configuration des canaux de communication entre Flex et Java, le lien avec Spring;
  • src/main/webapp/WEB-INF/granite/granite-config.xml pour la configuration de granite et le lien avec Spring Security.
Les configurations (très standard) ont été simplifiées à l'extrême. En effet Granite DS est très bien intégré avec l'écosystème Spring.

Déclaration d'un canal de communication (services-config.xml)

Dans le fichier services-config.xml. Nous allons demander la création d'un service de communication (granite-service) qui s'appuiera sur un canal de communication de type AMF (protocole de communication binaire très efficace d'Adobe) à une adresse précisée dans la configuration :
  • La création du service :
<services>
  <service class="flex.messaging.services.RemotingService" id="granite-service" messagetypes="flex.messaging.messages.RemotingMessage">
   <destination id="spring">
    <channels>
     <channel ref="amf">
    </channel></channels>
    <properties>
     <factory>tideSpringFactory</factory>
     <entitymanagerfactorybeanname>entityManagerFactory</entitymanagerfactorybeanname>
    </properties>
   </destination>
  </service>
 </services>
  • La création du canal :
<channels>
  <channel-definition class="mx.messaging.channels.AMFChannel" id="amf">
   <endpoint class="flex.messaging.endpoints.AMFEndpoint" url="http://{server.name}:{server.port}/{context.root}/amf">
  </endpoint></channel-definition>
 </channels>
Enfin nous allons demander la création d'une factory permettant de créer les objets Java à partir de leurs équivalent Flex. Pour cela nous utilisons un adapteur vers Spring.
<factories>
  <factory class="org.granite.tide.spring.SpringServiceFactory" id="tideSpringFactory">
  </factory>
 </factories>

Configuration de granite (granite-config.xml)

La configuration de granite est encore plus simple. Nous allons juste demander à granite de se configurer avec les annotations dans le code (@RemoteDestination...) et nous lui précisons que la sécurité est gérée par Spring Security ; un adapteur spécifique existe.
<granite-config scan="true">
    <security type="org.granite.spring.security.SpringSecurity3Service">
    </security>
 </granite-config>

Authentification en Flex

Encore une fois le travail est complètement pré-maché par Granite DS. Pour gérer l'utilisateur de l'application Flex, granite met à disposition du programmeur un objet Identity qui permet d'accéder aux informations utiles (données de l'utilisateurs, droits...).
Pour effectuer un login il suffit de faire :
private var __identity:Identity = Identity.instance();
    __identity.login(username, password, onSuccessfulLogin, onFaultLogin);
La méthode effectue exactement la même action que si un utilisateur remplissait un formulaire de login de Spring Security. Les méthodes onSuccessfulLogin et onFaultLogin sont appelée en cas de réussite ou d'échec. Nous pouvons récupérer le nom d'utilisateur avec la méthode suivante :
__identity.username

Autorisation en Flex

Il existe 3 méthodes très utiles sur l'objet Identity permettant de gérer l'autorisation :
  • ifAllGranted : Si l'utilisateur à toutes les autorisations demandées
  • ifAnyGranted : Si l'utilisateur a au moins l'une des autorisations demandées
  • ifNotGranted : Si l'utilisateur n'a pas l'autorisation demandée
Ces méthodes sont asynchrones mais bindable, ce qui permet de les utiliser dans un script mxml :
<mx:button click="updateProduct()" enabled="{dg.selectedItem}" id="bUpdate" label="Update" visible="{identity.hasRole('admin')}"></mx:button>

Ainsi le bouton ne sera visible que pour les administrateurs.
Les appels peuvent aussi être effectués avec un respondeur pour prévoir le prévoir le coté asynchrone de l'appel.
Il existe un cache d'autorisations qui peut être vidé manuellement.

En conclusion

L'utilisation de l'authentification/autorisation est extrêmement simple en Flex, mais parfois un peu limité. Dès qu'un projet à des besoins plus complexes en terme d'autorisation (variabilité en fonction d'un contexte...) il sera nécessaire de s'appuyer sur ces briques de base pour développer un système plus souple.


Article rédigé pour la plateforme de capitalisation technique de Sword

SchemaCrawler pour extraire la structure d'une base de donnée

SchemaCrawler est un framework open-source de découverte de base de données sur jdbc. Il permet de récupérer facilement les métadonnées de la base étudiée. Le framework est aussi publié sous forme d'outil indépendant à utiliser en ligne de commande. Il permet par exemple d'extraire un fichier de description d'une base.
SchemaCrawler permet ainsi de récupérer les informations suivantes :

  • Liste des catalogs (rarement utilisés)
  • Liste des schémas
  • Liste des tables, vues, routines, triggers, contraintes...
  • Liste et détails des champs

Une recherche en deux étapes

La récupération des métadonnées se fait en deux étapes :
  • La définition des options de recherche
  • Le lancement de la recherche sur une connection jdbc

Les options de recherche

La définition des options de recherche est la phase la plus longue et complexe. Des options mal configurées engendrent des temps de réponse très long et une quantité d'informations inutiles et parasites importante. Il est nécessaire de filtrer tout ce que l'on ne souhaite pas récupérer.
Pour filtrer les éléments à conserver, SchemaCrawler met à disposition des objets InclusionRule qui prennent en paramètre une regExp à inclure et une regExp à exclure.
Dans l'exemple suivant, nous définissons un objet option de schemaCrawler permettant de ne récupérer aucune table, aucune vue, aucune procédure, aucun champ... Ainsi nous ne récupérons que la liste des schémas/catalogs de la base.
// Création d'un objet option de SchemaCrawler
    SchemaCrawlerOptions options = new SchemaCrawlerOptions();

    // On n'autorise aucun type de tables (tables, vues...)
    TableType[] types = {};
    options.setTableTypes(types);

    // On exclue toutes les procédures (inclure rien, exclure tout)
    InclusionRule procedureInclusionRule = new InclusionRule("", ".*");
    options.setProcedureInclusionRule(procedureInclusionRule);

    // On exclue toutes les colonnes des procédures (inclure rien, exclure tout)
    InclusionRule procedureColumnInclusionRule = new InclusionRule("", ".*");
    options.setProcedureColumnInclusionRule(procedureColumnInclusionRule);

    // On exclue toutes les tables(inclure rien, exclure tout)
    InclusionRule tableInclusionRule = new InclusionRule("", ".*");
    options.setTableInclusionRule(tableInclusionRule);

    // On fixe le niveau de détail des métadonnées au minimum
    options.setSchemaInfoLevel(SchemaInfoLevel.minimum());

    // On exclue les procédure stockées
    options.setShowStoredProcedures(false);

Le lancement de la recherche

Le lancement de la recherche est la partie la plus simple. Il suffit de faire appel à la méthode SchemaCrawlerUtility.getDatabase et de passer en paramètre une connexion à la base de données valide (de type java.sql.Connection) et un objet option de schemaCrawler.
Database crawlingDatabase = SchemaCrawlerUtility.getDatabase(databaseConnection, options);
Une fois l'objet database de SchemaCrawler récupéré, il est très simple de parcourir les métadonnées. Par exemple pour tracer la liste des schémas de la base de données :
for (final Schema schema : crawlingDatabase.getSchemas()) {
        log.info("Found database schema " + schema.getSchemaName() + " in catalog " + schema.getCatalogName());
    }

Différentes options intéressantes

Comme expliqué dans le chapitre précédent, la complexité d'utilisation de ce framework réside principalement dans la configuration de l'objet option permettant de spécifier correctement les informations à remonter pour réduire au maximum les temps de recherche en base (un scan complet de la base étant extrêmement long). Dans cette section, nous allons proposer deux exemples types de définition de l'objet option pour récupérer les schémas puis les tables et vues. Sur le même principe toutes les métadonnées de la base peuvent être récupérées.

Récupération de la liste des schémas

Idem chapitre précédant :
// Création d'un objet option de SchemaCrawler
    SchemaCrawlerOptions options = new SchemaCrawlerOptions();

    // On n'autorise aucun type de tables (tables, vues...)
    TableType[] types = {};
    options.setTableTypes(types);

    // On exclue toutes les procédures (inclure rien, exclure tout)
    InclusionRule procedureInclusionRule = new InclusionRule("", ".*");
    options.setProcedureInclusionRule(procedureInclusionRule);

    // On exclue toutes les colonnes des procédures (inclure rien, exclure tout)
    InclusionRule procedureColumnInclusionRule = new InclusionRule("", ".*");
    options.setProcedureColumnInclusionRule(procedureColumnInclusionRule);

    // On exclue toutes les tables(inclure rien, exclure tout)
    InclusionRule tableInclusionRule = new InclusionRule("", ".*");
    options.setTableInclusionRule(tableInclusionRule);

    // On fixe le niveau de détail des métadonnées au minimum
    options.setSchemaInfoLevel(SchemaInfoLevel.minimum());

    // On exclue les procédure stockées
    options.setShowStoredProcedures(false);

Récupération de la liste des tables et vues

// Création d'un objet option de SchemaCrawler
    SchemaCrawlerOptions options = new SchemaCrawlerOptions();

    // On autorise les tables et les vues
    TableType[] types = { TableType.table, TableType.view };
    options.setTableTypes(types);

    // On exclue toutes les procédures (inclure rien, exclure tout)
    InclusionRule procedureInclusionRule = new InclusionRule("", ".*");
    options.setProcedureInclusionRule(procedureInclusionRule);

    // On exclue toutes les colonnes des procédures (inclure rien, exclure tout)
    InclusionRule procedureColumnInclusionRule = new InclusionRule("", ".*");
    options.setProcedureColumnInclusionRule(procedureColumnInclusionRule);

    // On exclue les procédure stockées
    options.setShowStoredProcedures(false);

    // On inclue toutes les tables mais on exclue les tables commençant par $ (pour éviter les tables système d'Oracle)
    InclusionRule tableInclusionRule = new InclusionRule(".*", ".*[\\x24]+.*");
    options.setTableInclusionRule(tableInclusionRule);

    // On fixe le niveau de détail des métadonnées au minimum
    options.setSchemaInfoLevel(SchemaInfoLevel.minimum());

    // On spécifie le schéma que l'on souhaite crawler
    InclusionRule schemaInclusionRule = new InclusionRule("MySchema", "");
    options.setSchemaInclusionRule(schemaInclusionRule);
Article rédigé pour la plateforme de capitalisation technique de Sword

vendredi 3 juillet 2009

Contrat de travail signé !

C'est enfin fait, une semaine après la fin de mon stage, je signe mon nouveau contrat de travail, un CDI chez Sword !


PS : Merci Sanh pour le contact !

vendredi 29 mai 2009

Google Wave, une nouvelle plateforme de communication pour le Web de demain


Juste une petite news, pour vous faire partager mon enthousiasme pour Google Wave.

Google Wave part du du constat que le système des mails a été créé il y a plus de 40 ans, avant internet tel qu'on le connait (réseaux sociaux, twitter, RSS, outils collaboratifs...). Ils proposent de remplacer le système de mail par un nouvel outils de communication et collaboration. C'est une sorte de boite à tout faire, jolie, simple et très pratique. Ça semble révolutionnaire...

Quelques source pour ceux qui veulent se documenter :

La sortie de wave est prévue pour fin 2009