mardi 20 janvier 2015

Sécuriser son API HTTP (2) (Basic | Digest) Authentication

Laisser un commentaire

Après avoir posé les fondamentaux dans le précédent article nous allons dans celui-ci nous intéresser à deux méthodes d’authentification à savoir l’authentification basique (HTTP Basic Authentication) et l’authentification “Digest” qui sont assez simples à mettre en oeuvre.

L’authentification basique

L’authentification basique est similaire à ce que font la plupart des sites web lorsqu’ils permettent à leurs utilisateurs de s’authentifier via un formulaire qui récupèrent le couple login/mot de passe. La différence est que dans le cas de l’authentification basique les crédentials sont renseignés dans l’en-tête de la requête HTTP qui est envoyée au serveur donc à votre API. L’en-tête de la requête est formaté de la façon suivante :

Authorization: Basic Base64Encode(login:password)
Pour le couple johndoe:jane doe cela donne :
Authorization: Basic am9obmRvZTpqYW5lIGRvZQ==

En général vous n’avez pas besoin d’encoder à la main l’en-tête de la requête car c’est pris en charge par le client HTPP. Dans l’exemple suivant curl construit le bon en-tête :

curl -i -u johndoe:jane https://api.example.com 

L’encodage en base 64 n’offre aucune sécurité en soi car il suffit de faire l’opération inverse (Base64Decode) pour retrouver les infos en clair. En outre les credentials de l’utilisateur sont envoyés dans chaque requête avec l’authentification basique. Cela accroît le risque d’interception de ces informations sensibles.

Comment améliorer la sécurité de l’authentification basique ? Utiliser HTTPs, veiller à la sécurité du stockage des mots de passe et à la façon dont ils sont récupérés lors de l’authentification. Hacher les mots de passe avant de les stocker permet de faire en sorte qu’ils ne soient connus que des utilisateurs et les protège lorsqu’un attaquant accède au stockage car il ne verra que les haches et non les mots de passe en clair. Cependant pour que tout cela soit efficace vous devez choisir un algorithme de hachage qui ne permette pas de retrouver la donnée initiale à partir du hache. Le hachage des mots de passe est un vaste sujet qui vaut bien un article à lui tout seul.

L’authentification “digest”

L’authentification “digest” a été mise en place pour pallier les limitations de l’authentification basique. Elle n’envoie pas le mot de passe en clair dans la requête mais procède à l’authentification par un échange de challenge/réponse.

Le challenge est envoyé par le serveur en réponse à l’initiation de l’échange par le client. Il contient notamment un “nonce” unique à chaque processus d’authentification Digest et un code qui porte le petit d’”opaque” et doit être retourné au serveur dans la réponse au challenge. Dans sa réponse l’utilisateur spécifie son identité (le paramètre username) et la réponse au challenge posé par le serveur. Cette réponse contient un certain nombre d’informations dont un hache calculé avec l'algorithme MD5 come : Hache = MD5(username:password:realm) Ce hache et le login (username) permettent au serveur d’authentifier l’utilisateur. Cela nécessite cependant que le serveur stocke le hache MD5(username:password:realm) ! Cette page wiki donne plus de détails sur les échanges entre le serveur et le client. Vous pouvez initier une authentification digest avec curl comme suit :

curl -k –-digest –u username:password -v https://api.example.com

Pour la route

L’authentification basique est simple à mettre oeuvre mais présente toutefois l’inconvénient de transférer les mots de passe en clair. Il est sage de n’utiliser qu’avec TLS (a.k.a HTTPs) qui ajoute la confidentielle au niveau transport. En revanche l’authentification digest est un peu plus complexe à implémenter mais a le mérite de ne pas transférer les mots de passe en clair. Disposant de ces infos et de vos contraintes propres (deadline, sensibilité de votre API etc.) vous êtes suffisamment armé pour choisir entre ces deux schémas d'authentification.

Lire la suite...

lundi 19 janvier 2015

Sécuriser son API HTTP (1)

Laisser un commentaire

