omniauth-aleph 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,24 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
19
+
20
+ # Ignore the Gemfile.lock
21
+ /Gemfile.lock
22
+
23
+ # Ignore .ruby-version
24
+ .ruby-version
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - jruby-19mode
7
+ - rbx
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
3
+
4
+ gem "coveralls", "~> 0.7.0", require: false, group: :test
5
+ gem "pry-debugger", group: :development, platform: :mri
6
+ gem "pry", group: :development, platforms: [:jruby, :rbx]
7
+
8
+ platforms :rbx do
9
+ gem 'rubysl', '~> 2.0' # if using anything in the ruby standard library
10
+ gem 'json', '~> 1.8.1'
11
+ gem 'rubinius-coverage'
12
+ end
13
+
14
+ # XML parsing
15
+ gem 'ox', '~> 2.0.0', platform: :ruby
16
+ gem 'nokogiri', '~> 1.6.1', platform: :jruby
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Scot Dalton
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # OmniAuth Aleph
2
+ [![Build Status](https://api.travis-ci.org/scotdalton/omniauth-aleph.png?branch=master)](https://travis-ci.org/scotdalton/omniauth-aleph)
3
+ [![Dependency Status](https://gemnasium.com/scotdalton/omniauth-aleph.png)](https://gemnasium.com/scotdalton/omniauth-aleph)
4
+ [![Code Climate](https://codeclimate.com/github/scotdalton/omniauth-aleph.png)](https://codeclimate.com/github/scotdalton/omniauth-aleph)
5
+ [![Coverage Status](https://coveralls.io/repos/scotdalton/omniauth-aleph/badge.png?branch=master)](https://coveralls.io/r/scotdalton/omniauth-aleph)
6
+
7
+ Aleph patron login strategy for OmniAuth.
8
+
9
+ ## Installation
10
+ Add to your Gemfile:
11
+
12
+ gem 'omniauth-aleph'
13
+
14
+ Then `bundle install`.
15
+
16
+ ## Usage
17
+ `OmniAuth::Strategies::Aleph` simply makes a call to the Aleph bor_auth X-Service and
18
+ returns the attributes from the returned XML.
19
+
20
+ Use the Aleph strategy as a middleware in your application:
21
+
22
+ use OmniAuth::Strategies::Aleph, title: 'My Library's Aleph',
23
+ host: 'aleph.library.edu', port: 80, library: 'ADM50', sub_library: 'SUB'
24
+
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc 'Default: run specs.'
6
+ task :default => :spec
7
+
8
+ desc "Run specs"
9
+ RSpec::Core::RakeTask.new
@@ -0,0 +1 @@
1
+ require "omniauth/aleph"
@@ -0,0 +1,3 @@
1
+ require 'omniauth/aleph/version'
2
+ require 'omniauth/aleph/adaptor'
3
+ require 'omniauth/strategies/aleph'
@@ -0,0 +1,57 @@
1
+ module OmniAuth
2
+ module Aleph
3
+ require 'faraday'
4
+ require 'multi_xml'
5
+ class Adaptor
6
+ class AlephError < StandardError; end
7
+
8
+ # List of keys, all are required.
9
+ KEYS = [:scheme, :host, :port, :library, :sub_library]
10
+
11
+ def self.validate(configuration={})
12
+ message = []
13
+ KEYS.each do |key|
14
+ message << key if(configuration[key].nil?)
15
+ end
16
+ unless message.empty?
17
+ raise ArgumentError.new(message.join(",") +" MUST be provided")
18
+ end
19
+ end
20
+
21
+ def initialize(configuration={})
22
+ self.class.validate(configuration)
23
+ @configuration = configuration.dup
24
+ @logger = @configuration.delete(:logger)
25
+ KEYS.each do |key|
26
+ instance_variable_set("@#{key}", @configuration[key])
27
+ end
28
+ end
29
+
30
+ def authenticate(username, password)
31
+ url = bor_auth_url + "&bor_id=#{username}&verification=#{password}"
32
+ response = Faraday.get url
33
+ # If we get a successful response AND we are looking at XML and we have a body
34
+ if response.status == 200 && response.headers["content-type"] == 'text/xml' && response.body
35
+ json = MultiXml.parse(response.body)
36
+ if json["bor_auth"] && (error = json["bor_auth"]["error"]).nil?
37
+ return json
38
+ elsif json["bor_auth"].nil?
39
+ raise AlephError.new("Aleph responded, but it's not a response I understand.")
40
+ else
41
+ raise AlephError.new(error)
42
+ end
43
+ else
44
+ raise AlephError.new("Aleph response:\n\t #{@response.inspect}.")
45
+ end
46
+ rescue Faraday::ConnectionFailed => e
47
+ raise AlephError.new("Couldn't connect to Aleph.")
48
+ end
49
+
50
+ def bor_auth_url
51
+ @bor_auth_url ||= "#{@scheme}://#{@host}:#{@port}"+
52
+ "/X?op=bor-auth&library=#{@library}&sub_library=#{@sub_library}"
53
+ end
54
+ private :bor_auth_url
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,5 @@
1
+ module OmniAuth
2
+ module Aleph
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,61 @@
1
+ require 'omniauth'
2
+
3
+ module OmniAuth
4
+ module Strategies
5
+ class Aleph
6
+ include OmniAuth::Strategy
7
+
8
+ # Set defaults for options
9
+ option :title, "Aleph Authentication"
10
+ option :scheme, 'http'
11
+ option :port, 80
12
+
13
+ uid { @raw_info["bor_auth"]["z303"]["z303_id"] }
14
+
15
+ info do
16
+ {
17
+ 'name' => @raw_info["bor_auth"]["z303"]["z303_name"],
18
+ 'nickname' => @raw_info["bor_auth"]["z303"]["z303_id"],
19
+ 'email' => @raw_info["bor_auth"]["z304"]["z304_email_address"],
20
+ 'phone' => @raw_info["bor_auth"]["z304"]["z304_telephone"]
21
+ }
22
+ end
23
+
24
+ extra do
25
+ (skip_info?) ? {} : { 'raw_info' => @raw_info }
26
+ end
27
+
28
+ def request_phase
29
+ OmniAuth::Aleph::Adaptor.validate @options
30
+ OmniAuth::Form.build(title: options[:title], url: callback_path) do |f|
31
+ f.text_field 'Login', 'username'
32
+ f.password_field 'Password', 'password'
33
+ end.to_response
34
+ end
35
+
36
+ def callback_phase
37
+ return fail!(:missing_credentials) if missing_credentials?
38
+ adaptor = OmniAuth::Aleph::Adaptor.new(@options)
39
+ @raw_info = adaptor.authenticate(username, password)
40
+ super
41
+ rescue OmniAuth::Aleph::Adaptor::AlephError => e
42
+ fail!(e.message)
43
+ end
44
+
45
+ def username
46
+ @username ||= request['username']
47
+ end
48
+ private :username
49
+
50
+ def password
51
+ @password ||= request['password']
52
+ end
53
+ private :password
54
+
55
+ def missing_credentials?
56
+ username.nil? || username.empty? || password.nil? || password.empty?
57
+ end
58
+ private :missing_credentials?
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/omniauth/aleph/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = 'omniauth-aleph'
6
+ gem.version = OmniAuth::Aleph::VERSION
7
+ gem.authors = ['Scot Dalton']
8
+ gem.email = ['scotdalton@gmail.com']
9
+ gem.summary = 'Aleph Patron Login Strategy for OmniAuth'
10
+ gem.homepage = 'https://github.com/scotdalton/omniauth-aleph'
11
+ gem.license = 'MIT'
12
+
13
+ gem.files = `git ls-files`.split("\n")
14
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
16
+ gem.require_paths = ['lib']
17
+
18
+ gem.add_runtime_dependency 'omniauth', '~> 1.2.0'
19
+ gem.add_runtime_dependency 'faraday', '~> 0.9.0'
20
+ gem.add_runtime_dependency 'multi_xml', '~> 0.5.0'
21
+
22
+ gem.add_development_dependency 'rake', '~> 10.1.0'
23
+ gem.add_development_dependency 'rspec', '~> 2.14.0'
24
+ gem.add_development_dependency 'rack-test', '~> 0.6.2'
25
+ gem.add_development_dependency 'webmock', '~> 1.17.0'
26
+ gem.add_development_dependency 'vcr', '~> 2.8.0'
27
+ end
@@ -0,0 +1,217 @@
1
+ require 'spec_helper'
2
+ describe "OmniAuth::Aleph::Adaptor" do
3
+ context 'when the configuration is missing :scheme' do
4
+ let(:config) do
5
+ { host: 'aleph.library.edu', port: 80, library: 'ADM50', sub_library: 'SUB' }
6
+ end
7
+
8
+ describe '.validate' do
9
+ it "should raise an ArgumentError" do
10
+ expect{ OmniAuth::Aleph::Adaptor.validate config }.to raise_error(ArgumentError)
11
+ end
12
+ end
13
+
14
+ describe '.new' do
15
+ it "should raise an ArgumentError" do
16
+ expect{ OmniAuth::Aleph::Adaptor.new config }.to raise_error(ArgumentError)
17
+ end
18
+ end
19
+ end
20
+
21
+ context 'when the configuration is missing :host' do
22
+ let(:config) do
23
+ { scheme: 'http', port: 80, library: 'ADM50', sub_library: 'SUB' }
24
+ end
25
+
26
+ describe '.validate' do
27
+ it "should raise an ArgumentError" do
28
+ expect{ OmniAuth::Aleph::Adaptor.validate config }.to raise_error(ArgumentError)
29
+ end
30
+ end
31
+
32
+ describe '.new' do
33
+ it "should raise an ArgumentError" do
34
+ expect{ OmniAuth::Aleph::Adaptor.new config }.to raise_error(ArgumentError)
35
+ end
36
+ end
37
+ end
38
+
39
+ context 'when the configuration is missing :port' do
40
+ let(:config) do
41
+ { scheme: 'http', host: 'aleph.library.edu', library: 'ADM50', sub_library: 'SUB' }
42
+ end
43
+
44
+ describe '.validate' do
45
+ it "should raise an ArgumentError" do
46
+ expect{ OmniAuth::Aleph::Adaptor.validate config }.to raise_error(ArgumentError)
47
+ end
48
+ end
49
+
50
+ describe '.new' do
51
+ it "should raise an ArgumentError" do
52
+ expect{ OmniAuth::Aleph::Adaptor.new config }.to raise_error(ArgumentError)
53
+ end
54
+ end
55
+ end
56
+
57
+ context 'when the configuration is missing :library' do
58
+ let(:config) do
59
+ { scheme: 'http', host: 'aleph.library.edu', port: 80, sub_library: 'SUB' }
60
+ end
61
+
62
+ describe '.validate' do
63
+ it "should raise an ArgumentError" do
64
+ expect{ OmniAuth::Aleph::Adaptor.validate config }.to raise_error(ArgumentError)
65
+ end
66
+ end
67
+
68
+ describe '.new' do
69
+ it "should raise an ArgumentError" do
70
+ expect{ OmniAuth::Aleph::Adaptor.new config }.to raise_error(ArgumentError)
71
+ end
72
+ end
73
+ end
74
+
75
+ context 'when the configuration is missing :sub_library' do
76
+ let(:config) do
77
+ { scheme: 'http', host: 'aleph.library.edu', port: 80, library: 'ADM50' }
78
+ end
79
+
80
+ describe '.validate' do
81
+ it "should raise an ArgumentError" do
82
+ expect{ OmniAuth::Aleph::Adaptor.validate config }.to raise_error(ArgumentError)
83
+ end
84
+ end
85
+
86
+ describe '.new' do
87
+ it "should raise an ArgumentError" do
88
+ expect{ OmniAuth::Aleph::Adaptor.new config }.to raise_error(ArgumentError)
89
+ end
90
+ end
91
+ end
92
+
93
+ context 'when the configuration has all required fields' do
94
+ let(:config) do
95
+ { scheme: 'http', host: aleph_host,
96
+ port: 80, library: aleph_library,
97
+ sub_library: aleph_sub_library }
98
+ end
99
+
100
+ subject(:adaptor) { OmniAuth::Aleph::Adaptor.new(config) }
101
+
102
+ describe '.validate' do
103
+ it "shouldn't raise an error" do
104
+ expect{ OmniAuth::Aleph::Adaptor.validate config }.not_to raise_error
105
+ end
106
+ end
107
+
108
+ context 'when the username and password are correct' do
109
+ describe '#authenticate', vcr: { cassette_name: "valid" } do
110
+ it "shouldn't raise an error" do
111
+ expect{ adaptor.authenticate(aleph_username, aleph_password) }.not_to raise_error
112
+ end
113
+
114
+ let(:user_info) { adaptor.authenticate(aleph_username, aleph_password) }
115
+ it "should return a hash" do
116
+ expect(user_info).to be_a(Hash)
117
+ end
118
+
119
+ it "should return 'USERNAME' as the username" do
120
+ expect(user_info["bor_auth"]["z303"]["z303_id"]).to eql("USERNAME")
121
+ end
122
+
123
+ it "should return 'username@library.nyu.edu' as the email" do
124
+ expect(user_info["bor_auth"]["z304"]["z304_email_address"]).to eql("username@library.edu")
125
+ end
126
+
127
+ it "should return 'USERNAME, TEST-RECORD' as the name" do
128
+ expect(user_info["bor_auth"]["z303"]["z303_name"]).to eql("USERNAME, TEST-RECORD")
129
+ end
130
+ end
131
+ end
132
+
133
+ context 'when the password is invalid' do
134
+ describe '#authenticate', vcr: { cassette_name: "invalid password" } do
135
+ it "should raise an Aleph error" do
136
+ expect{ adaptor.authenticate(aleph_username, "INVALID") }.to raise_error(OmniAuth::Aleph::Adaptor::AlephError)
137
+ end
138
+ end
139
+ end
140
+
141
+ context 'when the password is nil' do
142
+ describe '#authenticate', vcr: { cassette_name: "nil password" } do
143
+ it "should raise an Aleph error" do
144
+ expect{ adaptor.authenticate(aleph_username, nil) }.to raise_error(OmniAuth::Aleph::Adaptor::AlephError)
145
+ end
146
+ end
147
+ end
148
+
149
+ context 'when the password is empty' do
150
+ describe '#authenticate', vcr: { cassette_name: "empty password" } do
151
+ it "should raise an Aleph error" do
152
+ expect{ adaptor.authenticate(aleph_username, "") }.to raise_error(OmniAuth::Aleph::Adaptor::AlephError)
153
+ end
154
+ end
155
+ end
156
+
157
+ context 'when the user is does not exist' do
158
+ describe '#authenticate', vcr: { cassette_name: "nonexistent user" } do
159
+ it "should raise an Aleph error" do
160
+ expect{ adaptor.authenticate("NONEXISTENTUSER", "NONEXISTENTPASSWORD") }.to raise_error(OmniAuth::Aleph::Adaptor::AlephError)
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ context 'when the aleph host doesn\'t connect' do
167
+ let(:config) do
168
+ { scheme: 'http', host: "aleph.library.edu",
169
+ port: 80, library: "ADM50",
170
+ sub_library: "SUB" }
171
+ end
172
+
173
+ subject(:adaptor) { OmniAuth::Aleph::Adaptor.new(config) }
174
+
175
+ describe '#authenticate', :vcr do
176
+ it "should raise an Aleph error" do
177
+ expect{ adaptor.authenticate("USERNAME", "PASSWORD") }.to raise_error(OmniAuth::Aleph::Adaptor::AlephError)
178
+ end
179
+ end
180
+ end
181
+
182
+ context 'when the aleph host is not actually Aleph' do
183
+ let(:config) do
184
+ { scheme: 'http', host: "example.com",
185
+ port: 80, library: "ADM50",
186
+ sub_library: "SUB" }
187
+ end
188
+
189
+ subject(:adaptor) { OmniAuth::Aleph::Adaptor.new(config) }
190
+
191
+ describe '#authenticate' do
192
+ context 'when the host returns HTML', vcr: { cassette_name: "aleph host returns html" } do
193
+ it "should raise an Aleph error" do
194
+ expect{ adaptor.authenticate("USERNAME", "PASSWORD") }.to raise_error(OmniAuth::Aleph::Adaptor::AlephError)
195
+ end
196
+ end
197
+
198
+ context 'when the host returns an empty body', vcr: { cassette_name: "aleph host returns an empty body" } do
199
+ it "should raise an Aleph error" do
200
+ expect{ adaptor.authenticate("USERNAME", "PASSWORD") }.to raise_error(OmniAuth::Aleph::Adaptor::AlephError)
201
+ end
202
+ end
203
+
204
+ context 'when the host returns with status not found', vcr: { cassette_name: "aleph host returns with status not found" } do
205
+ it "should raise an Aleph error" do
206
+ expect{ adaptor.authenticate("USERNAME", "PASSWORD") }.to raise_error(OmniAuth::Aleph::Adaptor::AlephError)
207
+ end
208
+ end
209
+
210
+ context 'when the host returns non bor auth xml', vcr: { cassette_name: "aleph host returns non bor auth xml" } do
211
+ it "should raise an Aleph error" do
212
+ expect{ adaptor.authenticate("USERNAME", "PASSWORD") }.to raise_error(OmniAuth::Aleph::Adaptor::AlephError)
213
+ end
214
+ end
215
+ end
216
+ end
217
+ end