omniauth-aleph 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/.gitignore +24 -0
- data/.travis.yml +7 -0
- data/Gemfile +16 -0
- data/LICENSE +20 -0
- data/README.md +24 -0
- data/Rakefile +9 -0
- data/lib/omniauth-aleph.rb +1 -0
- data/lib/omniauth/aleph.rb +3 -0
- data/lib/omniauth/aleph/adaptor.rb +57 -0
- data/lib/omniauth/aleph/version.rb +5 -0
- data/lib/omniauth/strategies/aleph.rb +61 -0
- data/omniauth-aleph.gemspec +27 -0
- data/spec/omniauth/aleph/adaptor_spec.rb +217 -0
- data/spec/omniauth/strategies/aleph_spec.rb +251 -0
- data/spec/spec_helper.rb +59 -0
- data/spec/vcr_cassettes/OmniAuth_Aleph_Adaptor/when_the_aleph_host_doesn_t_connect/_authenticate/should_raise_an_Aleph_error.yml +36 -0
- data/spec/vcr_cassettes/aleph_host_returns_an_empty_body.yml +48 -0
- data/spec/vcr_cassettes/aleph_host_returns_html.yml +64 -0
- data/spec/vcr_cassettes/aleph_host_returns_non_bor_auth_xml.yml +48 -0
- data/spec/vcr_cassettes/aleph_host_returns_with_status_not_found.yml +64 -0
- data/spec/vcr_cassettes/empty_password.yml +41 -0
- data/spec/vcr_cassettes/invalid_password.yml +41 -0
- data/spec/vcr_cassettes/nil_password.yml +41 -0
- data/spec/vcr_cassettes/nonexistent_user.yml +41 -0
- data/spec/vcr_cassettes/valid.yml +47 -0
- metadata +218 -0
@@ -0,0 +1,251 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe "OmniAuth::Strategies::Aleph" do
|
3
|
+
let(:request) do
|
4
|
+
double('Request', params: {}, cookies: {}, env: {})
|
5
|
+
end
|
6
|
+
|
7
|
+
context "when it's improperly configured" do
|
8
|
+
let(:config) {{ host: "host" }}
|
9
|
+
|
10
|
+
subject(:strategy) do
|
11
|
+
OmniAuth::Strategies::Aleph.new(nil, config).tap do |strategy|
|
12
|
+
strategy.stub(:request) { request }
|
13
|
+
strategy.stub(:username) { aleph_username }
|
14
|
+
strategy.stub(:password) { aleph_password }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#request_phase" do
|
19
|
+
it 'should raise an Argument Error' do
|
20
|
+
expect{ strategy.request_phase }.to raise_error(ArgumentError)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#callback_phase" do
|
25
|
+
it 'should raise an Argument Error' do
|
26
|
+
expect{ strategy.callback_phase }.to raise_error(ArgumentError)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when it's properly configured" do
|
32
|
+
let(:config) do
|
33
|
+
{ host: aleph_host,
|
34
|
+
library: aleph_library,
|
35
|
+
sub_library: aleph_sub_library }
|
36
|
+
end
|
37
|
+
|
38
|
+
subject(:strategy) do
|
39
|
+
# First argument is the app, which gets called with env,
|
40
|
+
# i.e. app.call(env), so fake it with a stabby lambda
|
41
|
+
OmniAuth::Strategies::Aleph.new(->(env) {}, config).tap do |strategy|
|
42
|
+
strategy.stub(:request) { request }
|
43
|
+
strategy.stub(:username) { aleph_username }
|
44
|
+
strategy.stub(:password) { aleph_password }
|
45
|
+
strategy.stub(:env) { {} }
|
46
|
+
strategy.stub(:fail!) { true }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#options' do
|
51
|
+
it 'should not error' do
|
52
|
+
expect{ strategy.options }.not_to raise_error
|
53
|
+
end
|
54
|
+
|
55
|
+
subject(:options) { strategy.options }
|
56
|
+
|
57
|
+
describe'#name' do
|
58
|
+
it 'should equal "Aleph"' do
|
59
|
+
expect(options.name).to eq("aleph")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#title' do
|
64
|
+
it 'should equal "Aleph Authentication"' do
|
65
|
+
expect(options.title).to eq("Aleph Authentication")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#scheme' do
|
70
|
+
it 'should equal "http"' do
|
71
|
+
expect(options.scheme).to eq("http")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#host' do
|
76
|
+
it 'should be the aleph host' do
|
77
|
+
expect(options.host).to eq(aleph_host)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#port' do
|
82
|
+
it 'should be 80' do
|
83
|
+
expect(options.port).to be(80)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe '#library' do
|
88
|
+
it 'should be the aleph library' do
|
89
|
+
expect(options.library).to eq(aleph_library)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#sub_library' do
|
94
|
+
it 'should be the aleph sub_library' do
|
95
|
+
expect(options.sub_library).to eq(aleph_sub_library)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#request_phase" do
|
101
|
+
it 'shouldn\'t raise an error' do
|
102
|
+
expect{ strategy.request_phase }.not_to raise_error
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "#callback_phase", vcr: { cassette_name: "valid" } do
|
107
|
+
context "when the credentials are missing" do
|
108
|
+
before(:each) do |example|
|
109
|
+
strategy.stub(:username) { "" }
|
110
|
+
strategy.stub(:password) { "" }
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'shouldn\'t raise an error' do
|
114
|
+
expect{ strategy.callback_phase }.not_to raise_error
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should fail!' do
|
118
|
+
strategy.callback_phase
|
119
|
+
expect(strategy).to have_received(:fail!)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "when the credentials are included" do
|
124
|
+
before(:each) do
|
125
|
+
strategy.stub(:username) { aleph_username }
|
126
|
+
strategy.stub(:password) { aleph_password }
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'shouldn\'t raise an error' do
|
130
|
+
expect{ strategy.callback_phase }.not_to raise_error
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'shouldn\'t fail!' do
|
134
|
+
strategy.callback_phase
|
135
|
+
expect(strategy).not_to have_received(:fail!)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
context "when it's used as middleware" do
|
141
|
+
let(:config) do
|
142
|
+
{ title: "MY Aleph Authentication",
|
143
|
+
host: aleph_host,
|
144
|
+
library: aleph_library,
|
145
|
+
sub_library: aleph_sub_library }
|
146
|
+
end
|
147
|
+
|
148
|
+
let(:app) do
|
149
|
+
args = config
|
150
|
+
Rack::Builder.new {
|
151
|
+
use OmniAuth::Test::PhonySession
|
152
|
+
use OmniAuth::Strategies::Aleph, args
|
153
|
+
run lambda { |env| [404, {'Content-Type' => 'text/plain'}, [env.key?('omniauth.auth').to_s]] }
|
154
|
+
}.to_app
|
155
|
+
end
|
156
|
+
|
157
|
+
let(:session) do
|
158
|
+
last_request.env['rack.session']
|
159
|
+
end
|
160
|
+
|
161
|
+
describe '/auth/aleph' do
|
162
|
+
before(:each){ get '/auth/aleph' }
|
163
|
+
|
164
|
+
it 'should display a form' do
|
165
|
+
expect(last_response.status).to be(200)
|
166
|
+
expect(last_response.body).to include("<form")
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'should have the callback as the action for the form' do
|
170
|
+
expect(last_response.body).to include("action='/auth/aleph/callback'")
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'should have a text field for each of the fields' do
|
174
|
+
expect(last_response.body.scan('<input').size).to be(2)
|
175
|
+
end
|
176
|
+
it 'should have a label of the form title' do
|
177
|
+
expect(last_response.body.scan('MY Aleph Authentication').size).to be > 1
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
describe 'post /auth/aleph/callback' do
|
182
|
+
context 'failure' do
|
183
|
+
context "when username is not present" do
|
184
|
+
it 'should redirect to error page' do
|
185
|
+
post('/auth/aleph/callback', {})
|
186
|
+
expect(last_response).to be_redirect
|
187
|
+
expect(last_response.headers['Location']).to match(%r{missing_credentials})
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context "when username is empty" do
|
192
|
+
it 'should redirect to error page' do
|
193
|
+
post('/auth/aleph/callback', {:username => ""})
|
194
|
+
expect(last_response).to be_redirect
|
195
|
+
expect(last_response.headers['Location']).to match(%r{missing_credentials})
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
context "when username is present" do
|
200
|
+
context "and password is not present" do
|
201
|
+
it 'should redirect to error page' do
|
202
|
+
post('/auth/aleph/callback', {:username => aleph_username})
|
203
|
+
expect(last_response).to be_redirect
|
204
|
+
expect(last_response.headers['Location']).to match(%r{missing_credentials})
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context "and password is empty" do
|
209
|
+
it 'should redirect to error page' do
|
210
|
+
post('/auth/aleph/callback', {:username => aleph_username, :password => ""})
|
211
|
+
expect(last_response).to be_redirect
|
212
|
+
expect(last_response.headers['Location']).to match(%r{missing_credentials})
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
context "when username and password are present" do
|
218
|
+
context "and authentication failed", vcr: { cassette_name: "invalid password" } do
|
219
|
+
it 'should redirect to error page' do
|
220
|
+
post('/auth/aleph/callback', {:username => aleph_username, :password => 'INVALID'})
|
221
|
+
expect(last_response).to be_redirect
|
222
|
+
expect(last_response.headers['Location']).to match(%r{Error in Verification})
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
context "and Aleph is down" do
|
227
|
+
it "should redirect to error page"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
context 'success', vcr: { cassette_name: "valid" } do
|
233
|
+
let(:auth_hash){ last_request.env['omniauth.auth'] }
|
234
|
+
|
235
|
+
it 'should not redirect to error page' do
|
236
|
+
post('/auth/aleph/callback', { username: aleph_username, password: aleph_password })
|
237
|
+
expect(last_response).not_to be_redirect
|
238
|
+
end
|
239
|
+
|
240
|
+
it 'should map user info to Auth Hash' do
|
241
|
+
post('/auth/aleph/callback', { username: aleph_username, password: aleph_password })
|
242
|
+
expect(auth_hash.uid).to eq('USERNAME')
|
243
|
+
expect(auth_hash.info.name).to eq('USERNAME, TEST-RECORD')
|
244
|
+
expect(auth_hash.info.nickname).to eq('USERNAME')
|
245
|
+
expect(auth_hash.info.email).to eq('username@library.edu')
|
246
|
+
expect(auth_hash.info.phone).to be_nil
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
$:.unshift File.expand_path('..', __FILE__)
|
2
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
3
|
+
require 'coveralls'
|
4
|
+
Coveralls.wear!
|
5
|
+
require 'rspec'
|
6
|
+
require 'rack/test'
|
7
|
+
require 'vcr'
|
8
|
+
require 'faraday'
|
9
|
+
require 'omniauth'
|
10
|
+
require 'omniauth-aleph'
|
11
|
+
require 'pry'
|
12
|
+
|
13
|
+
def aleph_host
|
14
|
+
@aleph_host ||= (ENV['ALEPH_HOST'] || 'aleph.library.edu')
|
15
|
+
end
|
16
|
+
|
17
|
+
def aleph_username
|
18
|
+
@aleph_username ||= (ENV['ALEPH_USERNAME'] || "USERNAME")
|
19
|
+
end
|
20
|
+
|
21
|
+
def aleph_password
|
22
|
+
@aleph_password ||= (ENV['ALEPH_PASSWORD'] || "PASSWORD")
|
23
|
+
end
|
24
|
+
|
25
|
+
def aleph_email
|
26
|
+
@aleph_email ||= (ENV['ALEPH_EMAIL'] || "username@library.edu")
|
27
|
+
end
|
28
|
+
|
29
|
+
def aleph_library
|
30
|
+
@aleph_library ||= (ENV['ALEPH_LIBRARY'] || "ADM50")
|
31
|
+
end
|
32
|
+
|
33
|
+
def aleph_sub_library
|
34
|
+
@aleph_sub_library ||= (ENV['ALEPH_SUB_LIBRARY'] || "SUB")
|
35
|
+
end
|
36
|
+
|
37
|
+
VCR.configure do |c|
|
38
|
+
c.cassette_library_dir = 'spec/vcr_cassettes'
|
39
|
+
c.configure_rspec_metadata!
|
40
|
+
c.hook_into :webmock
|
41
|
+
c.filter_sensitive_data("aleph.library.edu") { aleph_host }
|
42
|
+
c.filter_sensitive_data("USERNAME") { aleph_username }
|
43
|
+
c.filter_sensitive_data("username") { aleph_username.downcase }
|
44
|
+
c.filter_sensitive_data("verification=PASSWORD") { "verification=#{aleph_password}" }
|
45
|
+
c.filter_sensitive_data("username@library.edu") { aleph_email }
|
46
|
+
c.filter_sensitive_data("ADM50") { aleph_library }
|
47
|
+
c.filter_sensitive_data("SUB") { aleph_sub_library }
|
48
|
+
end
|
49
|
+
|
50
|
+
RSpec.configure do |config|
|
51
|
+
config.include Rack::Test::Methods
|
52
|
+
config.extend OmniAuth::Test::StrategyMacros, :type => :strategy
|
53
|
+
config.expect_with :rspec do |c|
|
54
|
+
c.syntax = :expect
|
55
|
+
end
|
56
|
+
# so we can use :vcr rather than :vcr => true;
|
57
|
+
# in RSpec 3 this will no longer be necessary.
|
58
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
59
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://aleph.library.edu/X?bor_id=USERNAME&library=ADM50&op=bor-auth&sub_library=SUB&verification=PASSWORD
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- Faraday v0.9.0
|
12
|
+
Accept-Encoding:
|
13
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
14
|
+
Accept:
|
15
|
+
- ! '*/*'
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 303
|
19
|
+
message: See Other
|
20
|
+
headers:
|
21
|
+
Location:
|
22
|
+
- http://guidetest.a.id.opendns.com/?url=aleph%2Elibrary%2Eedu%2FX%3Fbor%5Fid%3DUSERNAME%26library%3DADM50%26op%3Dbor%2Dauth%26sub%5Flibrary%3DSUB%26verification%3DPASSWORD
|
23
|
+
Content-Length:
|
24
|
+
- '0'
|
25
|
+
Connection:
|
26
|
+
- close
|
27
|
+
Date:
|
28
|
+
- Thu, 23 Jan 2014 01:50:00 GMT
|
29
|
+
Server:
|
30
|
+
- OpenDNS Guide
|
31
|
+
body:
|
32
|
+
encoding: US-ASCII
|
33
|
+
string: ''
|
34
|
+
http_version:
|
35
|
+
recorded_at: Thu, 23 Jan 2014 01:49:59 GMT
|
36
|
+
recorded_with: VCR 2.8.0
|
@@ -0,0 +1,48 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://example.com/X?bor_id=USERNAME&library=ADM50&op=bor-auth&sub_library=SUB&verification=PASSWORD
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- Faraday v0.9.0
|
12
|
+
Accept-Encoding:
|
13
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
14
|
+
Accept:
|
15
|
+
- ! '*/*'
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Accept-Ranges:
|
22
|
+
- bytes
|
23
|
+
Cache-Control:
|
24
|
+
- max-age=604800
|
25
|
+
Content-Type:
|
26
|
+
- text/xml
|
27
|
+
Date:
|
28
|
+
- Wed, 22 Jan 2014 16:55:24 GMT
|
29
|
+
Etag:
|
30
|
+
- ! '"359670651"'
|
31
|
+
Expires:
|
32
|
+
- Wed, 29 Jan 2014 16:55:24 GMT
|
33
|
+
Last-Modified:
|
34
|
+
- Fri, 09 Aug 2013 23:54:35 GMT
|
35
|
+
Server:
|
36
|
+
- ECS (ewr/15E3)
|
37
|
+
X-Cache:
|
38
|
+
- HIT
|
39
|
+
X-Ec-Custom-Error:
|
40
|
+
- '1'
|
41
|
+
Content-Length:
|
42
|
+
- '1'
|
43
|
+
body:
|
44
|
+
encoding: US-ASCII
|
45
|
+
string: ! " "
|
46
|
+
http_version:
|
47
|
+
recorded_at: Wed, 22 Jan 2014 16:55:24 GMT
|
48
|
+
recorded_with: VCR 2.8.0
|
@@ -0,0 +1,64 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://example.com/X?bor_id=USERNAME&library=ADM50&op=bor-auth&sub_library=SUB&verification=PASSWORD
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- Faraday v0.9.0
|
12
|
+
Accept-Encoding:
|
13
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
14
|
+
Accept:
|
15
|
+
- ! '*/*'
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Accept-Ranges:
|
22
|
+
- bytes
|
23
|
+
Cache-Control:
|
24
|
+
- max-age=604800
|
25
|
+
Content-Type:
|
26
|
+
- text/html
|
27
|
+
Date:
|
28
|
+
- Wed, 22 Jan 2014 16:55:24 GMT
|
29
|
+
Etag:
|
30
|
+
- ! '"359670651"'
|
31
|
+
Expires:
|
32
|
+
- Wed, 29 Jan 2014 16:55:24 GMT
|
33
|
+
Last-Modified:
|
34
|
+
- Fri, 09 Aug 2013 23:54:35 GMT
|
35
|
+
Server:
|
36
|
+
- ECS (ewr/15E3)
|
37
|
+
X-Cache:
|
38
|
+
- HIT
|
39
|
+
X-Ec-Custom-Error:
|
40
|
+
- '1'
|
41
|
+
Content-Length:
|
42
|
+
- '1270'
|
43
|
+
body:
|
44
|
+
encoding: US-ASCII
|
45
|
+
string: ! "<!doctype html>\n<html>\n<head>\n <title>Example Domain</title>\n\n
|
46
|
+
\ <meta charset=\"utf-8\" />\n <meta http-equiv=\"Content-type\" content=\"text/html;
|
47
|
+
charset=utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width,
|
48
|
+
initial-scale=1\" />\n <style type=\"text/css\">\n body {\n background-color:
|
49
|
+
#f0f0f2;\n margin: 0;\n padding: 0;\n font-family: \"Open
|
50
|
+
Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n \n }\n
|
51
|
+
\ div {\n width: 600px;\n margin: 5em auto;\n padding:
|
52
|
+
50px;\n background-color: #fff;\n border-radius: 1em;\n }\n
|
53
|
+
\ a:link, a:visited {\n color: #38488f;\n text-decoration:
|
54
|
+
none;\n }\n @media (max-width: 700px) {\n body {\n background-color:
|
55
|
+
#fff;\n }\n div {\n width: auto;\n margin:
|
56
|
+
0 auto;\n border-radius: 0;\n padding: 1em;\n }\n
|
57
|
+
\ }\n </style> \n</head>\n\n<body>\n<div>\n <h1>Example Domain</h1>\n
|
58
|
+
\ <p>This domain is established to be used for illustrative examples in
|
59
|
+
documents. You may use this\n domain in examples without prior coordination
|
60
|
+
or asking for permission.</p>\n <p><a href=\"http://www.iana.org/domains/example\">More
|
61
|
+
information...</a></p>\n</div>\n</body>\n</html>\n"
|
62
|
+
http_version:
|
63
|
+
recorded_at: Wed, 22 Jan 2014 16:55:24 GMT
|
64
|
+
recorded_with: VCR 2.8.0
|