Nous vivons une époque où les API web sont légion et les organisations qu’elles soient des “géants du web” ou non en développent pour être utilisées à la fois par leurs propres applications et par celles de tiers. Dans ce contexte de généralisation et d’ouverture des API la question de la sécurité se pose assez rapidement.

La bonne nouvelle est que les problèmes de sécurité que nous rencontrons sont assez classiques et ont déjà été, dans leur grande majorité, résolus par d’autres personnes. La mauvaise, eh oui il en a une, est que les solutions et standards sont tellement nombreux (OAuth, TLS, JWS, JWE, OpenID...) qu’on y perd rapidement !

En ce moment j’explore ces solutions et standards et compte partager ici mes notes en espérant qu’elles aideront les plus pressés d’entre vous.

Dans ce premier article je parle brièvement de quelques principes fondamentaux de la sécurité à savoir la confidentialité, l’intégrité, la disponibilité, la non répudiation, l’authentification, l’autorisation et l’audit.

Sans faire trop de chichis rentrons dans le vif du sujet.

La confidentialité

La confidentialité consiste à protéger les données de sorte qu’elles ne soient intelligibles qu’aux destinataires. Dans le cas qui m’intéresse le cas des APIs web la confidentialité peut être obtenue grâce au TLS (a.k.a HTTPs) qui ajoute une protection à la couche de transport du protocole HTTP. De nombreux sites utilisent HTTPs sur l’ensemble de leurs pages et non seulement sur les pages où transitent des informations sensibles comme les mots de passe ou des transactions financières.

L’intégration

L’intégrité consiste à s’assurer que les données n’ont pas été altérées au cours de leur transfert. En général on fait cette vérification avec un code d'authentification des données (MAC). Le protocole TLS, donc HTTPs, permet aussi la vérification de l’intégration des données !

La disponibilité

Toute API a une certaine utilité et s’adresse à un public donné. Elle doit être conçue de sorte qu’elle soit toujours disponible pour ses utilisateurs. La sécurité de l'API consiste aussi à fournir à cette garantie.

La non répudiation

Super importante surtout quand vous faites des transactions business (paiement, consommation de crédit, contraintes légales de traçabilité etc.) avec votre API, la non répudiation permet d’assurer qu’une entité ne peut rejeter (ou répudier) une action qu’elle effectue sur le système. Par exemple une consommation de crédits par un client via l’API ne pourrait être rejetée plus tard. La non répudiation nécessite un tiers de confiance comme une autorité de certification qui procède à la vérification de la transaction. Dans la pratique l’entité qui initie la transaction signe les données et l’autre entité vérifie l’intégrité des données auprès du tiers de confiance avant d’exécuter la transaction.

L’authentification

L’authentification consiste à s’assurer qu’une entité (un utilisateur) est bien qui elle prétend être. En général cela s’accomplit de trois manières différentes : à partir de quelque chose qu'il connait, à partir de quelque qu'il détient et à partir de quelque chose qu'il est ! Voyons ce que signifie chacune de ces trois options.

  • S’authentifier à partir de quelque chose que l’on connaît : C’est peut-être la manière la plus répandue. Il s’agit en fait de demander à l’utilisateur de fournir une information qui permet de l’authentifier. C’est par exemple le cas quand on lui demande un mot de passe sur un site web ou un code PIN pour accéder à un téléphone.
  • S’authentifier à partir de quelque chose que l’on détient : Dans cette forme d’authentification on se sert de quelque chose dont dispose une entité pour l’authentifier. Les cartes à puce et les certificats numériques en sont deux exemples courants.
  • S’authentifier à partir de quelque chose que l’on est : C'est la forme la plus forte car elle se base sur des choses comme la reconnaissance faciale ou rétinienne, les empreintes digitales etc.

Certains système utilisent plus d’une manière (ou facteur) d’authentifier un utilisateur d’où le terme “authentification multi-facteur”.

L’autorisation

L’autorisation, qui sous-entend que l’utilisateur a déjà été authentifié, consiste à s’assurer qu’un utilisateur authentifié ne peut accéder qu’aux ressources ou actions qui lui sont autorisées au sein du système. Par exemple quand vous vous connectez à votre compte Google vous pouvez accéder à vos mails et vos docs mais pas à ceux d’un autre compte Google.

L’audit

Il est important que le système produise des logs qui seront utilisés plus tard pour des besoins d’audit, de détection d’intrusion, d’abus etc. Ces logs doivent également être protégés notamment pour qu’ils ne soient pas altérés par un attaquant qui souhaite dissimuler ses activités illicites :-)).

Pour la route

Voilà pour les concepts de base ! Dans le prochain article j’essaierai d’illustrer mes propos avec du code mais en attendant place à l’exploration...

Lire la suite...

vendredi 7 mars 2014

Utiliser Git derrière un proxy avec authentification

Laisser un commentaire

Si vous utilisez Git derrière un proxy voici comment le configurer afin de pouvoir discuter avec le dépôt distant.

$ git config --global http.proxy http://username:password@proxy-server-domain:port
Cela ajoute une entrée dans le fichier de configuration globale de git : ~/.gitconfig
$ view ~/.gitconfig
....
[http]
        proxy = http://username:password@proxy-server-domain:port
Lire la suite...

mercredi 11 septembre 2013

Les fonctions et les fonctions d’ordre supérieur

5 comments

La programmation fonctionnelle a de nombreux attraits dont le traitement réservé aux fonctions. Dans beaucoup de langages de programmation les fonctions ont un statut particulier. Par exemple en Java il est possible de créer un entier, de l’assigner à une variable ou de le passer comme argument à une autre fonction mais on ne peut pas de même avec les fonctions. En revanche les fonctions jouent un rôle central dans la programmation fonctionnelle et ne sont pas traitées comme des citoyens de seconde zone par les langages dits fonctionnels (Haskell, Caml, Clojure etc.). Cela est important pour l’écriture de certains types d’abstractions bien utiles et courantes en développement comme nous le verrons dans la suite mais avant d’aller plus loin définissons une fonction.

C’est quoi une fonction ?

Ce qu’on entend par fonction varie selon les langages de programmation mais la définition que je vais donner ici est suffisante pour les propos de l’article. Une fonction est une transformation qui produit une valeur à partir d’un ensemble de paramètres, les arguments de la fonction. Les choses seront plus claires avec des exemples. Si nous notons par x un nombre entier quelconque. x peut donc prendre comme valeur 0, 1, 2 etc. nous pouvons définir la fonction f comme suit :

Ne vous laissez pas impressionner par la notation. f est le petit nom donné à la fonction, la lettre x à gauche de la flèche désigne l’argument de la fonction et à droite se trouve sa définition. En donnant une valeur spécifique au paramètre, par exemple 1 nous obtenons :

Si x vaut 3 la valeur retournée par la fonction est :
Au vu de ces valeurs il est aisé de comprendre que cette fonction double la valeur qu’on lui passe en paramètre. Renommons la fonction afin de mieux exprimer cette intention :

Le langage Scala est utilisé dans la suite pour illustrer nos propos. Et la fonction double est codée en Scala comme suit :

def double(number: Int) = 2 * number
Dans le code ci-dessus le mot clé def permet de définir une fonction. Il est suivi du nom de la fonction. J’ai choisi, à la place du paramètre x, un nom plus parlant : number. L’annotation Int (pour Integer) spécifie au compilateur que le paramètre number est un entier. Le reste du code est une traduction parfaite du pseudo code.

Les sorties suivantes de la console interactive (REPL) de Scala permettent de tester notre fonction :

$ scala
scala> def double(number: Int) = 2 * number
double: (number: Int)Int
scala> double(3)
res0: Int = 6
scala> double(1)
res1: Int = 2

Une fonction peut avoir plusieurs arguments par exemple la fonction faisant la somme de deux nombres entiers prend deux arguments. Soient x et y les deux entiers dont nous souhaitons faire la somme, la fonction add est définie comme suit :

Cette définition se traduit aisément en Scala de la façon suivante :
def add(x:Int, y:Int) = x + y
Le test de la fonction donne :
$ scala
scala> def add(x:Int, y:Int) = x + y
add: (x: Int, y: Int)Int
scala> add(1, 2)
res0: Int = 3

Et si nous souhaitions définir une fonction qui incrémente un entier nous pouvons réutiliser la fonction add. En effet incrémenter un entier revient à lui ajouter 1 :

def increment(number: Int) = add(number, 1)
En action cela donne:
$ scala
scala> def increment(number: Int) = add(number, 1)
increment: (number: Int)Int
scala> increment(3)
res1: Int = 4
scala> increment(10)
res2: Int = 11

Note : Une propriété importante de chacune des fonctions que nous avons vues jusque là est le fait que la valeur de retour est entièrement déterminée par les arguments de la fonction.

Munis de ces informations passions aux fonctions d’ordre supérieur.

Les fonctions d’ordre supérieur

Une fonction d’ordre supérieur est une fonction qui a au moins l’une des deux caractéristiques suivantes :

  • prend en paramètre une ou plusieurs fonctions
  • sa valeur de retour est une fonction

Les fonctions d’ordre supérieur aident à l’expressivité du code en mettant l’attention sur la tâche à accomplir plutôt que sur la façon de l’accomplir : le code dévient ainsi plus déclaratif. Cela devient plus clair avec un exemple. Considérons la classe Person ci-dessous :

public class Person {
 private final String name;
 private final String email;
 private final int age;

 public Person(String name, String email, int age) {
  this.name = name;
  this.email = email;
  this.age = age;
 }

 public String getName() {
  return name;
 }

 public String getEmail() {
  return email;
 }

 public int getAge() {
  return age;
 }
}
Supposons que vous disposez d’une liste d’objets de type Person sur laquelle vous souhaitez faire différentes opérations : transformation, filtrage etc. Commençons par la transformation de la liste.

Transformation des éléments d’une collection

Voici la liste initiale :

List<Person> persons = Arrays.asList(//
    new Person("Toto", "toto@email.com", 23), //
    new Person("John Doe", "john.doe@email.com", 34), //
    new Person("Mad Max", "mad.max@crazymail.com", 20), //
    new Person("Jane Doe", "jane@email.com", 17));
La première transformation qui nous intéresse est l’extraction des adresses email à partir de la liste des personnes. C’est ce que fait la méthode extractEmails :
public static List<String> extractEmails(List<Person> persons) {
 List<String> emails = new ArrayList<String>();

 for (Person person : persons) {
  emails.add(person.getEmail());
 }

 return emails;
}
Testons ce que cela donne :
System.out.println("Emails = " + extractEmails(persons));
Sortie de la console :
Emails = [toto@email.com, john.doe@email.com, mad.max@crazymail.com, jane@email.com]
On obtient une liste ayant la même taille que la liste des personnes mais ne contenant que les adresses email. Le code de la méthode extractEmails recèle quelques défauts dont la “dilution” de l’intention de la méthode dans du code verbeux.

Avant de voir comment résoudre ces défauts implémentons une autre fonctionnalité : extraire les noms à partir de la liste des personnes :

public static List<String> extractNames(List<Person> persons) {
 List<String> names = new ArrayList<String>();

 for (Person person : persons) {
  names.add(person.getName());
 }

 return names;
}

Cette méthode fait ressortir un autre défaut de notre code : la duplication. Les deux méthodes ne diffèrent véritablement que par la transformation effectuée à chaque itération sur l’élément de la liste initiale. Cette transformation peut être matérialisée par une fonction f qui prend comme argument un objet de type Person et retourne son nom ou son adresse email. Ainsi l’implémentation de l’une ou l’autre de nos fonctions se résume comme suit : "applique la fonction f à chaque élément de la liste initiale et renvoie-moi la liste correspondante."

