├── .gitignore ├── README.rdoc ├── Rakefile ├── lib └── sinatra │ └── authorization.rb ├── sinatra-authorization.gemspec └── test └── authorization_test.rb /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Sinatra Authorization 2 | 3 | HTTP Authorization helpers for Sinatra. 4 | 5 | == Example 6 | 7 | require "sinatra/authorization" 8 | 9 | set :authorization_realm, "Protected zone" 10 | 11 | helpers do 12 | def authorize(login, password) 13 | login == "admin" && password == "secret" 14 | end 15 | end 16 | 17 | get "/" do 18 | "Hello" 19 | end 20 | 21 | get "/admin" do 22 | login_required 23 | 24 | "Welcome in protected zone" 25 | end 26 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | task :default => :test 2 | 3 | desc "Run tests" 4 | task :test do 5 | ruby "test/authorization_test.rb" 6 | end 7 | 8 | begin 9 | require "mg" 10 | MG.new("sinatra-authorization.gemspec") 11 | rescue LoadError 12 | end 13 | -------------------------------------------------------------------------------- /lib/sinatra/authorization.rb: -------------------------------------------------------------------------------- 1 | require "sinatra/base" 2 | 3 | module Sinatra 4 | # HTTP Authorization helpers for Sinatra. 5 | # 6 | # In your helpers module, include Sinatra::Authorization and then define 7 | # an #authorize(user, password) method to handle user provided 8 | # credentials. 9 | # 10 | # Inside your events, call #login_required to trigger the HTTP 11 | # Authorization window to pop up in the browser. 12 | # 13 | # Code adapted from {Ryan Tomayko}[http://tomayko.com/about] and 14 | # {Christopher Schneid}[http://gittr.com], shared under an MIT License 15 | module Authorization 16 | # Redefine this method on your helpers block to actually contain 17 | # your authorization logic. 18 | def authorize(username, password) 19 | false 20 | end 21 | 22 | # From you app, call set :authorization_realm, "my app" to set this 23 | # or define a #authorization_realm method in your helpers block. 24 | def authorization_realm 25 | Sinatra::Default.authorization_realm 26 | end 27 | 28 | # Call in any event that requires authentication 29 | def login_required 30 | return if authorized? 31 | unauthorized! unless auth.provided? 32 | bad_request! unless auth.basic? 33 | unauthorized! unless authorize(*auth.credentials) 34 | request.env['REMOTE_USER'] = auth.username 35 | end 36 | 37 | # Convenience method to determine if a user is logged in 38 | def authorized? 39 | !!request.env['REMOTE_USER'] 40 | end 41 | alias :logged_in? :authorized? 42 | 43 | # Name provided by the current user to log in 44 | def current_user 45 | request.env['REMOTE_USER'] 46 | end 47 | 48 | private 49 | def auth 50 | @auth ||= Rack::Auth::Basic::Request.new(request.env) 51 | end 52 | 53 | def unauthorized!(realm=authorization_realm) 54 | response["WWW-Authenticate"] = %(Basic realm="#{realm}") 55 | throw :halt, [ 401, 'Authorization Required' ] 56 | end 57 | 58 | def bad_request! 59 | throw :halt, [ 400, 'Bad Request' ] 60 | end 61 | end 62 | 63 | helpers Authorization 64 | end 65 | -------------------------------------------------------------------------------- /sinatra-authorization.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = "sinatra-authorization" 3 | s.rubyforge_project = "integrity" 4 | s.version = "1.0.0" 5 | s.date = "2009-04-19" 6 | s.summary = "HTTP Authorization helpers for Sinatra." 7 | s.description = "HTTP Authorization helpers for Sinatra." 8 | s.homepage = "http://github.com/integrity/sinatra-authorization" 9 | s.email = "info@integrityapp.com" 10 | s.authors = ["Nicolás Sanguinetti", "Simon Rozet"] 11 | s.has_rdoc = false 12 | s.files = %w[ 13 | README.rdoc 14 | Rakefile 15 | sinatra-authorization.gemspec 16 | lib/sinatra/authorization.rb 17 | test/authorization_test.rb 18 | ] 19 | s.add_dependency("sinatra", [">= 0.9.1.1"]) 20 | end 21 | -------------------------------------------------------------------------------- /test/authorization_test.rb: -------------------------------------------------------------------------------- 1 | require "test/unit" 2 | require "rack/test" 3 | require "context" 4 | require "pending" 5 | 6 | require File.dirname(__FILE__) + "/../lib/sinatra/authorization" 7 | 8 | class AuthorizationApp < Sinatra::Default 9 | set :environment, :test 10 | 11 | get "/" do 12 | login_required 13 | 14 | "Welcome in protected zone" 15 | end 16 | 17 | def authorize(username, password) 18 | username == "user" && password = "test" 19 | end 20 | 21 | def authorization_realm 22 | "Move on" 23 | end 24 | end 25 | 26 | class SinatraAuthorizationTest < Test::Unit::TestCase 27 | before do 28 | @session = Rack::Test::Session.new(AuthorizationApp) 29 | end 30 | 31 | def basic_auth(user="user", password="test") 32 | credentials = ["#{user}:#{password}"].pack("m*") 33 | 34 | { "HTTP_AUTHORIZATION" => "Basic #{credentials}" } 35 | end 36 | 37 | it "is authorized with correct credentials" do 38 | @session.get "/", {}, basic_auth 39 | assert_equal 200, @session.last_response.status 40 | assert_equal ["Welcome in protected zone"], @session.last_response.body 41 | end 42 | 43 | it "sets REMOTE_USER" do 44 | pending "TODO" 45 | end 46 | 47 | it "is unauthorized without credentials" do 48 | @session.get "/" 49 | assert_equal 401, @session.last_response.status 50 | end 51 | 52 | it "is unauthorized with incorrect credentials" do 53 | @session.get "/", {}, basic_auth("evil", "wrong") 54 | assert_equal 401, @session.last_response.status 55 | end 56 | 57 | it "returns specified realm" do 58 | @session.get "/" 59 | assert_equal %Q(Basic realm="Move on"), @session.last_response["WWW-Authenticate"] 60 | end 61 | 62 | it "returns a 400, Bad Request if not basic auth" do 63 | @session.get "/", {}, { "HTTP_AUTHORIZATION" => "Foo bar" } 64 | assert_equal 400, @session.last_response.status 65 | end 66 | end 67 | --------------------------------------------------------------------------------