rack-unbasic 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ doc
2
+ tmp/*
3
+ dist/*
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2009 Pat Nakajima and Nicolas Sanguinetti
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,38 @@
1
+ = Rack::Unbasic
2
+
3
+ Wraps HTTP authentication in more friendly workflows.
4
+
5
+ == Usage
6
+
7
+ require "rack-unbasic"
8
+
9
+ use Rack::Unbasic do |on|
10
+ on.bad_request '/login'
11
+ on.unauthorized '/login'
12
+ end
13
+
14
+ A response with a 401 or 400 status code will be redirected to the routes you
15
+ specify in the config, with the <tt>\env['rack-unbasic.code']</tt> set to whatever
16
+ the original status code was, and <tt>\env['rack-unbasic.return-to']</tt> set to
17
+ whatever the requested URI was.
18
+
19
+ When a request comes in with <tt>:username</tt> and <tt>:password</tt> params,
20
+ those will be mapped to basic auth credentials, and the appropriate headers will
21
+ be added.
22
+
23
+ Once authorized, the appropriate credentials will be stashed in the session.
24
+ Subsequent requests will use the credentials in the session for authorization.
25
+
26
+ == Install
27
+
28
+ gem install rack-unbasic
29
+
30
+ Or cloning the git source repository:
31
+
32
+ git clone git://github.com/foca/rack-unbasic.git
33
+
34
+ == Credits
35
+
36
+ Idea:: Pat Nakajima (github[http://github.com/nakajima])
37
+ Initial implementation:: Nicolás Sanguinetti (github[http://github.com/foca])
38
+ License:: MIT. See attached LICENSE file.
@@ -0,0 +1,34 @@
1
+ require "rake/testtask"
2
+
3
+ rdoc_sources = %w(hanna/rdoctask rdoc/task rake/rdoctask)
4
+ begin
5
+ require rdoc_sources.shift
6
+ rescue LoadError
7
+ retry
8
+ end
9
+
10
+ begin
11
+ require "metric_fu" if RUBY_VERSION < "1.9"
12
+ rescue LoadError
13
+ end
14
+
15
+ begin
16
+ require "mg"
17
+ MG.new("rack-unbasic.gemspec")
18
+ rescue LoadError
19
+ end
20
+
21
+ desc "Default: run all tests"
22
+ task :default => :test
23
+
24
+ desc "Run library tests"
25
+ Rake::TestTask.new do |t|
26
+ t.test_files = FileList["test/**/test_*.rb"]
27
+ end
28
+
29
+ Rake::RDocTask.new do |rd|
30
+ rd.main = "README"
31
+ rd.title = "Documentation for Rack::Unbasic"
32
+ rd.rdoc_files.include("README.rdoc", "LICENSE", "lib/**/*.rb")
33
+ rd.rdoc_dir = "doc"
34
+ end
@@ -0,0 +1,106 @@
1
+ require "rack"
2
+
3
+ module Rack
4
+ class Unbasic
5
+ # Create the middleware. You can pass a block to configure how to handle
6
+ # 401 and 400 responses from the downstream app. See #unauthorized and
7
+ # #bad_request.
8
+ def initialize(app, &block) # :yields: self
9
+ @app = app
10
+ @locations = {}
11
+ block.call(self) if block
12
+ end
13
+
14
+ # Set the redirect URL for 401 responses from the downstream app.
15
+ def unauthorized(location)
16
+ @locations["unauthorized"] = location
17
+ end
18
+
19
+ # Set the redirect URL for 400 responses from the downstream app.
20
+ def bad_request(location)
21
+ @locations["bad_request"] = location
22
+ end
23
+
24
+ def call(env)
25
+ @env = env
26
+
27
+ clean_session_data
28
+
29
+ authorize_from_params || authorize_from_session
30
+
31
+ @response = @app.call(@env)
32
+
33
+ case status_code
34
+ when 401
35
+ unauthorized_response
36
+ when 400
37
+ bad_request_response
38
+ else
39
+ store_credentials
40
+ @response
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def unauthorized_response
47
+ return @response if @locations["unauthorized"].nil?
48
+ store_location_and_response_code
49
+ [302, {"Location" => @locations["unauthorized"]}, []]
50
+ end
51
+
52
+ def bad_request_response
53
+ return @response if @locations["bad_request"].nil?
54
+ store_location_and_response_code
55
+ [302, {"Location" => @locations["bad_request"]}, []]
56
+ end
57
+
58
+ def store_location_and_response_code
59
+ session["rack-unbasic.return-to"] = @env["PATH_INFO"]
60
+ session["rack-unbasic.code"] = status_code
61
+ end
62
+
63
+ def clean_session_data
64
+ unless session.respond_to?("delete")
65
+ raise "You need to enable sessions for this middleware to work"
66
+ end
67
+ @env["rack-unbasic.return-to"] = session.delete("rack-unbasic.return-to")
68
+ @env["rack-unbasic.code"] = session.delete("rack-unbasic.code")
69
+ end
70
+
71
+ def authorize_from_params
72
+ return nil if request.params["username"].nil? || request.params["password"].nil?
73
+ send_http_authorization(request.params["username"], request.params["password"])
74
+ end
75
+
76
+ def authorize_from_session
77
+ return nil if session["rack-unbasic.username"].nil? || session["rack-unbasic.password"].nil?
78
+ send_http_authorization(session["rack-unbasic.username"], session["rack-unbasic.password"])
79
+ end
80
+
81
+ def send_http_authorization(username, password)
82
+ return nil unless @env["HTTP_AUTHORIZATION"].nil?
83
+ @username, @password = username, password
84
+ encoded_login = ["#{username}:#{password}"].pack("m*")
85
+ @env["HTTP_AUTHORIZATION"] = "Basic #{encoded_login}"
86
+ end
87
+
88
+ def store_credentials
89
+ return if @username.nil? || @password.nil?
90
+ session["rack-unbasic.username"] = @username
91
+ session["rack-unbasic.password"] = @password
92
+ end
93
+
94
+ def status_code
95
+ @response[0].to_i
96
+ end
97
+
98
+ def request
99
+ @request ||= Rack::Request.new(@env)
100
+ end
101
+
102
+ def session
103
+ @env["rack.session"]
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,34 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "rack-unbasic"
3
+ s.version = "0.1"
4
+ s.date = "2009-05-23"
5
+
6
+ s.description = "Elegant workflow for Rack::Auth::Basic"
7
+ s.summary = "Handle HTTP auth errors nicely, and abstract auth logic a little bit."
8
+ s.homepage = "http://github.com/foca"
9
+
10
+ s.authors = ["Pat Nakajima", "Nicolás Sanguinetti"]
11
+ s.email = "contacto@nicolassanguinetti.info"
12
+
13
+ s.require_paths = ["lib"]
14
+ s.rubyforge_project = "rack-unbasic"
15
+ s.has_rdoc = true
16
+ s.rubygems_version = "1.3.1"
17
+
18
+ s.add_dependency "rack"
19
+
20
+ if s.respond_to?(:add_development_dependency)
21
+ s.add_development_dependency "sr-mg"
22
+ s.add_development_dependency "contest"
23
+ end
24
+
25
+ s.files = %w[
26
+ .gitignore
27
+ LICENSE
28
+ README.rdoc
29
+ Rakefile
30
+ rack-unbasic.gemspec
31
+ lib/rack/unbasic.rb
32
+ test/test_unbasic.rb
33
+ ]
34
+ end
@@ -0,0 +1,121 @@
1
+ ENV["RACK_ENV"] ||= "test"
2
+
3
+ require "test/unit"
4
+ require "rack/test"
5
+ require "contest"
6
+ require File.dirname(__FILE__) + "/../lib/rack/unbasic"
7
+
8
+ begin
9
+ require "redgreen"
10
+ rescue LoadError
11
+ end
12
+
13
+ class TestUnbasic < Test::Unit::TestCase
14
+ include Rack::Test::Methods
15
+
16
+ def unbasic_app(&unbasic)
17
+ Rack::Builder.new {
18
+ use Rack::Session::Cookie
19
+ use Rack::Unbasic, &unbasic
20
+
21
+ app = lambda { [200, {}, "Hi there"] }
22
+
23
+ map "/login" do
24
+ run app
25
+ end
26
+
27
+ map "/" do
28
+ use Rack::Auth::Basic do |user, password|
29
+ user == "johndoe" && password == "secret"
30
+ end
31
+ run app
32
+ end
33
+ }
34
+ end
35
+
36
+ def app
37
+ @app ||= unbasic_app do |on|
38
+ on.unauthorized "/login"
39
+ on.bad_request "/login"
40
+ end
41
+ end
42
+
43
+ context "Without providing authorization" do
44
+ test "it redirects to the specified location" do
45
+ get "/foobar"
46
+ follow_redirect!
47
+ assert_equal "http://example.org/login", last_request.url
48
+ end
49
+
50
+ test "it saves the previous url and code in env['rack-unbasic.*']" do
51
+ get "/foobar"
52
+ follow_redirect!
53
+ assert_equal "/foobar", last_request.env["rack-unbasic.return-to"]
54
+ assert_equal 401, last_request.env["rack-unbasic.code"]
55
+ end
56
+ end
57
+
58
+ context "When providing a broken authorization (or at least not HTTP Basic" do
59
+ test "it redirects to the specified location" do
60
+ get "/foobar", {}, { "HTTP_AUTHORIZATION" => "cuack" }
61
+ follow_redirect!
62
+ assert_equal "http://example.org/login", last_request.url
63
+ end
64
+
65
+ test "it saves the previous url and code in env['rack-unbasic.*']" do
66
+ get "/foobar", {}, { "HTTP_AUTHORIZATION" => "cuack" }
67
+ follow_redirect!
68
+ assert_equal "/foobar", last_request.env["rack-unbasic.return-to"]
69
+ assert_equal 400, last_request.env["rack-unbasic.code"]
70
+ end
71
+ end
72
+
73
+ context "Providing HTTP Basic authorization" do
74
+ test "it works transparently" do
75
+ authorize "johndoe", "secret"
76
+ get "/foobar"
77
+ assert_equal 200, last_response.status
78
+ assert_equal "Hi there", last_response.body
79
+ end
80
+ end
81
+
82
+ context "Passing the credentials as parameters" do
83
+ test "should work as if using HTTP Basic" do
84
+ get "/foobar", :username => "johndoe", :password => "secret"
85
+ assert last_response.ok?
86
+ assert_equal "Hi there", last_response.body
87
+ end
88
+
89
+ test "should work for subsequent requests" do
90
+ get "/foobar", :username => "johndoe", :password => "secret"
91
+ assert last_response.ok?
92
+
93
+ get "/baz"
94
+ assert last_response.ok?
95
+
96
+ get "/quux"
97
+ assert last_response.ok?
98
+ end
99
+ end
100
+
101
+ context "When unbasic isn't set to handle 401 or 400 responses" do
102
+ def app
103
+ @app ||= unbasic_app
104
+ end
105
+
106
+ test "it doesn't interfere with pages that don't require auth" do
107
+ get "/login"
108
+ assert last_response.ok?
109
+ end
110
+
111
+ test "it returns the original 401 if credentials aren't given" do
112
+ get "/foobar"
113
+ assert_equal 401, last_response.status
114
+ end
115
+
116
+ test "it returns the original 400 if credentials are malformed" do
117
+ get "/foobar", {}, { "HTTP_AUTHORIZATION" => "cuack" }
118
+ assert_equal 400, last_response.status
119
+ end
120
+ end
121
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-unbasic
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - Pat Nakajima
8
+ - "Nicol\xC3\xA1s Sanguinetti"
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-05-23 00:00:00 -03:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rack
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "0"
25
+ version:
26
+ - !ruby/object:Gem::Dependency
27
+ name: sr-mg
28
+ type: :development
29
+ version_requirement:
30
+ version_requirements: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ version:
36
+ - !ruby/object:Gem::Dependency
37
+ name: contest
38
+ type: :development
39
+ version_requirement:
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
46
+ description: Elegant workflow for Rack::Auth::Basic
47
+ email: contacto@nicolassanguinetti.info
48
+ executables: []
49
+
50
+ extensions: []
51
+
52
+ extra_rdoc_files: []
53
+
54
+ files:
55
+ - .gitignore
56
+ - LICENSE
57
+ - README.rdoc
58
+ - Rakefile
59
+ - rack-unbasic.gemspec
60
+ - lib/rack/unbasic.rb
61
+ - test/test_unbasic.rb
62
+ has_rdoc: true
63
+ homepage: http://github.com/foca
64
+ licenses: []
65
+
66
+ post_install_message:
67
+ rdoc_options: []
68
+
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "0"
76
+ version:
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ version:
83
+ requirements: []
84
+
85
+ rubyforge_project: rack-unbasic
86
+ rubygems_version: 1.3.3
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: Handle HTTP auth errors nicely, and abstract auth logic a little bit.
90
+ test_files: []
91
+