Nous pouvons schématiser cette phrase de la façon suivante, avec à gauche la liste initiale et à droite la liste obtenue suite à la transformation :

Cela correspond exactement à ce que fait la fonction map. La figure ci-dessous donne le fonctionnement de la méthode map présente sur la classe List de Scala.

Comme mentionné sur la figure la fonction map prend en paramètre une fonction, celle que nous souhaitons appliquer à chaque élément de la liste initiale. Dans notre cas la fonction a comme type Person => String. Nous allons maintenant réimplémenter nos deux méthodes en utilisant la méthode map disponible dans l'API des listes Scala.

Les fonctions extractEmails et extractNames revisitées

Définition de la classe Person :

case class Person(name: String, email: String, age: Int)
Nous définissons d’abord la fonction qui extrait une adresse email à partir d’une instance de Person :
     
def extractEmail(person: Person) = person.email
Puis nous passons cette fonction à la méthode map de la liste afin de récupérer la liste des adresses email.
def extractEmails(persons: List[Person]): List[String] = persons.map(extractEmail)
Sur le même principe voici la définition de la fonction extractNames :
     
def extractName(person: Person) = person.name
def extractNames(persons: List[Person]): List[String] = persons.map(extractName)
Note : Grâce aux fonctions anonymes nous pouvons nous passer des fonctions extractEmail et extractName qui n’ont pas grand intérêt.
   
def extractNames(persons: List[Person]): List[String] = persons.map(p => p.name)
def extractEmails(persons: List[Person]): List[String] = persons.map(p => p.email)

Qu'avons-nous gagné par rapport à avant ? Le code est plus expressif en éliminant le bruit et en mettant l’accent sur l’intention du code : "mapper chaque élément de la liste sur son application à la fonction que nous passons à la méthode map". Les fonctions d’ordre supérieur rendent pratique une autre opération courante sur les collections : le filtrage.

Filtrer une collection

Nous disposons d’une liste de nombres entiers allant de 1 à 15 et nous souhaitons filtrer cette liste en éliminant tous les éléments qui ne sont pas des multiples de 3. Ce genre d’opération est résolu en programmation fonctionnelle avec la fonction d’ordre supérieur filter. Elle prend en arguments une liste et un prédicat, une fonction qui retourne "vrai" ou "faux". Dans notre exemple le prédicat retourne “vrai” si son argument est un multiple de 3 et "faux" dans le cas contraire. Scala étant un langage orienté-objet filter est une méthode de List. Voici la définition du prédicat :

scala> def isMultipleOfThree(number: Int) = number % 3 == 0
isMultipleOfThree: (number: Int)Boolean
scala> isMultipleOfThree(2)
res0: Boolean = false
scala> isMultipleOfThree(6)
res1: Boolean = true
Et la liste de nombres :
scala> val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
numbers: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
Le filtrage s’effectue simplement en passant la fonction isMultipleOfThree à la méthode filter de la liste de nombres :
scala> val multiplesOfThree = numbers.filter(isMultipleOfThree)
multiplesOfThree: List[Int] = List(3, 6, 9, 12, 15)
Ceci peut être réécrit en remplaçant isMultipleOfThree par une fonction anonyme :
scala> numbers.filter(number => number % 3 == 0)
res2: List[Int] = List(3, 6, 9, 12, 15)
Nous allons maintenant combiner filter et map.

Combiner filter et map

Nous souhaitons obtenir la somme des doubles des multiples de 3 compris entre 1 et 15. Cela correspond à une opération de filtrage (suppression des nombres non multiples de 3) suivie d’une transformation des éléments (doublement des éléments des éléments restants) et enfin une opération de réduction (la sommation). On y arrive aisément avec le code suivant :

scala> val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
numbers: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
scala> numbers.filter(number => number % 3 == 0).map(number => number * 2).sum
res7: Int = 90
Il nous reste une dernière fonction d’ordre supérieur à découvrir avant de conclure cet article : la fonction fold.

La fonction fold, le couteau suisse

