bacchus 0.0.9 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/bacchus.rb +2 -0
- data/lib/bacchus/access_pass.rb +24 -0
- data/lib/bacchus/backend.rb +21 -0
- data/lib/bacchus/rack/beta_site.rb +31 -14
- data/lib/bacchus/version.rb +1 -1
- data/spec/bacchus/access_pass_spec.rb +54 -0
- data/spec/bacchus/rack/beta_site_spec.rb +57 -9
- metadata +23 -3
data/lib/bacchus.rb
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Bacchus
|
2
|
+
class AccessPass # TODO: AccessCode elsewhere (backend, see other places).
|
3
|
+
PARAM_KEY = "access_pass"
|
4
|
+
COOKIE_KEY = "access_pass"
|
5
|
+
|
6
|
+
def self.get(request)
|
7
|
+
AccessPass.new(request)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(request)
|
11
|
+
param = request.params[PARAM_KEY]
|
12
|
+
@pass = param || request.cookies[COOKIE_KEY] || ""
|
13
|
+
@updated = !param.nil? && !param.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def maybe_save(response)
|
17
|
+
response.set_cookie(COOKIE_KEY, @pass) if @updated
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
@pass
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
|
3
|
+
module Bacchus
|
4
|
+
class Backend
|
5
|
+
def initialize(base_url)
|
6
|
+
@base_url = base_url
|
7
|
+
end
|
8
|
+
|
9
|
+
def locked?(access_pass)
|
10
|
+
RestClient.get(path_to("/api/1/access/#{access_pass.to_s}")) do |response, _|
|
11
|
+
return response.code != 200
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def path_to(resource)
|
16
|
+
File.join(@base_url, resource)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
@@ -21,31 +21,48 @@ module Rack
|
|
21
21
|
@excluded_routes = options[:except] || []
|
22
22
|
@access_form_routes = options[:access_through] || []
|
23
23
|
@access_template_route = options[:access_template] or raise "Specify the route to the template for the access form."
|
24
|
-
|
24
|
+
base_url = options[:backend] or raise "Specify the address of the backend server."
|
25
|
+
@backend = Bacchus::Backend.new(base_url)
|
25
26
|
end
|
26
27
|
|
27
28
|
def call(env)
|
28
29
|
request = Request.new(env)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
elsif access_denied?(request)
|
35
|
-
render_locked
|
36
|
-
else
|
37
|
-
@app.call(env)
|
38
|
-
end
|
30
|
+
access_pass = Bacchus::AccessPass.get(request)
|
31
|
+
status, headers, body = respond_to(request, access_pass)
|
32
|
+
response = Response.new(body, status, headers)
|
33
|
+
access_pass.maybe_save(response)
|
34
|
+
response.finish
|
39
35
|
end
|
40
36
|
|
41
37
|
private
|
38
|
+
|
39
|
+
def respond_to(request, access_pass)
|
40
|
+
if @backend.locked?(access_pass)
|
41
|
+
if access_form_route?(request)
|
42
|
+
return render_with_bacchus(redirect(request, @access_template_route))
|
43
|
+
elsif access_denied?(request)
|
44
|
+
return render_locked
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
@app.call(request.env)
|
49
|
+
end
|
50
|
+
|
51
|
+
def redirect(original_request, path)
|
52
|
+
request = original_request.dup
|
53
|
+
request.path = @access_template_route
|
54
|
+
env = request.env
|
55
|
+
env["PATH_INFO"] = request.path
|
56
|
+
env["REQUEST_URI"] = request.fullpath
|
57
|
+
Request.new(env)
|
58
|
+
end
|
42
59
|
|
43
60
|
def access_form_route?(request) # TODO: Duplication -- access_denied? & excluded_route?
|
44
61
|
@access_form_routes.index {|route| matching_path?(request, route)}
|
45
62
|
end
|
46
63
|
|
47
|
-
def render_with_bacchus(
|
48
|
-
status, headers, body = @app.call(env)
|
64
|
+
def render_with_bacchus(request)
|
65
|
+
status, headers, body = @app.call(request.env)
|
49
66
|
script = <<-EOS
|
50
67
|
<script src="#{path_to('bacchus.js')}" type="text/javascript"></script>
|
51
68
|
EOS
|
@@ -57,7 +74,7 @@ module Rack
|
|
57
74
|
########################################################
|
58
75
|
|
59
76
|
def path_to(file)
|
60
|
-
|
77
|
+
@backend.path_to(file)
|
61
78
|
end
|
62
79
|
|
63
80
|
########################################################
|
data/lib/bacchus/version.rb
CHANGED
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative '../../lib/bacchus/access_pass'
|
2
|
+
|
3
|
+
module Bacchus
|
4
|
+
describe AccessPass do
|
5
|
+
let(:request) { mock("request") }
|
6
|
+
let(:access_pass) { "399tehu" }
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
request.stub!(:params).and_return({})
|
10
|
+
request.stub!(:cookies).and_return({})
|
11
|
+
end
|
12
|
+
|
13
|
+
context "retrieving" do
|
14
|
+
it "should try to get it from request params" do
|
15
|
+
request.should_receive(:params).and_return({})
|
16
|
+
AccessPass.get(request)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should get it from params if present" do
|
20
|
+
request.stub!(:params).and_return("access_pass" => access_pass)
|
21
|
+
AccessPass.get(request).to_s.should == access_pass
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should get it from cookies if not in params" do
|
25
|
+
request.stub!(:params).and_return({})
|
26
|
+
request.should_receive(:cookies).and_return("access_pass" => access_pass)
|
27
|
+
AccessPass.get(request).to_s.should == access_pass
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should be empty if neither in params nor in cookies" do
|
31
|
+
request.stub!(:params).and_return({})
|
32
|
+
request.stub!(:cookies).and_return({})
|
33
|
+
AccessPass.get(request).to_s.should == ""
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "saving" do
|
38
|
+
let(:response) { mock("response") }
|
39
|
+
|
40
|
+
it "should save if updated from params" do
|
41
|
+
request.stub!(:params).and_return("access_pass" => access_pass)
|
42
|
+
response.should_receive(:set_cookie).with("access_pass", access_pass)
|
43
|
+
AccessPass.get(request).maybe_save(response)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should not save if not updated" do
|
47
|
+
request.stub!(:params).and_return({})
|
48
|
+
request.stub!(:cookies).and_return("access_pass" => access_pass)
|
49
|
+
response.should_not_receive(:set_cookie)
|
50
|
+
AccessPass.get(request).maybe_save(response)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -1,47 +1,95 @@
|
|
1
1
|
require 'rack/mock'
|
2
2
|
require_relative '../../../lib/bacchus/rack/beta_site'
|
3
|
+
require_relative '../../../lib/bacchus/backend'
|
4
|
+
require_relative '../../../lib/bacchus/access_pass'
|
3
5
|
|
4
6
|
describe Rack::BetaSite do
|
5
7
|
let(:app) { lambda { |env| app_response } }
|
6
8
|
let(:app_response) { [200, { 'Content-Type' => 'text/plain' }, ['<body>hello</body>']] }
|
7
9
|
let(:access_template_route) { "/access_template" }
|
8
|
-
let(:
|
10
|
+
let(:backend) { mock("backend").as_null_object }
|
11
|
+
let(:access_pass) { mock("access_pass").as_null_object }
|
12
|
+
let(:middleware) { Rack::BetaSite.new(app, except: ["/", %r{^/open/?$}], access_through: ["/form"], backend: "localhost", access_template: access_template_route) }
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
Bacchus::Backend.stub!(:new).and_return(backend)
|
16
|
+
Bacchus::AccessPass.stub!(:get).and_return(access_pass)
|
17
|
+
end
|
9
18
|
|
10
19
|
def response_for(sample_app, path)
|
11
20
|
sample_app.call(Rack::MockRequest.env_for(path))
|
12
21
|
end
|
13
22
|
|
23
|
+
it "should query the backend if the site is locked" do
|
24
|
+
request = mock("request").as_null_object
|
25
|
+
Rack::Request.stub!(:new).and_return(request)
|
26
|
+
Bacchus::AccessPass.should_receive(:get).with(request).and_return(access_pass)
|
27
|
+
backend.should_receive(:locked?).with(access_pass)
|
28
|
+
response_for(middleware, "/")
|
29
|
+
end
|
30
|
+
|
14
31
|
context "given a locked site" do
|
32
|
+
|
33
|
+
before(:each) do
|
34
|
+
backend.stub!(:locked?).and_return(true)
|
35
|
+
end
|
36
|
+
|
15
37
|
it "should block access to all pages" do
|
16
|
-
sample_app = middleware
|
17
38
|
status, * = response_for(middleware, "/page1")
|
18
39
|
status.should == 500
|
19
40
|
end
|
20
41
|
|
21
42
|
it "should support string exceptions" do
|
22
|
-
sample_app = middleware
|
23
43
|
status, * = response_for(middleware, "/")
|
24
44
|
status.should == 200
|
25
45
|
end
|
26
46
|
|
27
47
|
it "should support regex exceptions" do
|
28
|
-
sample_app = middleware
|
29
48
|
status, * = response_for(middleware, "/open")
|
30
49
|
status.should == 200
|
31
50
|
end
|
32
51
|
|
33
52
|
it "should render access form script after the body closing tag" do
|
34
|
-
|
35
|
-
status, _, body = response_for(middleware, "/form")
|
53
|
+
status, _, res = response_for(middleware, "/form")
|
36
54
|
status.should == 200
|
37
|
-
body.first.should include("hello")
|
38
|
-
body.first.should include("</body>\n<script")
|
55
|
+
res.body.first.should include("hello")
|
56
|
+
res.body.first.should include("</body>\n<script")
|
39
57
|
end
|
40
58
|
|
41
59
|
it "should redirect to access template" do
|
42
|
-
sample_app = middleware
|
43
60
|
app.should_receive(:call).with(hash_including("PATH_INFO" => "/access_template")).and_return(app_response)
|
44
61
|
response_for(middleware, "/form")
|
45
62
|
end
|
46
63
|
end
|
64
|
+
|
65
|
+
context "given an unlocked site" do
|
66
|
+
before(:each) do
|
67
|
+
backend.stub!(:locked?).and_return(false)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should allow access to all pages" do
|
71
|
+
status, * = response_for(middleware, "/page1")
|
72
|
+
status.should == 200
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should not render access form script" do
|
76
|
+
status, _, res = response_for(middleware, "/form")
|
77
|
+
status.should == 200
|
78
|
+
res.body.first.should_not include("</body>\n<script")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "should save access code" do
|
83
|
+
let(:response) { mock("response").as_null_object }
|
84
|
+
let(:access_pass) { mock("access_pass") }
|
85
|
+
|
86
|
+
before(:each) do
|
87
|
+
Rack::Response.stub!(:new).and_return(response)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should not set if not present" do
|
91
|
+
access_pass.should_receive(:maybe_save).with(response)
|
92
|
+
response_for(middleware, "/")
|
93
|
+
end
|
94
|
+
end
|
47
95
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bacchus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,24 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-11-
|
13
|
-
dependencies:
|
12
|
+
date: 2012-11-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rest-client
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
14
30
|
description: Adds support for private beta invites for Rack-based apps.
|
15
31
|
email:
|
16
32
|
- gyamtso@gmail.com
|
@@ -20,9 +36,12 @@ extra_rdoc_files: []
|
|
20
36
|
files:
|
21
37
|
- README.md
|
22
38
|
- LICENSE.txt
|
39
|
+
- lib/bacchus/access_pass.rb
|
40
|
+
- lib/bacchus/backend.rb
|
23
41
|
- lib/bacchus/rack/beta_site.rb
|
24
42
|
- lib/bacchus/version.rb
|
25
43
|
- lib/bacchus.rb
|
44
|
+
- spec/bacchus/access_pass_spec.rb
|
26
45
|
- spec/bacchus/rack/beta_site_spec.rb
|
27
46
|
homepage: ''
|
28
47
|
licenses: []
|
@@ -49,4 +68,5 @@ signing_key:
|
|
49
68
|
specification_version: 3
|
50
69
|
summary: Beta invites for Rack-based apps.
|
51
70
|
test_files:
|
71
|
+
- spec/bacchus/access_pass_spec.rb
|
52
72
|
- spec/bacchus/rack/beta_site_spec.rb
|