Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    septembre 2002
    Messages
    11
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : septembre 2002
    Messages : 11
    Points : 10
    Points
    10

    Par défaut [TOMCAT] [JAAS] Extension de LoginModule dans Tomcat

    Bonjour,

    je cherche désespérément une solution à l'intégration de JAAS dans Tomcat. J'ai développé ma propre classe d'authentification (LoginModule), authentification basée sur un schéma de ma base de données (JDBC).
    Le test en mode console fonctionne, et j'aimerai à présent intégrer tout cela à tomcat, afin de protéger mes pages jsp et mes servlet.

    Problème:
    Je ne comprend pas comment le lien est effectué entre Tomcat et mon LoginModule.

    J'ai effectué le paramétrage du fichier jaas.config, et l'ai installé dans le répertoire conf de tomcat.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    GestLicJAAS {
       RdbmsLoginModule required debug="true" 
           url="jdbc:microsoft:sqlserver://SQL:1433;DatabaseName=test;User=sa;Password=admin" 
           driver="com.microsoft.jdbc.sqlserver.SQLServerDriver"
           table="login"
           userField="login"
           passField="motdepasse"
           nameField="nom"
           fornameField="prenom"
           insertPerm="insertion"
           deletePerm="suppression"
           updatePerm="miseajour"
           selectPerm="visu";
    };
    Puis, configuration du fichier context.xml pour ajouter un module Realm
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    <Realm className="org.apache.catalina.realm.JAASRealm"
    	appname="GestLicJAAS"
    	userClassNames="com.actemium.lille.gestlic.security.RDBPrincipal"
    	roleClassNames="com.actemium.lille.gestlic.security.RDBCredential"
    />
    Enfin, j'ai ajouté les security constraints dans le fichier web.xml
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
     
    <security-constraint>
      	<display-name>WebApp Administration</display-name>
    	<web-resource-collection>
    		<web-resource-name>WebApp Admin</web-resource-name>
    		<url-pattern>/*</url-pattern>
    		<http-method>DELETE</http-method>
    		<http-method>GET</http-method>
    		<http-method>POST</http-method>
    		<http-method>PUT</http-method>
    	</web-resource-collection>
    	<auth-constraint>
    		<role-name>administrator</role-name>
    		<role-name>other-role</role-name>
    	</auth-constraint>
     
      </security-constraint>
     
      <login-config>
      	<auth-method>FORM</auth-method>
      	<realm-name>GestLicJAAS</realm-name>
      	<form-login-config>
      		<form-login-page>
      			/login.jsp
      		</form-login-page>
      		<form-error-page>
      			/error.jsp
      		</form-error-page>
      	</form-login-config>
      </login-config>
     
      <security-role>
      	<role-name>administrator</role-name>
      </security-role>
      <security-role>
    	<role-name>other-role</role-name>
      </security-role>
    Le déploiement fonctionne.
    A la connexions sur la page, j'obtiens le message d'erreur "Impossible de trouver une configuration de connexion".

    J'ai cherché au niveau des forum sans grand succés, les tutorats sur JAAS sont nombreux, mais l'intégration dans Tomcat est quelque peu trouble.

    Un coup de main serait sympa.

    Merci.

  2. #2
    Membre à l'essai
    Profil pro
    Inscrit en
    septembre 2002
    Messages
    11
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : septembre 2002
    Messages : 11
    Points : 10
    Points
    10

    Par défaut

    bonjour,

    un petit détail, j'ai effectué aussi la relation entre la JVM et le fichier de configuration de JAAS (jaas.config) depuis le script de lancement de Tomcat.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    rem JAVA_OPTS pour TOMCAT et JASS Realms
    SET JAVA_OPTS=-DJAVA_OPTS=-Djava.security.auth.login.config==%CATALINA_HOME%/conf/jaas.config

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    septembre 2002
    Messages
    11
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : septembre 2002
    Messages : 11
    Points : 10
    Points
    10

    Par défaut

    ok, solution trouvée, dur dur !!!! 8)

    je donne la procédure détaillée, histoire que d'autres ne mettent pas autant de temps pour mettre en place JAAS sous Tomcat.

    1. Développer un module implémentant LoginModule, il faut aussi une classe Principal, et Role.

    voir tutorial suivant
    http://forum.java.sun.com/thread.jsp?thread=233317&forum=60&message=1216065

    Soit :

    Fichier RDBLoginmodule
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
     
     
    package my;
     
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.Map;
     
    import javax.security.auth.Subject;
    import javax.security.auth.callback.Callback;
    import javax.security.auth.callback.CallbackHandler;
    import javax.security.auth.callback.NameCallback;
    import javax.security.auth.callback.PasswordCallback;
    import javax.security.auth.login.LoginContext;
    import javax.security.auth.login.LoginException;
    import javax.security.auth.spi.LoginModule;
     
    public class RDBLoginModule implements LoginModule {
     
        // Etat initial, nécessaire par l'implements
        CallbackHandler callbackHandler;										// Chargé de l'interface avec l'utilisateur ou tout autre moyen d'authentification
        Subject subject;														// Informations concernant l'identification (princiapl, credential, nom, ...)
        Map sharedState;														// Informations partagées entre les différents LoginModule
        Map options;															// Options complémentaires du fichier de configuration, ici, l'URL et le driver d'accès à la base de données
     
        // Etat temporaire, stockage temporaire des informations d'identification
        // Une personne peut être identifié par son nom, N°= sécu, ... == Tableau
        ArrayList tempCredentials;												// Nom ou clé publique
        ArrayList tempPrincipals;												// mot de passe ou clé privée
     
        // Statut d'authentification
        boolean success;														// true si ok !
     
        // Options de configuration récupérées dans le fichier de configuration
        String jdbcUrl;															// URL d'accès à la base de données
        String jdbcDriver;														// Pilote d'accès à la base de données
        String jdbcTable;														// Table contenant les informations de connexion
        String userField;														// Champs contenant les informations du nom/login de l'utilisateur
        String passField;														// Champs contenant le mot de passe de l'utilisateur
        String nameField;														// Nom de l'utilisateur
        String fornameField;														// Prénom de l'utilisateur
        String insertPerm;														// Champs contenant la valeur de la permission d'insérer
        String deletePerm;														// Champs contenant la valeur de la persmission de supprimer
        String updatePerm;														// Champs contenant la valeur de la permission de mettre à jour
        String selectPerm;														// Champs contenant la valeur de la permission de visualiser les données
     
        private boolean commited;
     
        public RDBLoginModule() {
            tempCredentials = new ArrayList();
            tempPrincipals = new ArrayList();
     
            success = false;
     
            jdbcUrl = null;
            jdbcDriver = null;
            jdbcTable = null;
            userField = null;
            passField = null;
            nameField = null;
            fornameField = null;
            insertPerm = null;
            deletePerm = null;
            updatePerm = null;
            selectPerm = null;
     
            callbackHandler = null;
            subject = null;
            sharedState=null;
            options=null;
            System.out.println("depuis construct RDBLoginmodule");
     
            commited = false;
        }
     
        public boolean abort() throws LoginException {
            success = false;
     
            tempPrincipals.clear();
            tempCredentials.clear();
     
            if (callbackHandler instanceof PassiveCallbackHandler) 
                ((PassiveCallbackHandler)callbackHandler).clearPassword();
     
            logout();
     
            return true;
        }
     
        public boolean commit() throws LoginException {
            if (success) {
                try {
                    subject.getPrincipals().addAll(tempPrincipals);
                    subject.getPrincipals().add(new RDBRolePrincipal("admin"));
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new LoginException(e.getMessage());
                }
            }
            commited=true;
            return true;
        }
     
        public boolean login() throws LoginException {
            // Y a-t-il bien un CallbackHandler initialisé ?
            if (callbackHandler == null) {
                throw new LoginException("Erreur: pas de callback !");
            }
            try {
                // initialisation des callbacks
                Callback[] callbacks = new Callback[]{
                        // Callback pour retrouver les informations de Nom, l'argument est le prompt à afficher
                        new NameCallback("Nom:"),
                        // Callback pour le mot de passe, prompt et true pour afficher le mot de passe en le tapant
                        new PasswordCallback("Mot de passe:",false)
                };
                // Appel de la méthode handle pour récupérer les informations d'identification
                // La méthode dce récupération ne nous interesse pas ici, seul nous intéresse la récupération
                // du user et du mot de passe ou des clés
                callbackHandler.handle(callbacks);  
     
                // Récupération des informations
                // L'ordre des données est donné plus haut
                String userName = ((NameCallback)callbacks[0]).getName();
                String password = new String(((PasswordCallback)callbacks[1]).getPassword());
     
                // Clear du password dans le callbacks
                ((PasswordCallback)callbacks[1]).clearPassword();
     
                // Validation des informations 
                success = rdbValidate(userName, password);
     
                // Annulation des callbacks
                callbacks[0]=null;
                callbacks[1]=null;
     
                if (!success) {
                    throw new LoginException("Authentification incorrecte !");
                }
                return success;    
            } catch (Exception ex) {
     
            }
            return false;
        }
     
        public boolean logout() throws LoginException {
            // clear des informations utilisateur et autorisations
            tempPrincipals.clear();
            tempCredentials.clear();
            if (callbackHandler instanceof PassiveCallbackHandler)
                ((PassiveCallbackHandler)callbackHandler).clearPassword();
            // Clear des informations enregistrées dans le Subject
            Iterator it = subject.getPrincipals().iterator();
            while (it.hasNext()) {
                RDBPrincipal p = (RDBPrincipal)it.next();
                subject.getPrincipals().remove(p);
            }
            it = subject.getPublicCredentials().iterator();
            while (it.hasNext()) {
                RDBCredential c = (RDBCredential)it.next();
                subject.getPublicCredentials().remove(c);
            }
            return true;
        }
     
        public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState,
                Map options) {
     
            this.subject = subject;
            this.callbackHandler = callbackHandler;
            this.sharedState = sharedState;
            this.options = options;
            // récupération et initialisation des options
            jdbcUrl = (String) options.get("url");
            jdbcDriver = (String) options.get("driver");
            jdbcTable = (String) options.get("table");
            userField = (String) options.get("userField");
            passField = (String) options.get("passField");
            nameField = (String) options.get("nameField");
            fornameField = (String) options.get("fornameField");
            insertPerm = (String) options.get("insertPerm");
            deletePerm = (String) options.get("deletePerm");
            updatePerm = (String) options.get("updatePerm");
            selectPerm = (String) options.get("selectPerm");
            System.out.println("depuis init RDBLoginmodule");
        }
     
        private boolean rdbValidate(String userName, String password) throws LoginException {
            System.out.println("depuis validate RDBLoginmodule");
            Connection con;
            // Query avec les champs personnalisés
            String query = "SELECT * FROM " + jdbcTable + " where " + userField + "='" + userName + "'";
            Statement stmt;
            RDBPrincipal  p = null;
            RDBCredential c = null;
            boolean passwordMatch = false;
     
            try {
                Class.forName(jdbcDriver);
            } catch (ClassNotFoundException cnfex) {
                throw new LoginException("Pilote JDBC introuvable, vérifier votre CLASSPATH");
            }
            try {
                // Effectue la connexion à la base de données, requête sur l'utilisateur
                // récupération des données
                con = DriverManager.getConnection(jdbcUrl,"sa","admin");
                stmt = con.createStatement();
                ResultSet rs = stmt.executeQuery(query);
                String dbPassword = null, dbFname = null, dbLname = null;
                String updatePerm = null, deletePerm = null, insertPerm=null, selectPerm=null;
                boolean isEqual   = false;
                while (rs.next()) {
                    // Test en dur !!!
                    dbPassword = "jamaïc";
                    dbFname = "bob";
                    dbLname = "marley";
                    insertPerm = "1";
                    deletePerm = "1";
                    updatePerm = "1";
                    selectPerm = "1";
     
                }
                // Password null == utilisateur introuvable
                if (dbPassword==null) 
                    throw new LoginException("Utilisateur ou mot de passe incorrect !");
                passwordMatch = password.equals(dbPassword);
     
                // Principals et Credentials
                if (passwordMatch) {
                    c = new RDBCredential();
                    c.setProperty("insertPerm",insertPerm);
                    c.setProperty("deletePerm",deletePerm);
                    c.setProperty("updatePerm",updatePerm);
                    c.setProperty("selectPerm",selectPerm);
                    this.tempCredentials.add(c);
                    this.tempPrincipals.add(new RDBPrincipal(dbFname + " " + dbLname));
                }
     
                rs.close();
                stmt.close();
                con.close();
            } catch (SQLException sqlex) {
                sqlex.printStackTrace() ;
                System.out.println("exception capturée SQL");
                throw new LoginException(sqlex.getMessage());
            }
            return passwordMatch;
        }
        static public void main(String args[]) throws Exception{
     
            LoginContext ctx = new LoginContext("RDBLogin");
            System.out.println("depuis constructeur RDBLoginmodule");
            ctx.login();
        }
    }
    Fichier RDBRolePrincipal
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
     
    package com.actemium.lille.gestlic.security;
    public class RDBRolePrincipal extends RDBPrincipal {
     
        public RDBRolePrincipal(String s) {
            super(s);
        }
    }
    Fichier RDBPrincipal:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
     
     
    package my;
     
    import java.io.Serializable;
    import java.security.Principal;
     
    public class RDBPrincipal implements Principal, Serializable {
        private String name;
     
        public RDBPrincipal(String name) {
            this.name = name;
        }
     
        public String getName() {
            return name;
        }
        public boolean equals (Object another) {
            try {
                RDBPrincipal pm = (RDBPrincipal)another;
                return pm.name.equalsIgnoreCase(name);
            } catch(Exception e){
                return false;   
            }
        }
        public int hashCode() {
            return name.hashCode();
        }
        public String toString() {
            return name;
        }
    }
    Compiler le tout et plasser le répertoire racine du package dans le répertoire class du répertoire server de tomcat

    1 bis. Créer une base de données de test et une table avec un schéma comparable à ce dernier :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    CREATE TABLE [dbo].[login] (
    	[idlogin] [int] NOT NULL ,
    	[login] [varchar] (50) COLLATE French_CI_AS NULL ,
    	[motdepasse] [varchar] (50) COLLATE French_CI_AS NULL ,
    	[nom] [varchar] (50) COLLATE French_CI_AS NULL ,
    	[prenom] [varchar] (50) COLLATE French_CI_AS NULL ,
    	[insertion] [int] NULL ,
    	[suppression] [int] NULL ,
    	[miseajour] [int] NULL ,
    	[visu] [int] NULL 
    ) ON [PRIMARY]
    GO
    2. Pour la validation à travers une base de données, il faut utiliser une méthode de validation qui effectue la connexion à la base et requête sur la table des utilisateurs. Le mot de passe de la table des utilisateurs est comparé avec le mot de passe fournis par le CallbackHandler de Tomcat.

    3. Créer un fichier de config jaas :
    les variables table, url, driver, userfield, passfield, ... permettent de rendre généric la méthode de connexion quelque soit le schéma de la base utilisé.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    RDBLogin {
       my.RDBLoginModule required debug="true" 
           url="jdbc:microsoft:sqlserver://SQL:1433;DatabaseName=test;User=sa;Password=pass" 
           driver="com.microsoft.jdbc.sqlserver.SQLServerDriver"
           table="login"
           userField="login"
           passField="motdepasse"
           nameField="nom"
           fornameField="prenom"
           insertPerm="insertion"
           deletePerm="suppression"
           updatePerm="miseajour"
           selectPerm="visu";
    };
    Enregistrer ce fichier dans un répertoire sous jaas.config

    3. Modification du fichier server.xml :

    Modifier la section Realm du fichier serv.xml

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    <Realm className="org.apache.catalina.realm.JAASRealm" debug="3" appName="RDBLogin" 
    userClassNames="my.RDBPrincipal" 
    roleClassNames="my.RDBRolePrincipal">
    </Realm>
    appName doit correspondre au nom donné dans le fichier jaas.config

    4. Créer ou mettre à jour la variable d'environnement JAVA_OPTS:

    set JAVA_OPTS=-Djava.security.auth.login.config=g:/www/jaas.config

    depuis une session Dos et lancer sous la session dos la commande startup depuis le répertoire bin de Tomcat.
    Cette variable est importante et permet de donner le chemin d'accès au fichier de condfiguration de JAAS.

    5. Lancer un navigateur Web et aller dans la section administration de Tomcat.
    Le gestionnaire de sécurité est à présent basé sur votre classe LoginModule.
    Ici, la gestion se fait depuis la base de données.
    Créer donc un utilisateur dans la table des utilisateurs.

    ATTENTION: depuis le LoginModule, dans la méthode commit, il faut ajouter un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
     subject.getPrincipals().add(new RDBRolePrincipal("admin"));
    pour permettre à Tomcat de connaître le rôle de la personne connecté.

    Voilà, j'espère que ce n'était pas trop confus, ca l'est peut-être encore un peu pour moi.

    Je début sous les Servlet, JSP, Hibernate et Tomcat et met en place une application minimale de test pour mettre tout cela en oeuvre.

    Ca pourrait peut-être donné lieu à un tutorial si cela interresse quelques personnes.

    @+

  4. #4
    Membre régulier
    Homme Profil pro
    Consultant informatique
    Inscrit en
    avril 2003
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Luxembourg

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : avril 2003
    Messages : 145
    Points : 100
    Points
    100

    Par défaut

    Salut GiHe,

    Je cherche exactement la même chose pour le moment afin de faire un squelette pour toutes mes applications. Je viens de passer plus d'une semaine a essayer mais sans grand succès pour le moment.

    Dans la fin de ton explication tu dis que tu mets en place un application minimale de test pour mettre tout ca en oeuvre.

    Serais-tu d'accord de partager tes sources ?

    Si tu es d'accord peux-tu me les envoyer par mail ?

    Merci
    NiGhMa

  5. #5
    Membre habitué
    Profil pro
    Inscrit en
    février 2003
    Messages
    136
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : février 2003
    Messages : 136
    Points : 139
    Points
    139

    Par défaut

    salut nighma,
    la solution de GiHe est élégante, et est un bon point de départ.
    je suis parti sur la même voie et ai crée un projet open source appelé jGuard (http://jguard.sourceforge.net).

    GiHe a rejoint le projet pour implémenter ensemble une solution plus aboutie.

    je t'incite donc à jeter un oeil à jGuard.

    cordialement,

    Charles(jGuard team).

  6. #6
    Membre régulier
    Homme Profil pro
    Consultant informatique
    Inscrit en
    avril 2003
    Messages
    145
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Luxembourg

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : avril 2003
    Messages : 145
    Points : 100
    Points
    100

    Par défaut

    Merci Charles,

    Je vais de ce pas y jeter un oeil.

    Je pense que c'est une bonne initiative, ça manquait.

    Seb
    NiGhMa

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Comment se déconnecter avec JAAS dans Tomcat
    Par astoner dans le forum Tomcat et TomEE
    Réponses: 2
    Dernier message: 06/09/2013, 14h29
  2. [Tomcat 4 VS 5] différence dans server.xml
    Par pmartin8 dans le forum Tomcat et TomEE
    Réponses: 2
    Dernier message: 13/10/2005, 15h53
  3. [Tomcat] [JAAS] Des idées mais pas de solution concrètes
    Par cgougeon dans le forum Tomcat et TomEE
    Réponses: 6
    Dernier message: 27/09/2005, 15h22
  4. [TOMCAT] acces aux page jsp dans un autre repertoire
    Par yanagiba dans le forum Tomcat et TomEE
    Réponses: 8
    Dernier message: 16/08/2005, 07h48
  5. [JOnAS-Tomcat][JAAS]Probleme de securisation de contexte
    Par SEMPERE Benjamin dans le forum JOnAS
    Réponses: 3
    Dernier message: 24/06/2005, 14h49

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo