hancock-client 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.md +49 -0
- data/Rakefile +58 -0
- data/lib/hancock-client.rb +26 -0
- data/lib/rack-openid.rb +195 -0
- data/lib/sinatra/hancock/sso.rb +67 -0
- data/spec/client.rb +16 -0
- data/spec/hancock_sinatra_spec.rb +12 -0
- data/spec/spec_helper.rb +28 -0
- metadata +85 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Corey Donohoe <cdonohoe@engineyard.com, Tim Carey-Smith <tim@spork.in>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
hancock-client
|
2
|
+
==============
|
3
|
+
|
4
|
+
A gem that integrates [sinatra][sinatra] applications into the Hancock SSO
|
5
|
+
environment. It also doubles as rack middleware that can be used in
|
6
|
+
rails(>= 2.3.2) and merb(>= 1.0)
|
7
|
+
|
8
|
+
Dependencies
|
9
|
+
============
|
10
|
+
% gem sources
|
11
|
+
*** CURRENT SOURCES ***
|
12
|
+
|
13
|
+
http://gems.rubyforge.org
|
14
|
+
% sudo gem sources -a http://gems.github.com
|
15
|
+
http://gems.github.com added to sources
|
16
|
+
|
17
|
+
% sudo gem install sinatra-sinatra
|
18
|
+
% sudo gem install atmos-hancock
|
19
|
+
|
20
|
+
testing
|
21
|
+
=======
|
22
|
+
Rake works but I'm not 100% sure how to test this correctly
|
23
|
+
|
24
|
+
Application
|
25
|
+
===========
|
26
|
+
The goal is to make it simple to write sso enabled apps.
|
27
|
+
|
28
|
+
require 'rubygems'
|
29
|
+
require 'sinatra'
|
30
|
+
require 'sinatra/base'
|
31
|
+
require 'hancock-client'
|
32
|
+
|
33
|
+
run Hancock::Client
|
34
|
+
class ConsumerApp < Hancock::Client::Default
|
35
|
+
set :sso_url, 'http://hancock.atmos.org/sso'
|
36
|
+
|
37
|
+
set :views, 'views'
|
38
|
+
set :public, 'public'
|
39
|
+
set :environment, :production
|
40
|
+
|
41
|
+
get '/' do
|
42
|
+
redirect '/login' unless session[:user_id]
|
43
|
+
haml(%Q{%h3= "#{session[:first_name]} #{session[:last_name]} - #{session[:email]}"})
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
run ConsumerApp
|
48
|
+
|
49
|
+
[sinatra]: http://www.sinatrarb.com
|
data/Rakefile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rubygems/specification'
|
4
|
+
require 'date'
|
5
|
+
require 'spec/rake/spectask'
|
6
|
+
|
7
|
+
GEM = "hancock-client"
|
8
|
+
GEM_VERSION = "0.0.2"
|
9
|
+
AUTHOR = "Corey Donohoe"
|
10
|
+
EMAIL = ['atmos@atmos.org', 'tim@spork.in']
|
11
|
+
HOMEPAGE = "http://github.com/atmos/hancock-client"
|
12
|
+
SUMMARY = "A gem that SSO enables sinatra applications with hancock"
|
13
|
+
|
14
|
+
spec = Gem::Specification.new do |s|
|
15
|
+
s.name = GEM
|
16
|
+
s.version = GEM_VERSION
|
17
|
+
s.platform = Gem::Platform::RUBY
|
18
|
+
s.has_rdoc = true
|
19
|
+
s.extra_rdoc_files = ["README.md", "LICENSE"]
|
20
|
+
s.summary = SUMMARY
|
21
|
+
s.description = s.summary
|
22
|
+
s.author = AUTHOR
|
23
|
+
s.email = EMAIL
|
24
|
+
s.homepage = HOMEPAGE
|
25
|
+
|
26
|
+
# Uncomment this to add a dependency
|
27
|
+
s.add_dependency "sinatra", ">=0.9.1.1"
|
28
|
+
s.add_dependency "ruby-openid", ">=2.1.2"
|
29
|
+
|
30
|
+
s.require_path = 'lib'
|
31
|
+
s.autorequire = GEM
|
32
|
+
s.files = %w(LICENSE README.md Rakefile) + Dir.glob("{lib,spec}/**/*")
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => :spec
|
36
|
+
|
37
|
+
desc "Run specs"
|
38
|
+
Spec::Rake::SpecTask.new do |t|
|
39
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
40
|
+
t.spec_opts = %w(-fs --color)
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
45
|
+
pkg.gem_spec = spec
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "install the gem locally"
|
49
|
+
task :install => [:package] do
|
50
|
+
sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "create a gemspec file"
|
54
|
+
task :make_spec do
|
55
|
+
File.open("#{GEM}.gemspec", "w") do |file|
|
56
|
+
file.puts spec.to_ruby
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
gem 'sinatra', '~>0.9.1'
|
2
|
+
require 'rack'
|
3
|
+
require 'sinatra/base'
|
4
|
+
gem 'dm-core', '~>0.9.10'
|
5
|
+
require 'dm-core'
|
6
|
+
|
7
|
+
gem 'ruby-openid', '>=2.1.2'
|
8
|
+
require 'openid'
|
9
|
+
|
10
|
+
require File.dirname(__FILE__)+'/sinatra/hancock/sso'
|
11
|
+
|
12
|
+
module Hancock
|
13
|
+
module Client
|
14
|
+
class Default < ::Sinatra::Default
|
15
|
+
enable :sessions
|
16
|
+
cattr_accessor :sso_url
|
17
|
+
|
18
|
+
register Sinatra::Hancock::SSO
|
19
|
+
|
20
|
+
def self.sso_configure(&block)
|
21
|
+
yield self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
data/lib/rack-openid.rb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'openid'
|
2
|
+
require 'openid/consumer'
|
3
|
+
require 'openid/extensions/sreg'
|
4
|
+
require 'openid/store/filesystem'
|
5
|
+
|
6
|
+
module Rack
|
7
|
+
class OpenID
|
8
|
+
def self.build_header(params = {})
|
9
|
+
value = 'OpenID '
|
10
|
+
value += params.map { |k, v|
|
11
|
+
if v.is_a?(Array)
|
12
|
+
"#{k}=\"#{v.join(',')}\""
|
13
|
+
else
|
14
|
+
"#{k}=\"#{v}\""
|
15
|
+
end
|
16
|
+
}.join(', ')
|
17
|
+
value
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse_header(str)
|
21
|
+
params = {}
|
22
|
+
if str =~ /^OpenID/
|
23
|
+
str = str.gsub(/^OpenID /, '')
|
24
|
+
str.split(', ').each { |e|
|
25
|
+
k, v = e.split('=')
|
26
|
+
v.gsub!(/^\"/, '').gsub!(/\"$/, "")
|
27
|
+
v = v.split(',')
|
28
|
+
params[k] = v.length > 1 ? v : v.first
|
29
|
+
}
|
30
|
+
end
|
31
|
+
params
|
32
|
+
end
|
33
|
+
|
34
|
+
class TimeoutResponse
|
35
|
+
include ::OpenID::Consumer::Response
|
36
|
+
STATUS = :failure
|
37
|
+
end
|
38
|
+
|
39
|
+
class MissingResponse
|
40
|
+
include ::OpenID::Consumer::Response
|
41
|
+
STATUS = :missing
|
42
|
+
end
|
43
|
+
|
44
|
+
HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS)
|
45
|
+
|
46
|
+
RESPONSE = "rack.openid.response".freeze
|
47
|
+
AUTHENTICATE_HEADER = "WWW-Authenticate".freeze
|
48
|
+
|
49
|
+
|
50
|
+
def initialize(app, store = nil)
|
51
|
+
@app = app
|
52
|
+
@store = store || ::OpenID::Store::Filesystem.new("tmp/openid")
|
53
|
+
freeze
|
54
|
+
end
|
55
|
+
|
56
|
+
def call(env)
|
57
|
+
req = Rack::Request.new(env)
|
58
|
+
if env["REQUEST_METHOD"] == "GET" && req.GET["openid.mode"]
|
59
|
+
complete_authentication(env)
|
60
|
+
end
|
61
|
+
|
62
|
+
status, headers, body = @app.call(env)
|
63
|
+
|
64
|
+
if status.to_i == 401 && (qs = headers[AUTHENTICATE_HEADER])
|
65
|
+
begin_authentication(env, qs)
|
66
|
+
else
|
67
|
+
[status, headers, body]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
def begin_authentication(env, qs)
|
73
|
+
req = Rack::Request.new(env)
|
74
|
+
params = self.class.parse_header(qs)
|
75
|
+
|
76
|
+
unless session = env["rack.session"]
|
77
|
+
raise RuntimeError, "Rack::OpenID requires a session"
|
78
|
+
end
|
79
|
+
|
80
|
+
consumer = ::OpenID::Consumer.new(session, @store)
|
81
|
+
identifier = params["identifier"]
|
82
|
+
|
83
|
+
begin
|
84
|
+
oidreq = consumer.begin(identifier)
|
85
|
+
add_simple_registration_fields(oidreq, params)
|
86
|
+
url = open_id_redirect_url(req, oidreq, params["trust_root"], params["return_to"], params["method"])
|
87
|
+
return redirect_to(url)
|
88
|
+
rescue ::OpenID::OpenIDError, Timeout::Error => e
|
89
|
+
env[RESPONSE] = MissingResponse.new
|
90
|
+
return self.call(env)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def complete_authentication(env)
|
95
|
+
req = Rack::Request.new(env)
|
96
|
+
|
97
|
+
unless session = env["rack.session"]
|
98
|
+
raise RuntimeError, "Rack::OpenID requires a session"
|
99
|
+
end
|
100
|
+
|
101
|
+
oidresp = timeout_protection_from_identity_server {
|
102
|
+
consumer = ::OpenID::Consumer.new(session, @store)
|
103
|
+
consumer.complete(req.params, req.url)
|
104
|
+
}
|
105
|
+
|
106
|
+
if oidresp.status == :failure
|
107
|
+
raise "Open ID Response is a failure: #{oidresp.inspect}"
|
108
|
+
end
|
109
|
+
|
110
|
+
env[RESPONSE] = oidresp
|
111
|
+
|
112
|
+
if method = req.GET["_method"]
|
113
|
+
method = method.upcase
|
114
|
+
if HTTP_METHODS.include?(method)
|
115
|
+
env["REQUEST_METHOD"] = method
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
query_hash = env["rack.request.query_hash"]
|
120
|
+
query_hash.delete("_method")
|
121
|
+
query_hash.delete_if do |key, value|
|
122
|
+
key =~ /^openid\./
|
123
|
+
end
|
124
|
+
|
125
|
+
env["QUERY_STRING"] = env["rack.request.query_string"] =
|
126
|
+
Rack::Utils.build_query(env["rack.request.query_hash"])
|
127
|
+
|
128
|
+
request_uri = env["PATH_INFO"]
|
129
|
+
if env["QUERY_STRING"].any?
|
130
|
+
request_uri << "?" + env["QUERY_STRING"]
|
131
|
+
end
|
132
|
+
env["REQUEST_URI"] = request_uri
|
133
|
+
end
|
134
|
+
|
135
|
+
def realm_url(req)
|
136
|
+
url = req.scheme + "://"
|
137
|
+
url << req.host
|
138
|
+
|
139
|
+
if req.scheme == "https" && req.port != 443 ||
|
140
|
+
req.scheme == "http" && req.port != 80
|
141
|
+
url << ":#{req.port}"
|
142
|
+
end
|
143
|
+
|
144
|
+
url
|
145
|
+
end
|
146
|
+
|
147
|
+
def request_url(req)
|
148
|
+
url = realm_url(req)
|
149
|
+
url << req.script_name
|
150
|
+
url << req.path_info
|
151
|
+
url
|
152
|
+
end
|
153
|
+
|
154
|
+
def redirect_to(url)
|
155
|
+
[303, {"Content-Type" => "text/html", "Location" => url}, []]
|
156
|
+
end
|
157
|
+
|
158
|
+
def open_id_redirect_url(req, oidreq, trust_root = nil, return_to = nil, method = nil)
|
159
|
+
if return_to
|
160
|
+
method ||= "get"
|
161
|
+
else
|
162
|
+
return_to = request_url(req)
|
163
|
+
method ||= req.request_method
|
164
|
+
end
|
165
|
+
|
166
|
+
method = method.to_s.downcase
|
167
|
+
oidreq.return_to_args['_method'] = method unless method == "get"
|
168
|
+
oidreq.redirect_url(trust_root || realm_url(req), return_to || request_url(req))
|
169
|
+
end
|
170
|
+
|
171
|
+
def add_simple_registration_fields(oidreq, fields)
|
172
|
+
sregreq = ::OpenID::SReg::Request.new
|
173
|
+
|
174
|
+
if required = fields["required"]
|
175
|
+
sregreq.request_fields(Array(required), true)
|
176
|
+
end
|
177
|
+
|
178
|
+
if optional = fields["optional"]
|
179
|
+
sregreq.request_fields(Array(optional), false)
|
180
|
+
end
|
181
|
+
|
182
|
+
if policy_url = fields["policy_url"]
|
183
|
+
sregreq.policy_url = policy_url
|
184
|
+
end
|
185
|
+
|
186
|
+
oidreq.add_extension(sregreq)
|
187
|
+
end
|
188
|
+
|
189
|
+
def timeout_protection_from_identity_server
|
190
|
+
yield
|
191
|
+
rescue Timeout::Error
|
192
|
+
TimeoutResponse.new
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require File.dirname(__FILE__)+'/../../rack-openid'
|
3
|
+
|
4
|
+
module Sinatra
|
5
|
+
module Hancock
|
6
|
+
module SSO
|
7
|
+
module Helpers
|
8
|
+
def sso_url
|
9
|
+
@app.class.sso_url
|
10
|
+
end
|
11
|
+
|
12
|
+
def absolute_url(suffix = nil)
|
13
|
+
port_part = case request.scheme
|
14
|
+
when "http"
|
15
|
+
request.port == 80 ? "" : ":#{request.port}"
|
16
|
+
when "https"
|
17
|
+
request.port == 443 ? "" : ":#{request.port}"
|
18
|
+
end
|
19
|
+
"#{request.scheme}://#{request.host}#{port_part}#{suffix}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.registered(app)
|
24
|
+
app.use(Rack::OpenID)
|
25
|
+
app.helpers Hancock::SSO::Helpers
|
26
|
+
app.enable :sessions
|
27
|
+
app.disable :raise_errors
|
28
|
+
app.before { ensure_sso_authenticated unless request.path_info =~ %r!^/sso/log(in|out)! }
|
29
|
+
|
30
|
+
app.get '/sso/login' do
|
31
|
+
if contact_id = params['id']
|
32
|
+
response['WWW-Authenticate'] = Rack::OpenID.build_header(
|
33
|
+
:identifier => "#{sso_url}/users/#{contact_id}",
|
34
|
+
:trust_root => absolute_url('/sso/login')
|
35
|
+
)
|
36
|
+
throw :halt, [401, 'got openid?']
|
37
|
+
elsif openid = request.env["rack.openid.response"]
|
38
|
+
if openid.status == :success
|
39
|
+
if contact_id = openid.display_identifier.split("/").last
|
40
|
+
session.delete(:last_oidreq)
|
41
|
+
session.delete('OpenID::Consumer::last_requested_endpoint')
|
42
|
+
session.delete('OpenID::Consumer::DiscoveredServices::OpenID::Consumer::')
|
43
|
+
|
44
|
+
session[:user_id] = contact_id
|
45
|
+
params = openid.message.get_args("http://openid.net/extensions/sreg/1.1")
|
46
|
+
params.each { |key, value| session[key.to_sym] = value.to_s }
|
47
|
+
redirect '/'
|
48
|
+
else
|
49
|
+
raise "No contact could be found for #{openid.display_identifier}"
|
50
|
+
end
|
51
|
+
else
|
52
|
+
throw :halt, [503, "Error: #{openid.status}"]
|
53
|
+
end
|
54
|
+
else
|
55
|
+
redirect "#{sso_url}/login?return_to=#{absolute_url('/sso/login')}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
app.get '/sso/logout' do
|
60
|
+
session.clear
|
61
|
+
redirect "#{sso_url}/logout"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
register Hancock::SSO
|
67
|
+
end
|
data/spec/client.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
|
3
|
+
module Hancock
|
4
|
+
module Spec
|
5
|
+
class Client < ::Hancock::Client::Default
|
6
|
+
get '/' do
|
7
|
+
redirect '/sso/login' unless session[:user_id]
|
8
|
+
haml(<<-HAML
|
9
|
+
%p
|
10
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
11
|
+
HAML
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe Sinatra::Hancock::SSO do
|
4
|
+
it "should protect the root url" do
|
5
|
+
get '/'
|
6
|
+
@response.headers['Location'].should eql('/sso/login')
|
7
|
+
end
|
8
|
+
it "should greet the user when authenticated" do
|
9
|
+
get '/', { }, :session => {:user_id => 42}
|
10
|
+
@response.body.should have_selector('p')
|
11
|
+
end
|
12
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
$:.push File.join(File.dirname(__FILE__), '..', 'lib')
|
2
|
+
require 'rubygems'
|
3
|
+
require 'pp'
|
4
|
+
gem 'rspec', '~>1.2.0'
|
5
|
+
require 'spec'
|
6
|
+
gem 'webrat', '~>0.4.2'
|
7
|
+
require 'webrat'
|
8
|
+
require 'dm-sweatshop'
|
9
|
+
|
10
|
+
gem 'sinatra', '~>0.9.1'
|
11
|
+
require 'sinatra/base'
|
12
|
+
require 'sinatra/test'
|
13
|
+
require 'sinatra/hancock/sso'
|
14
|
+
|
15
|
+
require 'hancock-client'
|
16
|
+
|
17
|
+
require File.dirname(__FILE__)+'/client'
|
18
|
+
|
19
|
+
Spec::Runner.configure do |config|
|
20
|
+
config.include(Sinatra::Test)
|
21
|
+
config.include(Webrat::Methods)
|
22
|
+
config.include(Webrat::Matchers)
|
23
|
+
config.before(:each) do
|
24
|
+
@app = Rack::Builder.new do
|
25
|
+
run Hancock::Spec::Client
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hancock-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Corey Donohoe
|
8
|
+
autorequire: hancock-client
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-20 00:00:00 -06:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: sinatra
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.9.1.1
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: ruby-openid
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.1.2
|
34
|
+
version:
|
35
|
+
description: A gem that SSO enables sinatra applications with hancock
|
36
|
+
email:
|
37
|
+
- atmos@atmos.org
|
38
|
+
- tim@spork.in
|
39
|
+
executables: []
|
40
|
+
|
41
|
+
extensions: []
|
42
|
+
|
43
|
+
extra_rdoc_files:
|
44
|
+
- README.md
|
45
|
+
- LICENSE
|
46
|
+
files:
|
47
|
+
- LICENSE
|
48
|
+
- README.md
|
49
|
+
- Rakefile
|
50
|
+
- lib/hancock-client.rb
|
51
|
+
- lib/rack-openid.rb
|
52
|
+
- lib/sinatra
|
53
|
+
- lib/sinatra/hancock
|
54
|
+
- lib/sinatra/hancock/sso.rb
|
55
|
+
- spec/client.rb
|
56
|
+
- spec/hancock_sinatra_spec.rb
|
57
|
+
- spec/spec_helper.rb
|
58
|
+
has_rdoc: true
|
59
|
+
homepage: http://github.com/atmos/hancock-client
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: "0"
|
70
|
+
version:
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: "0"
|
76
|
+
version:
|
77
|
+
requirements: []
|
78
|
+
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 1.3.1
|
81
|
+
signing_key:
|
82
|
+
specification_version: 2
|
83
|
+
summary: A gem that SSO enables sinatra applications with hancock
|
84
|
+
test_files: []
|
85
|
+
|