La fonction fold est un véritable couteau suisse. Elle réduit les éléments d’une liste en une valeur. Cette fonction existe en deux versions : l’une (foldLeft) parcourt la liste de la gauche vers la droite et l’autre de la droite vers la gauche (foldRight). Nous allons plutôt utiliser la première dont voici la signature :

def foldLeft[B](z: B)(f: (B, A) ⇒ B): B
C’est une fonction curryfiée. Elle prend une valeur initiale de type B, désignée par z et une fonction f prenant deux arguments de types A (le type des éléments de la liste) et B. Pour chaque élément de la liste la fonction f est invoquée. f est invoquée avec la valeur initiale z fournie à la méthode foldLeft. Puis la valeur résultante est passée avec le deuxième élément de la liste à la fonction f. Ce processus continue avec tous les éléments de la liste. Pensez à lire cet article si vous souhaitez en savoir davantage. Place à l’action ! Afin de comprendre comment fonctionne foldLeft nous allons résoudre trois petits problèmes. Le premier problème consiste à faire la somme des éléments d’une liste d’entiers. La valeur initiale dans le cas de la somme est 0 (l’élément neutre de l’addition) et la fonction f additionne ces deux paramètres. Voici ce que cela donne dans le REPL :
scala> val nums = List(1, 2, 3, 4)
nums: List[Int] = List(1, 2, 3, 4)
scala> val sum = nums.foldLeft(0)((acc, num) => acc + num)
sum: Int = 10     
scala> val nums2 = List(1, 3, 5, 7)
nums2: List[Int] = List(1, 3, 5, 7)
scala> val sum2 = nums2.foldLeft(0)((acc, num) => acc + num)
sum2: Int = 16
Simple non ?! Ne nous arrêtons pas en si bon chemin continuons sur le deuxième problème : faire le produit des éléments d'une liste. La valeur initiale passée à foldLeft dans ce cas est l’élément neutre de la multiplication à savoir 1. Quant à la fonction à passer foldLeft elle fait le produit de ces deux paramètres.
     
scala> val nums = List(1, 2, 3, 4)
nums: List[Int] = List(1, 2, 3, 4)
scala> val prod = nums.foldLeft(1)((acc, num) => acc * num)
prod: Int = 24
scala> val nums2 = List(1, 3, 5, 7)
nums2: List[Int] = List(1, 3, 5, 7)
scala> val prod2 = nums2.foldLeft(1)((acc, num) => acc * num)
prod2: Int = 105
So far so good! Maintenant résolvons le dernier exercice pour la route. Il s’agit de construire à partir d’une liste d’entiers une map dont les clés sont les éléments pairs de la liste et les valeurs leurs doubles. Voyons un exemple afin de clarifier les choses :
List(7, 2, 10, 3, 6) -> Map(2 -> 4, 10 -> 20, 6 -> 12)
La valeur initiale est une map vide. La fonction passée à foldLeft prend une map et un entier. Si l’entier est pair son double est mappé sur sa valeur. La fonction retourne la map résultante.
    
scala> val nums = List(1, 2, 3, 4)
nums: List[Int] = List(1, 2, 3, 4)

scala> val evenDoubled = nums.foldLeft(Map[Int, Int]()) { (acc, num) =>
| if (num % 2 == 0) acc + ((num, 2 * num)) else acc
| }
evenDoubled: scala.collection.immutable.Map[Int,Int] = Map(2 -> 4, 4 -> 8)

Pour conclure

Nous arrivons à la fin de cette introduction aux fonctions d’ordre supérieur. Elles sont particulièrement adaptées aux traitements sur des collections. Mais leur usage va au-delà des collections. Consultez la liste de références ci-dessous pour aller loin.

Références

Lire la suite...

lundi 9 septembre 2013

[Intro à Scala 2] Premiers pas avec Scala

Laisser un commentaire

Après une introduction générale à Scala et à son écosystème dans le premier article, nous allons installer Scala puis écrire nos premières lignes de code Scala afin de nous familiariser avec le REPL.

