ssh est un outil merveilleux : il permet de se connecter à un hôte et exécuter des commandes à distances de manière sécurisée.

Il permet également de tunneler un port local vers un port distant d’une autre machine. Ainsi tout ce qui est envoyé sur le port local va être transmis au port distant via la communication ssh sécurisée. Cette manipulation s’avère très pratique pour sortir d’un réseau dont seuls les ports 22 et 80 sont ouverts.

Je viens d’apprendre récemment comment faire l’inverse : tunneler un port d’une machine distante vers un port d’une machine locale. Ainsi tout ce qui est envoyé sur le port de la machine distante va être transmis au port de la machine locale. En résumé, si votre machine locale est sur un réseau protégé par un NAT et un firewall, çette manipulation met en place une backdoor vers le réseau protégé.
Lire la suite…

Lors de l’écriture de tests unitaires, on peut se retrouver à écrire ce genre de code :

assertEquals(42, answer);
assertFalse(answer == 34);
assertTrue(info.matches("color") || info.matches("colour"));
assertTrue(colorsList.contains("red"));
assertTrue(s instanceof MagicString);

Mais JUnit propose depuis la version 4.4 une nouvelle syntaxe à base de Matchers. En fait, cette syntaxe n’est pas totalement nouvelle : elle est empruntée à JMock qui possède déjà les Matchers JMock pour décrire les contraintes d’un mock. L’idée vient de Joe Walnes dans son article Flexible JUnit assertions with assertThat().

Les assertions précédentes peuvent alors s’écrire de la manière suivante :

assertThat(answer, is(42));
assertThat(answer, is(not(34)));
assertThat(info, either(containsString("color")).or(containsString("colour")));
assertThat(colorsList, hasItem("red"));
assertThat(s, instanceOf(MagicString.class));

Les avantages sont multiples :

  • Ça se lit mieux : le développeur communique donc mieux son intention aux autres développeurs, ce qui est essentiel !
  • Contrairement aux méthodes assertEquals et consorts, on ne risque pas de tromper dans l’ordre des arguments : la syntaxe d’assertEquals est assertEquals([expected], [actual]) mais beaucoup de gens se trompent et écrivent assertEquals(answer, 42). Avec assertThat, aucun risque.
  • Les matchers peuvent être modifiés: par exemple : not(s) pour inverser la condition, either(s).or(t) pour combiner des conditions, each(s) pour appliquer une condition sur chaque élément d’une collection, afterFiveSeconds(s) pour attendre 5 secondes avant de tester…
  • En cas d’échec de l’assertion, les messages sont beaucoup plus lisibles. Si on reprend l’exemple précédents, avec les assertXxx classiques, les messages ressembleraient à ça :
    assertEquals(42, answer);
     => expected:<42> but was:<36>
    assertFalse(answer == 34);
     => junit.framework.AssertionFailedError:
    assertTrue(info.matches("color") || info.matches("colour"));
     => junit.framework.AssertionFailedError:
    assertTrue(colorsList.contains("red"));
     => junit.framework.AssertionFailedError:
    assertTrue(s instanceof MagicString);
     => junit.framework.AssertionFailedError:
    

    Avec assertThat, les message ressemblent plutôt à ça :

    assertThat(answer, is(42));
     => Expected: is <42>
             got: <36>
    assertThat(answer, is(not(34)));
     => Expected: is not <34>
             got: <34>
    assertThat(info, either(containsString("color")).or(containsString("colour")));
     => Expected: (a string containing "colour" or a string containing "color")
             got: "rainbow"
    assertThat(colorsList, hasItem("red"));
     => Expected: a collection containing "hello"
             got: <[bonjour, ia orana]>
    assertThat(s, instanceOf(MagicString.class));
     => Expected: an instance of sandbox.FooTest$MagicString
             got: "polop"
    

    Inutile de dire qu’il est beaucoup plus facile de débugguer des tests écrits avec assertThat…

  • Il est plus facile d’écrire des Matcher maison plutôt que des méthodes assertXxxx maison. En plus ces Matchers pourront être réutilisés dans des tests JMock.
  • C’est sexy :)

Pour plus d’informations, je vous recommande la lecture de ce billet. Il est en français, il est très détaillé et je suis tombé dessus en écrivant cet article.

boîte de conserveEn java, n’importe quel objet implémentant l’interface Serializable peut être converti facilement en une séquence d’octets. Cette même séquence peut ensuite être reconverti en l’objet original. Ce mécanisme est par exemple utilisé pour transmettre des objets par RMI.

Voici une petite méthode pour vérifier que votre classe est bien sérialisable

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
 
