Framework de Programmation des Serveurs Web

Initiation à Ruby on Rails (RoR)

Olivier Dalle (@unice.fr)

Université de Nice Sophia Antipolis

Modélisation des utilisateurs


Modélisation des utilisateurs


Modélisation des utilisateurs


Modélisation des utilisateurs


Modélisation des utilisateurs


Modélisation des utilisateurs


Chapitres suivants:
7- Enregistrement/Création compte
8- Connexion/deconnexion

A propos d’Authentification


Deux approches:

A propos d’Authentification


Deux approches:

A propos d’Authentification


Deux approches:

A propos d’Authentification


Deux approches:

A propos d’Authentification


Deux approches:

A propos d’Authentification


Deux approches:

Dans ce cours on cherche à bien comprendre, donc …

A propos d’Authentification


Deux approches:

Dans ce cours on cherche à bien comprendre, donc on le fera nous-même!

Le formulaire d’enregistrement


On utilise un mockup…

Le formulaire d’enregistrement

Modèle de données en Rails


Modèle de données en Rails


Modèle de données en Rails


Modèle de données en Rails


Modèle de données en Rails


Modèle de données en Rails


Modèle de données en Rails


Abstraction totale de la BDDR.

Expliquons les migrations


Nous avons esquissé un modèle d’utilisateur en Ruby (vu au chap. 4):

class User
  attr_accessor :name, :email
  .
  .
  .
end

Expliquons les migrations


En Rails, on ne parle plus d’__attributs__ mais de colonnes dans une table.

→ On crée un modèle, et Rails se charge de définir les attributs:

Expliquons les migrations


En Rails, on ne parle plus d’ attributs mais de colonnes dans une table.

→ On crée un modèle, et Rails se charge de définir les attributs utiles:

$ rails generate model User name:string email:string
    invoke  active_record
    create    db/migrate/[timestamp]_create_users.rb
    create    app/models/user.rb
    invoke    rspec
    create      spec/models/user_spec.rb

Expliquons les migrations


Le premier produit de cette génération est la migration, dans db/migrate/[timestamp]_create_users.rb

Expliquons les migrations


Le premier produit de cette génération est la migration, dans db/migrate/[timestamp]_create_users.rb:

Expliquons les migrations


Le premier produit de cette génération est la migration, dans db/migrate/[timestamp]_create_users.rb

Expliquons les migrations


Le premier produit de cette génération est la migration, dans db/migrate/[timestamp]_create_users.rb:

Expliquons les migrations


Le modèle de données produit par cette migration:

Expliquons les migrations


Le modèle de données produit par cette migration:

Exécution de la migration:

$ bundle exec rake db:migrate

Inspection de la BDDR


On utilise l’outil sqlite browser:

apt-get install sqlite-browser

Inspection de la BDDR


On utilise l’outil sqlite browser:

apt-get install sqlite-browser

Annulation d’une migration


Il est possible d’annuler une migration.

Annulation d’une migration


Il est possible d’annuler une migration. Ou plusieurs, en cascade.

Annulation d’une migration


Il est possible d’annuler une migration. Ou plusieurs, en cascade.

$ bundle exec rake db:rollback

Annulation d’une migration


Il est possible d’annuler une migration. Ou plusieurs, en cascade.

$ bundle exec rake db:rollback

On peut aussi re-appliquer une migration annulée…

$ bundle exec rake db:migrate

Le fichier du modèle (généré)


class User < ActiveRecord::Base
  attr_accessible :name, :email
end

Annotation du modèle


Ajout du gem annotate dans le Gemfile:

(Gemfile) gem 'annotate', '2.5.0', group: :development

Annotation du modèle


Ajout du gem annotate dans le Gemfile:

(Gemfile) gem 'annotate', '2.5.0', group: :development

Puis execution de la commande:

$ bundle exec annotate

Annotation du modèle


Résultat de l’annotation:

Jouons avec les object User