Télécharger Scala

Pour fonctionner Scala a besoin de Java. Vous devez donc vous assurer que Java est installé sur votre machine avant de procéder à l’installation de Scala. Dans cette série d’articles nous utiliserons la version 2.10.2 de Scala et cela requiert au moins la version 1.6 de Java. Commencez d’abord par vérifier la disponible de Java sur votre système avec la commande suivante :

$ java -version

En exécutant cette commande sur ma machine j’obtiens la sortie suivante :

$ java -version
java version "1.6.0_51"
Java(TM) SE Runtime Environment (build 1.6.0_51-b11-457-11M4509)
Java HotSpot(TM) 64-Bit Server VM (build 20.51-b01-457, mixed mode)

Vous devez avoir une sortie similaire si Java est installé sur votre machine sinon rendez-vous à l’adresse http://www.java.com pour le télécharger.

Maintenant que vous avez installé Java procédons à l’installation de Scala. En fonction de votre plate-forme elle ne se fera pas de la même manière.

Windows

Allez à l’adresse http://www.scala-lang.org/download/2.10.2.html et cliquez sur le lien nommé scala-2.10.2.msi. Cela va télécharger un fichier qui permet d’installer Scala sur votre machine Windows. Une fois le téléchargement terminé il suffit de cliquer dessus et de suivre les différentes étapes de l’installation. Une fois l’installation terminée vous pouvez vérifier si cela a marché avec la commande :

$ scala -version

Vous obtiendrez une sortie similaire à :

$ scala -version
Scala code runner version 2.10.2 -- Copyright 2002-2013, LAMP/EPFL

Unix/Linux, Mac OS X et Cygwin

Téléchargez le fichier archive disponible à l’adresse http://www.scala-lang.org/files/archive/scala-2.10.2.tgz puis extrayez-le à l’endroit de votre choix. Sur ma machine je vais l’installer dans /usr/local/share.

$ cd /usr/local/share/
$ sudo wget http://www.scala-lang.org/files/archive/scala-2.10.2.tgz

Une fois le téléchargement terminé j’extrais les fichiers :

$ tar xvfz scala-2.10.2.tgz

Histoire de ne pas lier mon installation à une version particulière je crée un lien symbolique vers le dossier scala-2.10.2.

$ sudo ln -s /usr/local/share/scala-2.10.2 /usr/local/share/scala

Ensuite éditez par exemple votre fichier .bashrc pour lui ajouter les lignes suivantes :

SCALA_HOME=/usr/local/share/scala
export PATH=$PATH:$SCALA_HOME/bin

Enfin vous pouvez vérifier l’installation comme suit :

$ source ~/.bashrc
$ scala -version
Scala code runner version 2.10.2 -- Copyright 2002-2013, LAMP/EPFL

Que contient l’installation de Scala ?

Faisons le tour du propriétaire pour comprendre ce que contient notre installation de Scala. Pour cela je me place dans le dossier où est installé Scala puis je lance la commande tree :

$ cd /usr/local/share/scala
$ tree -d -L 2

Cela me donne la structure suivante :

$ tree -d -L 2
.
├── bin
├── doc
│   └── tools
├── examples
│   ├── actors
│   ├── monads
│   ├── parsing
│   ├── tcpoly
│   └── xml
├── lib
├── man
│   └── man1
├── misc
│   └── scala-devel
└── src

15 directories

