tag_cfpAprès la présentation fonctionnelle, voyons un peu ce qu’il y a dans le moteur du CFP

L’architecture technique

Le CFP est une application Web, hébergé comme nous le verrons sur une plateforme type PaaS. Il a été développé en Scala, avec le framework Web Play Framework 2.2. Les données principales de l’application sont stockées sur Redis 2.8. ElasticSearch 0.90 est utilisé côté backoffice pour trouver rapidement un sujet, un speaker ou un événement particulier. Kibana a été utilisé pour fournir quelques statistiques sur le nombre de sujets, la répartition par track, le speaker le plus populaire, etc. Le code est hébergé sur Bitbucket. Il n’est pas public pour l’instant, nous pensons le mettre librement accessible plus tard, après Devoxx Belgique.

mind mapping software

 

Les systèmes externes

L’envoi des emails

Comme le montre le schéma, j’utilise différents systèmes externes pour les besoins de l’application. Tout d’abord l’envoi d’emails. En 2014, lorsque l’on développe une application Web, en général celle-ci n’expédie pas d’emails directement à vos utilisateurs.

Pourquoi ? Pour des problèmes de délivrabilité, sans même parler du besoin d’avoir un serveur SMTP correctement configuré.

Le CFP utilise Mailjet, une excellente solution d’emailing dans le Cloud. Cocorico, Mailjet est une société Française. Avec 23 000 clients, j’ai plus confiance dans le savoir faire de Mailjet, que dans celui de mon administrateur système. Surtout si je vous parle SPF et DKIM, ça ressemble déjà plus à du chinois non ?

SPF (Sender Policy Framework) est un moyen d’améliorer la délivrabilité d’un email, en ajoutant une entrée type TXT sur votre enregistrement DNS :

Sender Policy Framework (SPF) is a validation system that allows ISPs and webmail servers (Gmail, Yahoo, etc) to check if the incoming mail has been sent from an authorized server. Using the IP address of the sending server and the DNS records of your domain, ISPs can check if the sending server is authorized. If email is coming from an unauthorized sender, the emails will be marked as spam! (source)

Et DKIM ?

Domain Keys Identified Email est un système simple qui permet de signer vos emails, et de partager une clé publique via une entrée DNS. La documentation de Mailjet l’explique très bien. Tout ceci permet de s’assurer que les emails du CFP n’atterrissent pas dans le dossier « Spam » des speakers. Ce serait ennuyant. Retenons simplement qu’il existe un bon nombre de services Webs, et que parfois, il est plus intéressant d’utiliser ce type de service, plutôt que de se lancer dans de l’administration système. Mailjet, 23 000 clients contre Nicolas, pas 23 000 clients. Si ce type de sujet vous intéresse, j’en avais parlé dans l’article « Les secrets des Architectes Webs »

L’authentification via Github ou Google+

En tant que speaker, devoir créer un profil et se souvenir d’un n-ième mot de passe… bof bof. Pour gagner du temps, et offrir une meilleure expérience utilisateur, nous avons intégré l’authentification OAuth 2.0 via Github et Google.

Ces 2 fournisseurs nous donnent avec votre accord pas mal d’informations : prénom, nom, email, photo, bio, etc. Simple à coder avec Play2, cela ne demande pas de framework externe. Le code tient sur une vingtaine de ligne. Tout d’abord, nous devons définir une action afin de rediriger le visiteur vers le fournisseur, ici Github

  
def githubLogin = Action {
    implicit request =>
      Play.current.configuration.getString("github.client_id").map {
        clientId: String =>
          val redirectUri = routes.Authentication.callbackGithub.absoluteURL()
          val gitUrl = "https://github.com/login/oauth/authorize?scope=user:email&client_id=" + clientId + "&state=" + SecureCFP.generateState() + "&redirect_uri=" + redirectUri
          Redirect(gitUrl)
      }.getOrElse {
        InternalServerError("github.client_id is not set in application.conf")
      }
  }

Ceci redirige le visiteur vers Github, qui demande alors la permission d’envoyer certaines informations (user et email) au CFP de Devoxx France. Si vous acceptez, vous êtes alors redirigé vers le CFP, avec un code temporaire et le state, qui permet d’éviter les attaques type CSRF.

L’application doit alors échanger ce code temporaire avec un token d’accès, ce que je fais via un appel asynchrone, côté serveur, directement avec Play2 :

