aker-confident 0.1.1
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/.gitignore +11 -0
- data/.rbenv-version +1 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +159 -0
- data/LICENSE +19 -0
- data/README +33 -0
- data/Rakefile +2 -0
- data/aker-confident.gemspec +34 -0
- data/assets/templates/agreement.haml +24 -0
- data/init_testbed.rakefile +113 -0
- data/lib/aker-confident.rb +1 -0
- data/lib/aker/confident.rb +62 -0
- data/lib/aker/confident/aker_slice.rb +13 -0
- data/lib/aker/confident/aker_user_ext.rb +12 -0
- data/lib/aker/confident/configuration.rb +131 -0
- data/lib/aker/confident/rack.rb +24 -0
- data/lib/aker/confident/signer.rb +24 -0
- data/lib/aker/confident/sinatra.rb +15 -0
- data/lib/aker/confident/version.rb +5 -0
- data/spec/aker_user_spec.rb +38 -0
- data/spec/assets/test-users.yml +5 -0
- data/spec/assets/test_agreement.txt +1 -0
- data/spec/conf_rack_spec.rb +132 -0
- data/spec/conf_sinatra_spec.rb +24 -0
- data/spec/confident_spec.rb +28 -0
- data/spec/configuration_spec.rb +103 -0
- data/spec/integration/rails_integration_spec.rb +56 -0
- data/spec/signer_spec.rb +23 -0
- data/spec/spec_helper.rb +31 -0
- metadata +262 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
module Aker
|
|
2
|
+
module Confident
|
|
3
|
+
class ConfigurationError < RuntimeError
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
class Configuration
|
|
7
|
+
attr_accessor :agreement, :root_url, :host_hook
|
|
8
|
+
attr_reader :pass_through_hooks
|
|
9
|
+
def initialize(options_hash={})
|
|
10
|
+
@pass_through_hooks = []
|
|
11
|
+
conf_model(options_hash[:conf_model])
|
|
12
|
+
conf_file(options_hash[:conf_file])
|
|
13
|
+
set_url options_hash[:root_url]
|
|
14
|
+
if options_hash[:conf_host_hook]
|
|
15
|
+
conf_host_hook options_hash[:conf_host_hook]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def define &block
|
|
20
|
+
instance_eval &block
|
|
21
|
+
self
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def conf_model(model_name)
|
|
25
|
+
if model_name == :default
|
|
26
|
+
@model = Confident::DefaultSignature
|
|
27
|
+
else
|
|
28
|
+
@model = model_name
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def set_url(url)
|
|
33
|
+
@root_url = url
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def conf_file(file_name)
|
|
37
|
+
@agreement_file = file_name
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def conf_host_hook(callable)
|
|
41
|
+
@pass_through_hooks << callable
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def verify!
|
|
45
|
+
validate_agreement_file!
|
|
46
|
+
load_agreement_file
|
|
47
|
+
validate_hook!
|
|
48
|
+
validate_model!
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def agreement
|
|
52
|
+
@agreement ||= load_agreement_file
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def model
|
|
56
|
+
if @model.is_a? String
|
|
57
|
+
@model.constantize
|
|
58
|
+
else
|
|
59
|
+
@model
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def validate_agreement_file!
|
|
66
|
+
raise Aker::Confident::ConfigurationError unless @agreement_file.present?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def load_agreement_file
|
|
70
|
+
begin
|
|
71
|
+
@agreement = File.read(@agreement_file)
|
|
72
|
+
rescue Errno::ENOENT => e
|
|
73
|
+
raise Aker::Confident::ConfigurationError.new "The specified file does not exist"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def valid_option_keys?(options_hash)
|
|
78
|
+
options_hash.keys.include?(:conf_model) &&
|
|
79
|
+
options_hash.keys.include?(:conf_file)
|
|
80
|
+
end
|
|
81
|
+
private :valid_option_keys?
|
|
82
|
+
|
|
83
|
+
def valid_conf_model_value?(options_hash)
|
|
84
|
+
if options_hash[:conf_model].is_a? Symbol
|
|
85
|
+
options_hash[:conf_model] == :default
|
|
86
|
+
elsif options_hash[:conf_model].is_a? String
|
|
87
|
+
!options_hash[:conf_model].empty?
|
|
88
|
+
else
|
|
89
|
+
!options_hash[:conf_model].nil?
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
private :valid_conf_model_value?
|
|
93
|
+
|
|
94
|
+
def valid_conf_file_value?(options_hash)
|
|
95
|
+
if options_hash[:conf_file].is_a? String
|
|
96
|
+
!options_hash[:conf_file].empty?
|
|
97
|
+
else
|
|
98
|
+
!options_hash[:conf_file].nil?
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
private :valid_conf_file_value?
|
|
102
|
+
|
|
103
|
+
def validate_model!
|
|
104
|
+
unless model.respond_to?(:signed?)
|
|
105
|
+
raise Aker::Confident::ConfigurationError.new "Confidentiality must respond to signed? method."
|
|
106
|
+
end
|
|
107
|
+
unless model.respond_to?(:sign)
|
|
108
|
+
raise Aker::Confident::ConfigurationError.new "Confidentiality must respond to sign method."
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def validate_hook!
|
|
113
|
+
unless pass_through_hooks.empty?
|
|
114
|
+
unless pass_through_hooks.all?{ |hook| hook.respond_to?(:call)}
|
|
115
|
+
raise Aker::Confident::ConfigurationError.new "Host hook must respond to call"
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def validate_options(options_hash)
|
|
121
|
+
unless valid_option_keys?(options_hash)
|
|
122
|
+
raise Aker::Confident::ConfigurationError.new "Valid Configurations require a conf_model and a conf_file!"
|
|
123
|
+
end
|
|
124
|
+
unless valid_conf_model_value?(options_hash) && valid_conf_file_value?(options_hash)
|
|
125
|
+
raise Aker::Confident::ConfigurationError.new "Valid Configuration conf_model and conf_file cannot be nil or blank"
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
private :validate_options
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'rack/request'
|
|
2
|
+
module Aker
|
|
3
|
+
module Confident
|
|
4
|
+
class Rack
|
|
5
|
+
def initialize(app)
|
|
6
|
+
@app = app
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def call(env)
|
|
10
|
+
if Aker::Confident.pass_through?(env)
|
|
11
|
+
@app.call(env)
|
|
12
|
+
else
|
|
13
|
+
if env['REQUEST_METHOD'] == 'POST'
|
|
14
|
+
env['aker.check'].user.sign
|
|
15
|
+
req = ::Rack::Request.new(env)
|
|
16
|
+
[303, {"Location" => "#{req[:original]}"}, ""]
|
|
17
|
+
else
|
|
18
|
+
Aker::Confident::Sinatra.call(env)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Aker
|
|
2
|
+
module Confident
|
|
3
|
+
module Signer
|
|
4
|
+
|
|
5
|
+
def signed?(user)
|
|
6
|
+
validate_argument!(user)
|
|
7
|
+
configuration.model.signed?(user)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def sign(user)
|
|
11
|
+
validate_argument!(user)
|
|
12
|
+
configuration.model.sign(user)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def validate_argument!(arg)
|
|
17
|
+
unless arg.is_a? Aker::User
|
|
18
|
+
raise ArgumentError.new("Requires an instance of Aker::User as argument")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
private :validate_argument!
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'sinatra/base'
|
|
2
|
+
require 'haml'
|
|
3
|
+
module Aker
|
|
4
|
+
module Confident
|
|
5
|
+
class Sinatra < Sinatra::Base
|
|
6
|
+
set :views, File.dirname(__FILE__) + '/../../../assets/templates'
|
|
7
|
+
get // do
|
|
8
|
+
@original_target = request.url
|
|
9
|
+
@conf_agreement = Aker::Confident.configuration.agreement
|
|
10
|
+
@root_url = request.scheme + "://" + request.host_with_port + request.script_name
|
|
11
|
+
haml :agreement
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Aker
|
|
4
|
+
class User
|
|
5
|
+
#add this for convenience in testing
|
|
6
|
+
attr_accessor :signed
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe Aker::User do
|
|
11
|
+
before(:all) do
|
|
12
|
+
Aker::User.send(:include, Aker::Confident::AkerUserExt)
|
|
13
|
+
end
|
|
14
|
+
before(:each) do
|
|
15
|
+
@user = Aker::User.new(1)
|
|
16
|
+
end
|
|
17
|
+
describe "#signed?" do
|
|
18
|
+
it "is true if confident.signed? is true" do
|
|
19
|
+
Aker::Confident.stub(:signed?).and_return(true)
|
|
20
|
+
@user.signed?.should be_true
|
|
21
|
+
end
|
|
22
|
+
it "sets the users signed attribute" do
|
|
23
|
+
Aker::Confident.stub(:signed?).and_return(false)
|
|
24
|
+
@user.signed.should be_false
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
describe "#sign" do
|
|
28
|
+
it "calls confidents sign method" do
|
|
29
|
+
Aker::Confident.should_receive(:sign).with(@user).and_return(true)
|
|
30
|
+
@user.sign
|
|
31
|
+
end
|
|
32
|
+
it "updates the users signed attribute" do
|
|
33
|
+
Aker::Confident.should_receive(:sign).with(@user).and_return(true)
|
|
34
|
+
@user.sign
|
|
35
|
+
@user.signed.should == true
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This is an agreement
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Aker::Confident::Rack do
|
|
4
|
+
include Rack::Test::Methods
|
|
5
|
+
|
|
6
|
+
class MockApp
|
|
7
|
+
def call(env)
|
|
8
|
+
[200, {}, 'Hello from Mock App!']
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def app
|
|
13
|
+
configure_conf
|
|
14
|
+
@app = Rack::Builder.new {
|
|
15
|
+
use Aker::Confident::Rack
|
|
16
|
+
run MockApp.new
|
|
17
|
+
}
|
|
18
|
+
@app.to_app
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context "a non-interactive request" do
|
|
22
|
+
let(:env){ {"aker.interactive" => false} }
|
|
23
|
+
it "should ignore all non-interactive requests" do
|
|
24
|
+
get '/foo', {}, env
|
|
25
|
+
last_response.body.should include "Hello from Mock App!"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
context "there is a pass_through_hook defined" do
|
|
30
|
+
let(:env){ {"aker.check" => stub(:user => stub(:signed? => false)), "aker.interactive" => true} }
|
|
31
|
+
let(:mock_app){ mock(:call => [200, {}, 'ok']) }
|
|
32
|
+
it 'calls the host hook with the rack env' do
|
|
33
|
+
host_hook = stub
|
|
34
|
+
Aker::Confident.stub(:configuration).
|
|
35
|
+
and_return (stub(:pass_through_hooks => [host_hook]))
|
|
36
|
+
host_hook.should_receive(:call)
|
|
37
|
+
get '/foo', {}, env
|
|
38
|
+
end
|
|
39
|
+
context 'the hook is true' do
|
|
40
|
+
let(:host_hook){ lambda{ |env| true } }
|
|
41
|
+
before do
|
|
42
|
+
MockApp.stub(:new).
|
|
43
|
+
and_return mock_app
|
|
44
|
+
Aker::Confident.stub(:configuration).
|
|
45
|
+
and_return stub(:pass_through_hooks => [host_hook])
|
|
46
|
+
end
|
|
47
|
+
it 'skips confident' do
|
|
48
|
+
mock_app.should_receive(:call)
|
|
49
|
+
get '/foo', {}, env
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
context 'the hook is false' do
|
|
53
|
+
let(:host_hook){ lambda{ |env| false } }
|
|
54
|
+
before do
|
|
55
|
+
MockApp.stub(:new).
|
|
56
|
+
and_return mock_app
|
|
57
|
+
Aker::Confident.stub(:configuration).
|
|
58
|
+
and_return stub(:pass_through_hooks => [host_hook])
|
|
59
|
+
end
|
|
60
|
+
it 'calls sinatra' do
|
|
61
|
+
Aker::Confident::Sinatra.should_receive(:call).and_return [200, {}, 'ok']
|
|
62
|
+
get '/foo', {}, env
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
context "an interactive request" do
|
|
68
|
+
let(:mock_user){ stub(:signed? => false) }
|
|
69
|
+
let(:env){
|
|
70
|
+
{"aker.check" => stub(:user => mock_user),
|
|
71
|
+
"aker.interactive" => true}
|
|
72
|
+
}
|
|
73
|
+
context "post /sign_agreement" do
|
|
74
|
+
it "should call aker user sign method" do
|
|
75
|
+
mock_user.should_receive(:sign).and_return true
|
|
76
|
+
post '/sign_agreement', {:original => "/foo"}, env
|
|
77
|
+
end
|
|
78
|
+
it "should redirect to url in params" do
|
|
79
|
+
mock_user.stub(:sign).and_return true
|
|
80
|
+
post '/sign_agreement', {:original => "/foo"}, env
|
|
81
|
+
last_response.should be_redirect
|
|
82
|
+
last_response.headers['Location'].should == "/foo"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context "post elsewhere" do
|
|
87
|
+
before do
|
|
88
|
+
@mock_app = mock(:call => [200, {}, 'ok'])
|
|
89
|
+
MockApp.stub(:new).
|
|
90
|
+
and_return @mock_app
|
|
91
|
+
end
|
|
92
|
+
it "should call the app" do
|
|
93
|
+
@mock_app.should_receive(:call)
|
|
94
|
+
post '/junk', {}, env
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
context "get logout" do
|
|
98
|
+
before do
|
|
99
|
+
@mock_app = mock(:call => [200, {}, 'ok'])
|
|
100
|
+
MockApp.stub(:new).
|
|
101
|
+
and_return @mock_app
|
|
102
|
+
end
|
|
103
|
+
it "calls the app" do
|
|
104
|
+
@mock_app.should_receive(:call)
|
|
105
|
+
get '/logout', {}, env
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
context "when agreement is not signed" do
|
|
111
|
+
before(:each) do
|
|
112
|
+
mock_user.stub(:signed?).and_return false
|
|
113
|
+
end
|
|
114
|
+
it "should call the confident sinatra" do
|
|
115
|
+
Aker::Confident::Sinatra.should_receive(:call).
|
|
116
|
+
and_return [200, {}, 'Foo']
|
|
117
|
+
get '/foo', {}, env
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
context "when agreement is signed" do
|
|
122
|
+
before(:each) do
|
|
123
|
+
mock_user.stub(:signed?).and_return true
|
|
124
|
+
end
|
|
125
|
+
it "should call the app" do
|
|
126
|
+
get '/foo', {}, env
|
|
127
|
+
last_response.should be_ok
|
|
128
|
+
last_response.body.should include "Hello from Mock App!"
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Aker::Confident::Sinatra do
|
|
4
|
+
include Rack::Test::Methods
|
|
5
|
+
def app
|
|
6
|
+
configure_conf
|
|
7
|
+
@app = Aker::Confident::Sinatra
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "should respond to /sign_agreement" do
|
|
11
|
+
get '/sign_agreement'
|
|
12
|
+
last_response.should be_ok
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should render the agreement from the configuration" do
|
|
16
|
+
get '/sign_agreement'
|
|
17
|
+
last_response.body.should include "This is an agreement"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe "capybara tests" do
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Aker::Confident do
|
|
4
|
+
describe "#configure" do
|
|
5
|
+
let(:dummy_conf){ stub.as_null_object }
|
|
6
|
+
it "builds a new config object" do
|
|
7
|
+
Aker::Confident::Configuration.should_receive(:new).and_return dummy_conf
|
|
8
|
+
Aker::Confident.configure
|
|
9
|
+
end
|
|
10
|
+
it "uses a block to define the configuration" do
|
|
11
|
+
Signature = Class.new
|
|
12
|
+
def Signature.signed? ; end
|
|
13
|
+
def Signature.sign ; end
|
|
14
|
+
Aker::Confident.configure do
|
|
15
|
+
conf_file __FILE__
|
|
16
|
+
conf_model Signature
|
|
17
|
+
end
|
|
18
|
+
Aker::Confident.configuration.model.should == Signature
|
|
19
|
+
Aker::Confident.configuration.agreement.should == File.read(__FILE__)
|
|
20
|
+
end
|
|
21
|
+
it "verifys the configuration" do
|
|
22
|
+
Aker::Confident::Configuration.stub(:new).and_return dummy_conf
|
|
23
|
+
dummy_conf.stub(:define).and_return dummy_conf
|
|
24
|
+
dummy_conf.should_receive(:verify!)
|
|
25
|
+
Aker::Confident.configure do ; end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|