public class Assert {
    /**
     * Assert that an object is serializable by dumping it and then reading it
     * back and finally by checking the original object and the dumped and read
     * object are equals.
     *
     * <p>Obviously, the method <code>equals()</code> has to be implemented
     * correctly for this method can work.
     * @param objOriginal the instance which will be checked against serialization;
     * must implement <code>equals()</code> or it won't work
     */
    public static void assertSerializable(Object objOriginal) {
        try {
            final ByteArrayOutputStream stream = new ByteArrayOutputStream();
            final ObjectOutputStream out = new ObjectOutputStream(stream);
            out.writeObject(objOriginal);
            out.close();
 
            final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(stream.toByteArray()));
            final Object objSerialized = in.readObject();
            assertEquals(objOriginal, objSerialized);
        } catch (IOException e) {
            fail("unable to serialize or deserialize object " + objOriginal + ": " + e.getMessage());
        } catch (ClassNotFoundException e) {
            // unlikely to happen...
            fail(e.getMessage());
        }
    }
}
</p>

Lire la suite…

rubyDans un article précédent, je donnais une méthode pour convertir rapidement un nombre en binaire et inversement quand on n’avait pas python 3. Ça m’est pratique quand j’ai besoin de faire une conversion rapide : un terminal, on lance python, on tape l’expression et on a le résultat. En regardant les nouveautés de ruby 1.8.7, j’ai découvert que c’était encore plus trivial.

% irb
irb(main):001:0> 42.to_s(2)
=> "101010"
irb(main):002:0> "%b" % 42
=> "101010"
irb(main):003:0> "1001".to_i(2)
=> 9
irb(main):004:0> 0b1001
=> 9

Enfantin non ?

zsh est un shell complet qui remplace avantageusement bash dont on entend beaucoup parler en bien. J’ai décidé de l’essayer récemment.

Sans chercher à exploiter toutes les fonctionnalités qu’il propose, certaines facilités me sont maintenant indispensables lorsque je passe à bash. Une seule en fait : le ** qui se substitue à n’importe quel dossier ou sous-dossier. Un globbing puissant qui permet de s’affranchir de find.

Lire la suite…

Depuis python 2.6, le langage python peut gérer les nombres entier dans leur représentation binaire.

$ python2.6
>>> 0b101111
47
>>> int('1101', 2)
13
>>> int('0b1101', 2)
13
>>> bin(13)
'0b1101'

Cependant, avec les versions précédentes, seule la conversion de binaire vers décimal est possible facilement.

$ python2.5
>>> int('1101', 2)
13
>>> bin(13)
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'bin' is not defined

Voici donc une petite fonction bin :

def bin(n):
    """Convert an integer to its binary string representation"""
    res = []
    while n > 0:
        res.insert(0, str(int(n % 2)))
        n = n // 2
    return "".join(res)

Et ainsi :

$ python2.5
>>> def bin(n):
...     """Convertit un nombre en binaire"""
...     res = []
...     while n > 0:
...         res.insert(0, str(int(n % 2)))
...         n = n // 2
...     return "".join(res)
...
>>> bin(13)
'1101'
>>> int(bin(13), 2)
13

En installant rubygems et en l’utilisant avec Netbeans, on se rend vite compte que rubygems est configuré pour stocker ses gemmes dans /var/lib/gems et que Netbeans n’a pas les droits d’écriture à cet endroit. En effet, la manière traditionnelle d’installer gem implique d’utiliser sudo, en ligne de commande :

$ sudo gems install rails -y

Oui mais voilà, Netbeans n’utilise pas sudo. Pour que Netbeans puisse gérer les gemmes lui-même, il lui faut donc les droits d’écriture sur le dossier /var/lib/gems et ses sous-répertoires. Le plus naïf est de donner tous les droits en écriture, mais c’est mal ! Sinon, on peut aussi créer un groupe nommé rubygems et donner le droit d’écriture à ce groupe. Cette approche est meilleure mais un problème se pose lorsque l’utilisateur y crée un nouveau fichier ou dossier : non seulement celui-ci appartient à l’utilisateur et non pas à root, mais en plus le groupe n’a pas le droit d’écriture. C’est là qu’interviennent les ACLs…

Lire la suite…

S’il vous est déjà arrivé d’avoir à accéder à des dépôts debian placés derrière un proxy, vous pouvez utiliser un outil graphique comme Synaptics qui permet de configurer ça assez facilement. Si vous êtes pluto adepte de la ligne de commande, vous avez alors à modifier les fichiers de configuration d’apt.

sudo echo 'Acquire::http::Proxy "http://proxy.mynetwork.com:1234/";' > /etc/apt/apt.conf.d/proxy

ou plus simplement en définissant les paramètres du proxy dans la variable d’environnement http_proxy (ou ftp_proxy dans le cas d’un miroir ftp).

export http_proxy='http://proxy.mynetwork.com:1234/'

Si le proxy est un proxy authentifiant, alors il faut également préciser le login et le mot de passe dans http_proxy.

export http_proxy='http://login:pwd@proxy.mynetwork.com:1234/'

Évidemment, si vous avez un dépôt debian miroir sur votre réseau interne, vous n’avez pas besoin de définir tout ça. Cependant, si vous voulez également accéder aux dépôts externes, c’est là que ça se complique : si vous définissez http_proxy, le dépôt interne devient inaccessible car apt tente de passer par le proxy, et si vous ne le définissez pas, les depôts externes sont inaccessibles car vous ne passez plus par le proxy. Il faut donc procéder autrement.

Lire la suite…