def callbackGithub = Action.async {
    implicit request =>
      oauthForm.bindFromRequest.fold(invalidForm => {
        Future.successful(BadRequest(views.html.Application.home(invalidForm)).flashing("error" -> "Invalid form"))
      }, {
        case (code, state) if state == SecureCFP.getState() => {
          val auth = for (clientId <- Play.current.configuration.getString("github.client_id");
                          clientSecret <- Play.current.configuration.getString("github.client_secret")) yield (clientId, clientSecret)
          auth.map {
            case (clientId, clientSecret) => {
              val url = "https://github.com/login/oauth/access_token"
              val wsCall = WS.url(url).post(Map("client_id" -> Seq(clientId), "client_secret" -> Seq(clientSecret), "code" -> Seq(code)))
              wsCall.map {
                result =>
                  result.status match {
                    case 200 => {
                      val b = result.body
                      try {
                        val accessToken = b.substring(b.indexOf("=") + 1, b.indexOf("&"))
                        Redirect(routes.Authentication.createFromGithub()).withSession("access_token" -> accessToken)
                      } catch {
                        case e: IndexOutOfBoundsException => {
                          Redirect(routes.Application.index()).flashing("error" -> "access token not found in query string")
                        }
                      }
                    }
                    case _ => {
                      Redirect(routes.Application.index()).flashing("error" -> ("Could not complete Github OAuth, got HTTP response" + result.status + " " + result.body))
                    }
                  }
              }
            }
          }.getOrElse {
            Future.successful(InternalServerError("github.client_secret is not configured in application.conf"))
          }
        }
        case other => Future.successful(BadRequest(views.html.Application.home(loginForm)).flashing("error" -> "Invalid state code"))
      })
  }

Retenez que l’authentification OAuth 2.0 permet à vos visiteurs de s’authentifier, sans devoir créer un profil, et devoir retenir un autre mot de passe, pour votre site.

 

L’hébergement

La nuit je dors.

Et pendant Devoxx France, j’ai super bien dormi.
Pas de plantage, rien à toucher, et vraiment, un site qui tourne comme une horloge.

Le secret ? Clever-Cloud. Cocorico c’est aussi Français.

Clever-Cloud est une solution d’hébergement type PaaS, Platform as a Service, à la Heroku, mais en mieux. Au moment où j’écris ces lignes, la partie Redis et ElasticSearch est encore en beta, et j’ai eu la chance de pouvoir m’en servir, en avant première. Et bien ça tourne très bien. Rien à redire.

Combien cela coûte-t-il ?

Environ 1.80 EUR par jour. Cependant, pendant la conférence, nous sommes montés à 6 EUR par jour. Mazette…. la conférence m’a couté 24 EUR d’hébergement pendant les 4 jours où cela a bien tiré sur les ressources… C’est en fait un prix vraiment bas, étant donné que le service a super bien tenu, sans aucuns soucis.

Plus sérieusement donc : c’est une solution économique, qui scale automatiquement. Attention avec Play2, ne prenez pas un Scaler trop petit, sinon votre application ne démarre pas. Je tourne avec un scaler type S (Small) avec 1Gb de ram, 2 CPU, qui doit couter environ 28 EUR par mois. Oui je sais, pour ce prix chez OVH, vous avez un petra-gigahertz et 250Gb… mais vous devez alors tout faire vous même. Et je doute que vous soyez super-ninja en administration système. Si je vous dis fail2ban, firewall, patch open-ssl, ça vous parle ?

En quoi le PaaS est-il intéressant ? Le déploiement est ultra-simple, et la plateforme est auto-scalable. La mise en prod d’une version  s’effectue en effectuant un simple git push. La partie auto-scalable se configure, vous pouvez ne pas vous en servir, ou laisser la plate-forme gérer pour vous votre bébé.

Autorisation et gestion des droits d’accès

Je vous propose de continuer dans le prochain article, intitulé « Play2, autorisation, authentification et compagnie« 

3 réflexions sur « Architecture technique du CFP »

  1. J’avais envie de faire plus léger, de maitriser l’authentification et « l’accueil » des speakers en permettant de créer son profil
    Si le module est intéressant, je pense que je n’aurai pas utilisé plus de 40% de ses fonctionnalités

  2. Bonjour Nicolas,

    Quand tu dis \ »Clever-Cloud est une solution d’hébergement type PaaS, Platform as a Service, à la Heroku, mais en mieux\ ».
    Tu peux nous dire les avantages de Clever-Cloud par rapport à Heroku.
    Merci.

Les commentaires sont fermés.