Voici une brève description des dossiers ci-dessus :

  • bin : Ce dossier contient les programmes exécutables comme le compilateur de Scala scalac, le dé-compilateur scalap ou l'interpréteur scala. Lors de l’installation ce dossier a été ajouté au chemin où le système d’exploitation cherche les commandes que nous tapons dans le terminal. Ainsi nous n’avons pas besoin de taper le chemin complet des exécutables.
  • doc : Ce dossier contient de la documentation sur les commandes localisées dans le repertoire bin.
  • examples : Ici vous trouverez des exemples de programmes écrits en Scala. Faites-y un tour afin de voir à quoi ressemble la syntaxe de Scala ou pour trouver de l’inspiration.
  • lib : C’est là que résident les librairies c’est-à-dire un ensemble de fonctions et de classes prêtes à l’emploi que vous pouvez utiliser pour écrire vos propres programmes.
  • man : Le contenu de ce dossier est utilisé par la commande man qui affiche l’aide sur des commandes comme scala ou scalac. Essayez ça :
    $ man scala
    
  • src : Le code source de Scala et des librairies se trouvent ici.
  • misc : Ce dossier est, j’ai envie de dire, sans grand intérêt pour nous. A ce jour il contient le plugin pour la contituation.

Premiers pas avec l’interpréteur de Scala ou le REPL

Comme vous allez vous en rendre l’interpréteur de Scala est un outil puissant pour expérimenter des choses et accélérer son apprentissage du langage. Vous avez un doute et hop lancez l’interpréteur pour le lever ! Vous avez une idée et hop l’interpréteur est là pour la tester.

Et pour lancer l’interpréteur rien de plus simple que de taper la commande scala dans une console :

$ scala
Welcome to Scala version 2.10.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_51).
Type in expressions to have them evaluated.
Type :help for more information.
scala>

Vous voilà dans l’interpréteur prêt à taper des commandes. Tapez un nombre et appuyez sur la touche Enter du clavier pour voir.

scala> 20
res0: Int = 20

Le nombre 20 sur la première ligne correspond à ce que j’ai tapé et la deuxième ligne à ce que me répond l’interpréteur. Que s’est-il passé ? L’interpréteur a récupéré ce que j’ai tapé (ici 20), l’a évalué puis a affiché le résultat en lui donnant le nom res0 (pour résultat numéro 0) et la valeur 20. Désormais res0 contient le nombre 20. Pour vous en convraincre tapez res0 dans l’interpréteur :

scala> res0
res1: Int = 20

Note : Notez que le nom res0 est suivi de “: Int”. Cette annotation nous précise que le nom res0 contient un entier. Nous reviendrons sur ce point dans la suite.

Recommençons avec une opération un peu plus compliquée : l’addition de deux nombres, par exemple 2 + 7.

scala> 2 + 7
res2: Int = 9

Là encore le REPL a évalué ce que nous avons tapé puis a affiché le résultat.

Il est aussi possible de réutiliser le résultat d’une opération précédente :

scala> res2 + 2
res3: Int = 11
scala> res3 - 8
res4: Int = 3

En dehors du code Scala l’interpréteur de Scala accepte aussi d’autres commandes dont la plus importante est :help. Elle vous listera la liste des commandes disponibles. Pour l’instant je mets l’accent sur trois commandes seulement dont voici la description :

  • :cp : Cette commande vous permet d’utiliser depuis le REPL du code situé dans un fichier archive Java (avec l’extension .jar) ou dans un répertoire.
  • :quit : Celle-là est utilisée pour quitter le REPL. Eh oui il faut pouvoir en sortir !
  • :paste : Cette commande s’avère assez pratique quand on écrit du code sur plusieurs lignes dans le REPL.

Même si le REPL est symphatique comme calculatrice elle peut faire bien plus comme afficher un message sur l’écran par exemple.

scala> println("Bonjour ! Ça se passe bien jusque là ?")
Bonjour ! Ça se passe bien jusque là ?

Essayez la même chose chez vous, n’hésitez pas à changer le message. Ici nous avons utilisé la fonction println pour afficher notre message à l’écran. Nous verrons dans la suite ce qu’est une fonction mais si vous êtes pressé de le savoir lisez cet article.

Résumé et programme pour la suite

Mine de rien nous avons déjà fait beaucoup de choses. Nous avons installé Scala, appris à lancer le REPL et à le faire exécuter du code Scala pour nous. Dans le prochain article nous nous baserons sur ces connaissances pour apprendre de nouveaux concepts et nous initier à la syntaxe de Scala.

Lire la suite...