De plus en plus de services WEB universitaires sont aujourd'hui authentifiés via des mécanismes de Single-Sign-On tel que CAS (CAS Protocol).
Vous allez voir comment :
1: require 'rubygems' # 2: require 'nokogiri' # pour manipuler les arbres XML 3: require 'patron' # client web s'appuyant sur la libcurl 4: require 'yaml' # pour charger le fichier d'identifiant 5: require 'pp' # pour le pretty-printing 6: require 'uri' # pour le parsing des URI 7: 8: require 'sympa/client' 9: 10: Sympa::Client.new( 11: :service_uri => 'https://listes.example.com/sympa', 12: :credential_file => 'cas_credential.yaml' 13: )
1: username: leonardo.davinci 2: password: joconde
1: require 'sso/cas/client' 2: %w(a_completer get_cas_uri authenticate_session).each do |lib| require "sympa/client/#{lib}" end 3: 4: module Sympa; class Client; def initialize(params={}) 5: @cookies = {} 6: @service_uri = params[:service_uri] 7: @credential_file = params[:credential_file] 8: uri = URI(@service_uri) 9: @base_uri, @service_path = "#{uri.scheme}://#{uri.host}", uri.path 10: @session = Patron::Session.new 11: @session.base_url = @base_uri 12: @session.headers['User-Agent'] = 'Ruby/Script' 13: @session.enable_debug('debug/sympa_client.log') 14: @session.insecure = true 15: @session.max_redirects = 0 16: ####################################################################### 17: # 4 phases: 18: # - get_sso_uri : obtention du chemin, sur le serveur sympa, a authentifier par CAS 19: # - get_cas_uri : interrogation de ce chemin pour obtenir l'URI du serveur CAS 20: # - SSO::Cas::Client.new : on passe cette URI au client CAS pour l'authentification; le serveur CAS retourne un Login-Ticket 21: # on s'authentifie aupres du serveur cas avec le Login-Ticket; le serveur CAS retourne un service-ticket 22: # - on passe le service-ticket a apache qui va valider la session aupres du serveur CAS 23: ####################################################################### 24: get_sso_uri # dans le fichier 'sympa/a_completer.rb' 25: get_cas_uri 26: cas_client = SSO::CAS::Client.new( :cas_uri => @cas_uri, :credential_file => @credential_file ) 27: lt_uri = URI(cas_client.return_uri) 28: authenticate_session(lt_uri) 29: end; end; end
1: module Sympa; class Client; def get_sso_uri() 2: puts "===================================================================" 3: puts "= Sympa::Client.get_sso_uri()" 4: puts "===================================================================" 5: response = @session.get(@service_path) 6: doc = Nokogiri::HTML(response.body) 7: ####################################################################### 8: # 9: # DEBUT A COMPLETER 10: # 11: ####################################################################### 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: ####################################################################### 22: # 23: # FIN A COMPLETER 24: # 25: ####################################################################### 26: response = @session.post(form_action,post_fields) 27: @sso_uri = response.headers['Location'] 28: puts @sso_uri 29: end; end; end
1: module Sympa; class Client; def get_cas_uri() 2: puts "===================================================================" 3: puts "= Sympa::Client.get_cas_uri()" 4: puts "===================================================================" 5: uri = URI(@sso_uri) 6: base_uri,sso_path = "#{uri.scheme}://#{uri.host}", uri.path 7: response = @session.get(sso_path) 8: @cas_uri = response.headers['Location'] 9: puts @cas_uri 10: end; end; end
sympa/client/authenticate_session.rb
1: module Sympa; class Client; def authenticate_session(lt_uri) 2: last_hop = [lt_uri.path,lt_uri.query].join('?') 3: response = @session.get(last_hop) 4: @cookies['MOD_AUTH_CAS_S'] = $1 unless "#{response.headers['Set-Cookie']}".match(/MOD_AUTH_CAS_S=([^;]*)/).nil? 5: @session.headers['Cookie'] = @cookies.collect{|k,v| "#{k}=#{v}" }.join(';') 6: response = @session.get(lt_uri.path) 7: pp response.headers 8: end; end; end
1: %w(get_login_form authenticate_user a_completer).each do |lib| require "sso/cas/client/#{lib}" end 2: 3: module SSO; module CAS; class Client 4: attr_reader :return_uri 5: def initialize(params={}) 6: @cas_uri = params[:cas_uri] 7: @credential_file = params[:credential_file] 8: @session = Patron::Session.new 9: uri = URI(@cas_uri) 10: @base_uri,@service_path = "#{uri.scheme}://#{uri.host}:#{uri.port}", "#{uri.path}?#{uri.query}" 11: @session.base_url = @base_uri 12: @session.headers['User-Agent'] = 'Ruby/Script' 13: @session.enable_debug('debug/sso_cas_client.log') 14: @session.insecure = true 15: @session.max_redirects = 0 16: ####################################################################### 17: # 18: # 3 Phases : 19: # - récupération du formulaire de login 20: # - extraction des champs a renvoyer 21: # - post du formulaire avec les identifiants utilisateurs 22: # 23: ####################################################################### 24: get_login_form 25: extract_login_fields # dans le fichier 'sso/cas/client/a_completer.rb' 26: authenticate_user 27: end 28: end; end; end
sso/cas/client/get_login_form.rb
1: module SSO; module CAS; class Client; def get_login_form() 2: puts "===================================================================" 3: puts "= SSO::CAS::Client.get_login_form()" 4: puts "===================================================================" 5: response = @session.get(@service_path) 6: @doc = Nokogiri::HTML(response.body) 7: end; end; end; end
sso/cas/client/authenticate_user.rb
1: module SSO; module CAS; class Client; def authenticate_user(params={}) 2: puts "===================================================================" 3: puts "= SSO::CAS::Client.authenticate_user(params={})" 4: puts "===================================================================" 5: credential = YAML::load(File.open(@credential_file)) 6: @post_fields['username'] = credential['username'] 7: @post_fields['password'] = credential['password'] 8: pp @post_fields 9: response = @session.post(@form_action,@post_fields) 10: @return_uri=response.headers['Location'] 11: end; end; end; end
1: module SSO; module CAS; class Client; def extract_login_fields() 2: puts "===================================================================" 3: puts "= SSO::CAS::Client.extract_login_fields()" 4: puts "===================================================================" 5: ####################################################################### 6: # 7: # DEBUT A COMPLETER 8: # 9: ####################################################################### 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: ####################################################################### 20: # 21: # FIN A COMPLETER 22: # 23: ####################################################################### 24: output = { 25: '@form_action' => @form_action, 26: '@form_fields' => @post_fields 27: } 28: pp output 29: end; end; end; end
1: module Sympa; class Client; def get_sso_uri() 2: puts "===================================================================" 3: puts "= Sympa::Client.get_sso_uri()" 4: puts "===================================================================" 5: response = @session.get(@service_path) 6: doc = Nokogiri::HTML(response.body) 7: ####################################################################### 8: # 9: # DEBUT A COMPLETER 10: # 11: ####################################################################### 12: form = doc.xpath('//div[@id="Identity_not_connect"]/form') 13: form_action = form.attribute('action').text 14: form_inputs = form.xpath('.//input') 15: post_fields = {} 16: form_inputs.each{|input| 17: name = input.attribute('name').text 18: value = input.attribute('value').text 19: post_fields[name]=value 20: } 21: ####################################################################### 22: # 23: # FIN A COMPLETER 24: # 25: ####################################################################### 26: response = @session.post(form_action,post_fields) 27: @sso_uri = response.headers['Location'] 28: puts @sso_uri 29: end; end; end
1: module SSO; module CAS; class Client; def extract_login_fields() 2: puts "===================================================================" 3: puts "= SSO::CAS::Client.extract_login_fields()" 4: puts "===================================================================" 5: ####################################################################### 6: # 7: # DEBUT A COMPLETER 8: # 9: ####################################################################### 10: form = @doc.xpath('//form[@id="fm1"]') 11: @form_action = form.attribute('action').text 12: form_inputs = form.xpath('.//input') 13: @post_fields = {} 14: form_inputs.each do |input| 15: name = input.attribute('name').text 16: value = input.attribute('value').text 17: @form_fields[name]=value unless (name == 'reset') 18: end 19: ####################################################################### 20: # 21: # FIN A COMPLETER 22: # 23: ####################################################################### 24: output = { 25: '@form_action' => @form_action, 26: '@form_fields' => @post_fields 27: } 28: pp output 29: end; end; end; end
$Id: jeudi_corrige_ruby_1.txt 567 2012-05-21 07:29:58Z aicardi $