$ rails console --sandbox
/home/l3rails/.rvm/rubies/ruby-1.9.3-p286/lib/ruby/1.9.1/irb/
completion.rb:9:in `require': cannot load such file -- readli
ne (LoadError)

Il faut installer la librairie readline avec rvm:

$ sudo apt-get install  libncurses5-dev libreadline-gplv2-dev
$ rvm pkg install readline --with-opt-dir=$HOME/.rvm/usr \
 --verify-downloads 1

Puis recompiler ruby:

$ rvm reinstall all --force

Jouons avec les object User


$ rails console --sandbox

Loading development environment in sandbox Any modifications you make will be rolled back on exit >>

Jouons avec les object User


>> User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> 

Création puis sauvegarde

>> user = User.new(name: “Michael Hartl”, email: “mhartl@example.com”)
=> #<User id: nil, name: “Michael Hartl”, email: “mhartl@example.com”,
created_at: nil, updated_at: nil> 

>> user.save
=> true 

Jouons avec les object User


>>>> user
=> #<User id: 1, name: “Michael Hartl”, email: “mhartl@example.com”,
created_at: “2011-12-05 00:57:46”, updated_at: “2011-12-05 00:57:46”> 
>> user.name
=> “Michael Hartl”
>> user.email
=> “mhartl@example.com”
>> user.updated_at
=> Tue, 05 Dec 2011 00:57:46 UTC +00:00 

Jouons avec les object User


Création/sauvegarde en 1 seule opération:

>> User.create(name: “A Nother”, email: “another@example.org”)
#<User id: 2, name: “A Nother”, email: “another@example.org”, created_at:
“2011-12-05 01:05:24”, updated_at: “2011-12-05 01:05:24”> 
>> foo = User.create(name: “Foo”, email: “foo@bar.com”)
#<User id: 3, name: “Foo”, email: “foo@bar.com”, created_at: “2011-12-05
01:05:42”, updated_at: “2011-12-05 01:05:42”> 

Jouons avec les object User


Le contraire de create …

>> foo.destroy
=> #<User id: 3, name: “Foo”, email: “foo@bar.com”, created_at: “2011-12-05
01:05:42”, updated_at: “2011-12-05 01:05:42”> 

Détruit l’objet dans le BDDR …

>> foo
=> #<User id: 3, name: “Foo”, email: “foo@bar.com”, created_at: “2011-12-05
01:05:42”, updated_at: “2011-12-05 01:05:42”> 

Mais pas en mémoire!

Recherche avec Active Record


>> User.find(1)
=> #<User id: 1, name: “Michael Hartl”, email: “mhartl@example.com”,
created_at: “2011-12-05 00:57:46”, updated_at: “2011-12-05 00:57:46”> 

Recherche avec Active Record


>> User.find(1)
=> #<User id: 1, name: “Michael Hartl”, email: “mhartl@example.com”,
created_at: “2011-12-05 00:57:46”, updated_at: “2011-12-05 00:57:46”> 
>> User.find(3)
ActiveRecord::RecordNotFound: Couldn’t find User with ID=3 

Recherche avec Active Record


>> User.find(1)
=> #<User id: 1, name: “Michael Hartl”, email: “mhartl@example.com”,
created_at: “2011-12-05 00:57:46”, updated_at: “2011-12-05 00:57:46”> 
>> User.find(3)
ActiveRecord::RecordNotFound: Couldn’t find User with ID=3 

Car détruit par destroy.

Nombreuses fonctions AR


>> User.first
=> #<User id: 1, name: “Michael Hartl”, email: “mhartl@example.com”,
created_at: “2011-12-05 00:57:46”, updated_at: “2011-12-05 00:57:46”> 

>> User.all
=> [#<User id: 1, name: “Michael Hartl”, email: “mhartl@example.com”,
created_at: “2011-12-05 00:57:46”, updated_at: “2011-12-05 00:57:46”>,
#<User id: 2, name: “A Nother”, email: “another@example.org”, created_at:
“2011-12-05 01:05:24”, updated_at: “2011-12-05 01:05:24”>] 

Mises a jour


>> user           # Just a reminder about our user’s attributes
=> #<User id: 1, name: “Michael Hartl”, email: “mhartl@example.com”,
created_at: “2011-12-05 00:57:46”, updated_at: “2011-12-05 00:57:46”>
>> user.email = “mhartl@example.net”
=> “mhartl@example.net”
>> user.save
=> true 

Les colonnes magiques …


>> user.created_at
=> “2011-12-05 00:57:46”
>> user.updated_at
=> “2011-12-05 01:37:32” 

Mise a jour groupees


>> user.update_attributes(name: “The Dude”, email: “dude@abides.org”)
=> true
>> user.name
=> “The Dude”
>> user.email
=> “dude@abides.org” 

Validations


Permettent de valider les entrées dans le formulaires.

Validations


Permettent de valider les entrées dans le formulaires.

Exemple:

Validations


Permettent de valider les entrées dans le formulaires.

Exemple:

Validations


Permettent de valider les entrées dans le formulaires.

Exemple:

Validation de présence


On ajoute une instruction à la classe User:

class User < ActiveRecord::Base 
  attr_accessible :name, :email

validates :name, presence: true

end

Validation de présence


Vérifions à la console:

>> user = User.new(name: "", email: “mhartl@example.com”)
>> user.save
=> false
>> user.valid?
=> false

Mise à jour automatique d’un mesage d’erreur


>> user.errors.full_messages
=> [“Name can’t be blank”]

Mise en place d’un test d’echec


On commence par commenter pour faire échouer:

Mise en place d’un test d’echec

Mise en place d’un test d’echec


  1. Vérifier que le test échoue

Mise en place d’un test d’echec


  1. Vérifier que le test échoue
  2. Supprimer commentaire

Mise en place d’un test d’echec


  1. Vérifier que le test échoue
  2. Supprimer commentaire
  3. Vérifier que le test passe

Validation de longueur


D’abord le test:

Validation de longueur


Ensuite la validation:

Validation de format


D’abord le test

Validation de format

Validation de format


Ensuite la validation:

Validation d’unicite


D’abord le test

Validation d’unicite

Validation d’unicite


Ensuite la validation:

Validation d’unicite


Ensuite la validation, insensible à la casse:

Pièges de la validation d’unicite


Pièges de la validation d’unicite


Pièges de la validation d’unicite


Solutions?

Pièges de la validation d’unicite


Solutions?

Unicité au niveau de la BDD


Utilisation d’un index:

$ rails generate migration add_index_to_users_email

Unicité au niveau de la BDD


Utilisation d’un index:

$ rails generate migration add_index_to_users_email

Pièges de la validation d’unicite


Que se passe-t-il si on joue avec Majuscules/minuscules?

Pièges de la validation d’unicite


Que se passe-t-il si on joue avec Majuscules/minuscules?
→ Conversion auto en minuscules:

Ajout d’un mot de passe securisé


Sécurisé comment?

Ajout d’un mot de passe securisé


Sécurisé comment?

Ajout d’un mot de passe securisé


Sécurisé comment?

Ajout d’un mot de passe securisé


Sécurisé comment?

Ajout d’un mot de passe securisé


Sécurisé comment?

→ Personne ne peut intercepter le mdp original

Ajout d’un mot de passe securisé


Sécurisé comment?

→ Personne ne peut intercepter le mdp original

Vulnérabilité(s)?

Ajout d’un mot de passe securisé


Sécurisé comment?

→ Personne ne peut intercepter le mdp original

Vulnérabilité(s)? Oui, attaques par dictionnaire toujours possibles. Man in the middle?..

Ajout d’un mot de passe a la structure de données


Ajout d’une colonne password_digest

Ajout d’un mot de passe a la structure de données


Ajout d’une colonne password_digest
Attn: Le nom est imposé

Ajout d’un mot de passe a la structure de données


Ajout d’une colonne password_digest
Attn: Le nom est imposé

Ajout d’un mot de passe a la structure de données


Ajout d’une colonne password_digest

On reviendra sur cette étape …

Ajout d’un mot de passe a la structure de données


Ajout d’une colonne password_digest

On reviendra sur cette étape après la création du test!

Ajout d’un mot de passe a la structure de données


Ajout d’une colonne password_digest

Ajout d’un mot de passe a la structure de données


Ajout d’une colonne password_digest:

(Gemfile) gem 'bcrypt-ruby', '3.0.1'

Ajout d’un mot de passe a la structure de données


Ajout d’une colonne password_digest:

(Gemfile) gem 'bcrypt-ruby', '3.0.1'

Puis


$ bundle install

Ajout d’un mot de passe a la structure de données


Ajout d’une colonne password_digest:

Ajout d’un mot de passe a la structure de données


Ajout d’une colonne password_digest:

Ajout d’un mot de passe a la structure de données


Ajout d’une colonne password_digest:

$ rails generate migration add_password_digest_to_users \
> password_digest:string

Ajout d’un mot de passe a la structure de données


Ajout d’une colonne password_digest:

$ bundle exec rake db:migrate
$ bundle exec rake db:test:prepare
$ bundle exec rspec spec/

Mot de passe avec confirmation


Ajout de deux attributs password et password_confirmation

Mot de passe avec confirmation


Ajout de deux attributs password et password_confirmation

Commencons par les tests:

Mot de passe avec confirmation


Ajout de deux attributs password et password_confirmation

Commencons par les tests:

Mot de passe avec confirmation


Ajout de deux attributs password et password_confirmation

Commencons par les tests:

Mot de passe avec confirmation


Ajout de deux attributs password et password_confirmation

Commencons par les tests:

Mot de passe avec confirmation


Ajout de deux attributs password et password_confirmation

Commencons par les tests:

Ajoutons identification et authentification


Identification de l’utilisateur


Facile, il suffit d’utiliser les primitives de recherche par attribut

Identification de l’utilisateur


Facile, il suffit d’utiliser les primitives de recherche par attribut

user = User.find_by_email(email)

Identification de l’utilisateur


Facile, il suffit d’utiliser les primitives de recherche par attribut

user = User.find_by_email(email)

Rails génère automatiquement ces méthodes en fonction des attributs/colonnes du modèle. Exemples:

Authentification


Nous utilisons la primitive authenticate

Authentification


Nous utilisons la primitive authenticate

current_user = user.authenticate(password)

Authentification


Nous utilisons la primitive authenticate

current_user = user.authenticate(password)

Bien-sûr, on commence par les tests:

it { should respond_to(:authenticate) }

Authentification (Tests)


it { should respond_to(:authenticate) }

Et quoi d’autre?

Authentification (Tests)


it { should respond_to(:authenticate) }

Et quoi d’autre?

Authentification (Tests)


it { should respond_to(:authenticate) }

Et quoi d’autre?

Authentification (Tests)


Authentification (Tests)


Et quoi d’autre?

Authentification (Tests)


Authentification (Tests)


Ajoutons identification et authentification


Faisons passer tout cela au vert:

Ajoutons identification et authentification


Faisons passer tout cela au vert:

Ajoutons identification et authentification


Faisons passer tout cela au vert:

$ bundle exec rake db:test:prepare
$ bundle exec rspec spec/

Et pour finir…


Ajoutons un utilisateur test dans la BDD