bacchus 0.0.9 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|