Il est fréquent de vouloir restreindre l'accès à certaines ressources d'un service web, donc il faut être en mesure d'authentifier qui effectue la requête. On peut mettre en place une technique personnalisée pour la gestion des identifiants, mais il existe un mécanisme standard qui facilite l'implémentation via les headers.
WWW-Authenticate: Basic realm="zone restreinte!!!"
Authorization: Basic ZXRkOnNoYXdp
Les identifiants sont joints par :
et encodé en Base64, ex: etd:shawi
devient ZXRkOnNoYXdp
require "bundler/inline"
gemfile do
source "http://rubygems.org"
gem "sinatra-contrib"
gem "rackup"
gem "webrick"
end
require "sinatra/base"
require "sinatra/reloader"
class SimpleAuth < Sinatra::Base
configure :development do
register Sinatra::Reloader
end
# Pour toutes les routes
#
# ATTENTION, il faut redémarrer le serveur
# après avoir activé l'authentification
use Rack::Auth::Basic do |username, password|
# Le mecanisme de validation
# peut etre personnalisé, ex:
# fichier, BD, etc.
username == "admin" && password == "pwda"
end
before do
@request_user = env['REMOTE_USER']
# pour eviter de répéter dans chaque route
# le @ defini une variable membre
# https://docs.ruby-lang.org/en/3.1/syntax/assignment_rdoc.html#label-Instance+Variables
# http://ruby-for-beginners.rubymonstas.org/writing_classes/instance_variables.html
end
get "/" do
user = env['REMOTE_USER'] # !!!
"Ahoy #{user}"
end
get "/secret" do
user = env['REMOTE_USER'] # !!!
"Ahoy again, #{user}"
end
get "/secret-again" do
"Ahoy yet again, #{@request_user}"
end
run! if app_file == $0
end
require "bundler/inline"
gemfile do
source "http://rubygems.org"
gem "sinatra-contrib"
gem "rackup"
gem "webrick"
end
require "sinatra/base"
require "sinatra/reloader"
require "openssl"
SHA_KEY = "th3Superkey!" # Salt de hachage
class CustomAuth < Sinatra::Base
configure :development do
register Sinatra::Reloader
end
# On ne veut pas enregistrer de mot de passe en clair
# le hachage sha256 permet d'empecher une fuite
# des mots de passe pouvant amener a une attaque subsequente si réutilisé
# https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#background
def sha256(value)
nil if value.nil? || value.empty?
OpenSSL::HMAC.hexdigest("sha256", SHA_KEY, value)
end
def authorize
auth = Rack::Auth::Basic::Request.new(request.env)
return unless auth.provided? && auth.basic? && auth.credentials
username, password = auth.credentials
# auth.credentials est ["username", "password"]
# https://docs.ruby-lang.org/en/3.1/syntax/assignment_rdoc.html#label-Array+Decomposition
if username == "admin" && sha256(password) == "71b0e3e2c9d25193a93bf31beaed7c3cf08d2d39a37e52f79366af1b7f6bf9d6" # sha256("pwda")
@user = username
end
end
# Avant chaque route
before do
authorize
# trouver l'utilisateur authentifié, s'il existe
end
def guard!
auth_required = [
401,
{ "WWW-Authenticate" => "Basic" },
"Provide a username and password through Basic HTTP authentication"
]
# HALT interrompt IMMEDIATEMENT la requête et retourne le resultat
halt auth_required unless @user
end
get "/" do
"Welcome!"
end
get "/hey" do
message = @user || "[anonymous]"
"Hey #{message} :)"
end
get "/private" do
guard! # Oblige l'authentification pour cette route
"Ahoy #{@user}"
end
run! if app_file == $0
end
require "bundler/inline"
gemfile do
source "http://rubygems.org"
gem "faraday"
end
require "faraday"
#
# POUR LE SERVEUR PERSONNALISÉ
#
server = Faraday.new "http://localhost:4567"
def show(response)
puts response.status
puts "---"
puts response.headers
puts "---"
puts response.body
puts "\n\n"
end
def header(message)
puts "========================"
puts "========================"
puts message
puts "========================"
puts "========================"
puts "\n\n"
end
header "PAS auth"
show server.get("/")
show server.get("/hey")
show server.get("/private")
header "Auth PAR REQUETE"
# Auth manuelle, pour chaque requete
require "base64"
show server.get("/private", nil, {"Authorization" => "Basic #{Base64.encode64("admin:pwda")}"})
show server.get("/hey", nil, {"Authorization" => "Basic #{Base64.encode64("admin:pwda")}"})
show server.get("/hey")
header "Auth PAR CONNEXION"
# Helper sur la connexion
# tant que la connexion existe,
# les infos sont utilisées
server.set_basic_auth "admin", "pwda"
show server.get("/private")
show server.get("/hey")
# On peut egalement configurer l'authentification
# a la creation de la connexion https://lostisland.github.io/faraday/#/middleware/included/authentication