rack_my_openid 0.0.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 ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3@rails_openid_server
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rack_my_openid.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'rack-test', :git => 'git://github.com/brynary/rack-test.git'
8
+ end
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # rack_my_openid - a one-user OpenID provider solution for rack
2
+
3
+ So you have a Rack/Sinatra/Rails-powered blog and you want to make it an OpenID?
4
+
5
+ Now you can do it in under 5 minutes.
6
+
7
+ ## Operation details
8
+
9
+ Rack_my_openid is a simple, single-user OpenID provider inspired by (now deprecated) [phpMyId](http://siege.org/phpmyid.php). It uses
10
+
11
+ * [ruby-openid](https://github.com/openid/ruby-openid) for the protocol implementation;
12
+ * simple Yaml files for storing configuration;
13
+ * in-memory storage for authentication data;
14
+ * HTTP Digest authentication for security;
15
+ * Sinatra and Rack as the server backend.
16
+
17
+ It's designed to be drop-in compatible with any Rails application, since implementing OpenID is a confusing exercise even with ruby-openid. I extracted it from my own site/blog and am continuing to use it there.
18
+
19
+ It's fully covered by RSpec tests.
20
+
21
+ See the [OpenID specs](http://openid.net/specs/openid-authentication-2_0.html) if you really want to understand how the whole thing works.
22
+
23
+ ## Installation - Rails 3
24
+
25
+ * Add the `rack_my_openid` gem to your Gemfile
26
+ * Add this to your routes:
27
+
28
+ openid_provider = RackMyOpenid::Provider.new(YAML.load_file('config/rack_my_openid.yml'))
29
+ match '/openid' => openid_provider
30
+ match '/openid/*whatever' => openid_provider
31
+
32
+ The `/openid` path can't be changed, as of this release.
33
+
34
+ * Create a `config/rack_my_openid.yml` file (see below)
35
+ * Restart your Rails app and you're good to go.
36
+ * If you make any changes to the config you'll have to restart the app to pick them up.
37
+
38
+ ## Installation - Standalone
39
+
40
+ This assumes that the OpenID is the root path.
41
+
42
+ * Install the `rack_my_openid` gem.
43
+ * Create a `config.ru` in your desired path with these contents:
44
+
45
+ require 'rack_my_openid'
46
+ run RackMyOpenid::Provider.new(YAML.load_file('rack_my_openid.yml'))
47
+
48
+ * Create a `rack_my_openid.yml` file (see below) in the same path
49
+ * Create empty `/public` and `/tmp` directories in the same path
50
+ * Deploy with [Passenger](http://www.modrails.com/documentation/Users%20guide%20Nginx.html#deploying_a_rack_app), [Rackup](https://github.com/rack/rack/wiki/(tutorial)-rackup-howto) or whatever Rack handler you fancy.
51
+
52
+ ## `rack_my_openid.yml`
53
+
54
+ This is a simple flat Yaml file. The keys are symbols (as of this release).
55
+
56
+ * `:credentials` - run `md5 -s 'yourusername:rack_my_openid:yourpassword'` (or replace rack_my_openid with your realm name if you changed it);
57
+ * `:openid` - the actual OpenID identifier that you want to provide;
58
+ * `:realm` - the realm for HTTP Digest auth. The default is `"rack_my_openid"`, why would you change it?
59
+ * `:endpoint_url` - the URL of the OpenID endpoint (the one that's '/openid'). You shouldn't explicitly declare it
60
+
61
+ ## TODO
62
+
63
+ * Support stores other than memory store
64
+ * Support SReg data provision
65
+
66
+ ~ ~ ~
67
+
68
+ (c) Leonid Shevtsov http://leonid.shevtsov.me
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,89 @@
1
+ require 'openid/server'
2
+ require 'openid/store/memory'
3
+
4
+ module RackMyOpenid
5
+ class Handler
6
+ class BadRequest < RuntimeError; end
7
+ class UntrustedRealm < RuntimeError
8
+ attr_reader :realm
9
+ def initialize(realm); @realm = realm; end
10
+ def message; "OpenID realm #{@realm} not trusted by user."; end
11
+ end
12
+
13
+ def initialize(options)
14
+ @options = options
15
+ end
16
+
17
+ def handle(params, session)
18
+ if request = openid_server.decode_request(params)
19
+ return openid_server.encode_response handle_openid_request(request, session)
20
+ else
21
+ raise BadRequest
22
+ end
23
+ end
24
+
25
+ def cancel(params)
26
+ if request = openid_server.decode_request(params)
27
+ return openid_server.encode_response cancel_check_id_request(request)
28
+ else
29
+ raise BadRequest
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def openid_server
36
+ @openid_server ||= OpenID::Server::Server.new(@options[:endpoint_url], openid_store)
37
+ end
38
+
39
+ def openid_store
40
+ @openid_store ||= OpenID::Store::Memory
41
+ end
42
+
43
+ # Handle a valid OpenID request
44
+ #
45
+ # Requests are handled by the standard openid server,
46
+ # except the main one - which validates your OpenID.
47
+ # So we only have to handle a check_id request
48
+ def handle_openid_request(request, session)
49
+ case request
50
+ when OpenID::Server::CheckIDRequest
51
+ return handle_check_id_request(request, session)
52
+ else
53
+ return openid_server.handle_request(request)
54
+ end
55
+ end
56
+
57
+ def handle_check_id_request(request, session)
58
+ if request_provided_invalid_openid?(request)
59
+ return request.answer(false)
60
+ elsif trusted_realm?(request.realm, session)
61
+ return request.answer(true, nil, @options[:openid])
62
+ else
63
+ raise UntrustedRealm.new(request.realm)
64
+ end
65
+ end
66
+
67
+ def cancel_check_id_request(request)
68
+ request.answer(false)
69
+ end
70
+
71
+ # Returns true if the request's claimed id is different from ours
72
+ #
73
+ # OK, this is an 'inverted truth' method, but it
74
+ # seems appropriate since the opposite assertion isn't
75
+ # 'inverted' (request can either not pass an id or
76
+ # pass a valid id)
77
+ def request_provided_invalid_openid?(request)
78
+ !request.id_select && (request.claimed_id != @options[:openid])
79
+ end
80
+
81
+ def trusted_realm?(realm, session)
82
+ trusted_realms(session).include? realm
83
+ end
84
+
85
+ def trusted_realms(session={})
86
+ @options.fetch(:trusted_realms, []) + session.fetch(:trusted_realms, [])
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,91 @@
1
+ require 'cgi'
2
+ require 'sinatra/base'
3
+ require 'rack_my_openid/handler'
4
+
5
+ module RackMyOpenid
6
+ class Provider < Sinatra::Base
7
+ set :root, File.dirname(__FILE__)
8
+
9
+ use Rack::Session::Pool
10
+
11
+ # authorization
12
+ def authorize!
13
+ response = @auth.call(request.env)
14
+ unless response===true
15
+ throw(:halt, response)
16
+ end
17
+ end
18
+
19
+ # Options can contain
20
+ #
21
+ # * :credentials (required) - `md5 -s 'username:realm:password'`
22
+ # * :openid (required) - the OpenID you want to authorize
23
+ # * :realm (optional, the default is "rack_my_openid") - an arbitrary resource name for HTTP Authentication
24
+ def initialize(options = {})
25
+ super()
26
+ @options = default_options.merge options
27
+ @auth = Rack::Auth::Digest::MD5.new(lambda{|e| true}, @options[:realm]) do
28
+ @options[:credentials]
29
+ end
30
+ @auth.opaque = $$.to_s
31
+ @auth.passwords_hashed = true
32
+ end
33
+
34
+ get '/openid' do
35
+ if params.empty?
36
+ erb :endpoint
37
+ else
38
+ authorize!
39
+ @options[:endpoint_url] ||= "#{request.scheme}://#{request.host_with_port}#{request.fullpath}"
40
+ begin
41
+ render_openid_response RackMyOpenid::Handler.new(@options).handle(params, session)
42
+ rescue RackMyOpenid::Handler::BadRequest
43
+ status 400
44
+ body 'Bad Request'
45
+ rescue RackMyOpenid::Handler::UntrustedRealm => e
46
+ session[:openid_request_params] = params
47
+ session[:realm] = e.realm
48
+ redirect to('/openid/decide'), 302
49
+ end
50
+ end
51
+ end
52
+
53
+ get '/openid/decide' do
54
+ authorize!
55
+ if @realm = session[:realm]
56
+ erb :decide
57
+ else
58
+ redirect to('/openid'), 302
59
+ end
60
+ end
61
+
62
+ post '/openid/decide' do
63
+ authorize!
64
+ @realm = session.delete(:realm)
65
+ begin
66
+ if params[:yes]
67
+ session[:trusted_realms] ||= []
68
+ session[:trusted_realms] << @realm
69
+ response = RackMyOpenid::Handler.new(@options).handle(session[:openid_request_params], session)
70
+ else
71
+ response RackMyOpenid::Handler.new(@options).cancel(session[:openid_request_params])
72
+ end
73
+ render_openid_response response
74
+ rescue RackMyOpenid::Handler::BadRequest
75
+ erb :expired
76
+ end
77
+ end
78
+
79
+ def render_openid_response(response)
80
+ status response.code
81
+ headers response.headers
82
+ body response.body
83
+ end
84
+
85
+ def default_options
86
+ {
87
+ :realm => 'rack_my_openid'
88
+ }
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,3 @@
1
+ module RackMyOpenid
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,7 @@
1
+ <form method="POST" action="<%=request.path_info%>">
2
+ <p>
3
+ Do you wish to trust <strong><%=@realm %></strong> with your identity?
4
+ </p>
5
+ <input type="submit" value="Yes" name="yes">
6
+ <input type="submit" value="No" name="no">
7
+ </form>
@@ -0,0 +1,3 @@
1
+ <p>
2
+ This is an OpenID endpoint.
3
+ </p>
@@ -0,0 +1,3 @@
1
+ <p>
2
+ Your session has expired. Please try authenticating again.
3
+ </p>
@@ -0,0 +1,9 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>rack_my_openid OpenID endpoint</title>
5
+ </head>
6
+ <body>
7
+ <%= yield %>
8
+ </body>
9
+ </html>
@@ -0,0 +1,6 @@
1
+ require 'rack_my_openid/version'
2
+ require 'rack_my_openid/provider'
3
+
4
+ module RackMyOpenid
5
+
6
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rack_my_openid/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rack_my_openid"
7
+ s.version = RackMyOpenid::VERSION
8
+ s.authors = ["Leonid Shevtsov"]
9
+ s.email = ["leonid@shevtsov.me"]
10
+ s.homepage = "https://github.com/leonid-shevtsov/rack_my_openid"
11
+ s.summary = %q{Single-user OpenID provider implemented as a Rack (Sinatra) application.}
12
+ s.description = %q{Would be useful to enable OpenID authorisation with your Ruby/Rails-based blog, personal website or whatever.}
13
+
14
+ s.rubyforge_project = "rack_my_openid"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_runtime_dependency 'sinatra'
22
+ s.add_runtime_dependency 'ruby-openid'
23
+
24
+ s.add_development_dependency 'rspec'
25
+ s.add_development_dependency 'capybara'
26
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'a valid request' do
4
+ context 'when realm is trusted via options' do
5
+ let(:options_with_realm) { options.merge({:trusted_realms => [request.realm]}) }
6
+ it 'should answer true' do
7
+ described_class.new(options_with_realm).handle(params, session).should == 'answered true'
8
+ end
9
+ end
10
+
11
+ context 'when realm is trusted via session' do
12
+ let(:session) { {:trusted_realms => [request.realm] } }
13
+ it 'should answer true' do
14
+ described_class.new(options).handle(params, session).should == 'answered true'
15
+ end
16
+ end
17
+
18
+ context 'when realm is not trusted' do
19
+ it 'should raise an error' do
20
+ expect {
21
+ described_class.new(options).handle(params, session)
22
+ }.to raise_error(RackMyOpenid::Handler::UntrustedRealm)
23
+ end
24
+ end
25
+ end
26
+
27
+ describe RackMyOpenid::Handler do
28
+ let(:options) { {:openid => 'http://myopenid.myopenid/' } }
29
+ let(:params) { {} }
30
+ let(:session) { {} }
31
+ let(:server) {
32
+ server = double('OpenID::Server::Server')
33
+ server.should_receive(:decode_request).with(params) { request }
34
+ server
35
+ }
36
+ before(:each) do
37
+ OpenID::Server::Server.stub(:new) { server }
38
+ end
39
+
40
+ context 'with bad params' do
41
+ let(:params) { {:bad => :params} }
42
+ let(:request) { false }
43
+
44
+ it 'should raise error' do
45
+ expect {
46
+ described_class.new(options).handle(params, session)
47
+ }.to raise_error(RackMyOpenid::Handler::BadRequest)
48
+ end
49
+ end
50
+
51
+ context 'with a non-check_id request' do
52
+ let(:params) { {:mode => 'some_mode' } }
53
+ let(:request) { Object.new }
54
+ let(:response) { double('Response') }
55
+
56
+ it 'should pass the request to the server' do
57
+ server.should_receive(:handle_request).with(request) { response }
58
+ server.should_receive(:encode_response).with(response)
59
+ described_class.new(options).handle(params, session)
60
+ end
61
+ end
62
+
63
+ context 'with a check_id request' do
64
+ let(:params) { {:mode => 'check_id'} }
65
+ let(:request) {
66
+ request = OpenID::Server::CheckIDRequest.new(nil, nil, nil)
67
+ request.stub(:realm) { 'http://my.realm' }
68
+ request.stub(:answer).with(true, nil, options[:openid]) { 'answered true' }
69
+ request.stub(:answer).with(false) { 'answered false' }
70
+ request
71
+ }
72
+ before do
73
+ server.stub(:encode_response) {|params| params }
74
+ end
75
+
76
+ context 'and a valid openid' do
77
+ before do
78
+ request.stub(:id_select) { false }
79
+ request.stub(:claimed_id) { options[:openid] }
80
+ end
81
+
82
+ it_should_behave_like 'a valid request'
83
+ end
84
+
85
+ context 'and an invalid openid' do
86
+ before do
87
+ request.stub(:id_select) { false }
88
+ request.stub(:claimed_id) { 'bad id' }
89
+ end
90
+
91
+ it 'should answer false' do
92
+ described_class.new(options).handle(params, session).should == 'answered false'
93
+ end
94
+ end
95
+
96
+ context 'and no openid' do
97
+ before do
98
+ request.stub(:id_select) { true }
99
+ request.stub(:claimed_id) { nil }
100
+ end
101
+
102
+ it_should_behave_like 'a valid request'
103
+ end
104
+ end
105
+ end
106
+
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Authentication' do
4
+ setup_app
5
+ assume_authorization
6
+
7
+ scenario 'Bad request' do
8
+ handler.stub(:handle) { raise RackMyOpenid::Handler::BadRequest }
9
+ visit '/openid?foo=bar'
10
+ page.status_code.should == 400
11
+ end
12
+
13
+ scenario 'Untrusted realm' do
14
+ handler.stub(:handle) { raise RackMyOpenid::Handler::UntrustedRealm.new('http://my.realm') }
15
+ visit '/openid?foo=bar'
16
+ page.current_path.should == '/openid/decide'
17
+ page.should have_selector('form')
18
+ page.should have_content('http://my.realm')
19
+ end
20
+
21
+ scenario 'Happy case' do
22
+ handler.stub(:handle) { OpenID::Server::WebResponse.new(123, {'X-Test' => 'bar'}, 'testbody') }
23
+ visit '/openid?foo=bar'
24
+ page.status_code.should == 123
25
+ page.response_headers['X-Test'].should == 'bar'
26
+ page.source.should == 'testbody'
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ feature 'Decision whether to trust a realm or not' do
2
+ setup_app
3
+ assume_authorization
4
+
5
+ context 'when accessing decision page directly' do
6
+ scenario 'should redirect to the endpoint' do
7
+ visit '/openid/decide'
8
+ page.current_path.should == '/openid'
9
+ end
10
+ end
11
+
12
+ context 'when redirected to the decision page' do
13
+ before do
14
+ handler.stub(:handle) { raise RackMyOpenid::Handler::UntrustedRealm.new('http://my.realm') }
15
+ visit '/openid?foo=bar'
16
+ handler.stub(:handle) { OpenID::Server::WebResponse.new(200, {}, 'ok') }
17
+ end
18
+
19
+ scenario 'Trusting the realm - should allow authentication' do
20
+ handler.should_receive(:handle)
21
+ page.click_button('Yes')
22
+ end
23
+
24
+ scenario 'Not trusting the realm - should cancel authentication' do
25
+ handler.should_receive(:cancel)
26
+ page.click_button('No')
27
+ end
28
+
29
+ scenario 'Preserving realm trust' do
30
+ page.click_button('Yes')
31
+ handler.should_receive(:handle) { |params,session|
32
+ session[:trusted_realms].should == ['http://my.realm']
33
+ }
34
+ visit('/openid?foo=bar')
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Human-readable endpoint' do
4
+ setup_app
5
+
6
+ scenario 'Visiting the endpoint' do
7
+ visit '/openid'
8
+ page.should have_content 'This is an OpenID endpoint.'
9
+ end
10
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'HTTP authorisation' do
4
+ setup_app
5
+
6
+ before do
7
+ handler.stub(:handle) { OpenID::Server::WebResponse.new(200, {}, 'ok') }
8
+ visit '/openid?foo=bar'
9
+ end
10
+
11
+ scenario 'Without authorisation it should propose to authorise' do
12
+ page.status_code.should == 401
13
+ end
14
+
15
+ scenario 'With improper authorisation it should reject the request' do
16
+ page.driver.browser.digest_authorize('vasya', 'pupkin')
17
+ visit '/openid?foo=bar'
18
+ page.status_code.should == 401
19
+ end
20
+
21
+ scenario 'With proper authorisation we should handle the request' do
22
+ page.driver.browser.digest_authorize('correct_login', 'correct_password')
23
+ visit '/openid?foo=bar'
24
+ page.status_code.should == 200
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ require 'rack_my_openid'
2
+ require 'rspec'
3
+ require 'capybara/rspec'
4
+
5
+ module AcceptanceHelper
6
+ def setup_app
7
+ before { Capybara.app = RackMyOpenid::Provider.new({:realm => 'myrealm', :credentials => 'bd1baa373b11c42826d3b15ef77a26d8'}) }
8
+
9
+ let(:handler) { double('RackMyOpenid::Handler') }
10
+ before { RackMyOpenid::Handler.stub(:new).and_return(handler) }
11
+ end
12
+
13
+ def assume_authorization
14
+ before {
15
+ visit '/openid?foo=bar'
16
+ page.driver.browser.digest_authorize('correct_login', 'correct_password')
17
+ visit '/openid?foo=bar'
18
+ }
19
+ end
20
+ end
21
+
22
+ RSpec.configure do |config|
23
+ config.extend AcceptanceHelper, :type => :request
24
+ end
25
+
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack_my_openid
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Leonid Shevtsov
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sinatra
16
+ requirement: &70135767507380 !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: *70135767507380
25
+ - !ruby/object:Gem::Dependency
26
+ name: ruby-openid
27
+ requirement: &70135767506760 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70135767506760
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &70135767506180 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70135767506180
47
+ - !ruby/object:Gem::Dependency
48
+ name: capybara
49
+ requirement: &70135767503520 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70135767503520
58
+ description: Would be useful to enable OpenID authorisation with your Ruby/Rails-based
59
+ blog, personal website or whatever.
60
+ email:
61
+ - leonid@shevtsov.me
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - .gitignore
67
+ - .rvmrc
68
+ - Gemfile
69
+ - README.md
70
+ - Rakefile
71
+ - lib/rack_my_openid.rb
72
+ - lib/rack_my_openid/handler.rb
73
+ - lib/rack_my_openid/provider.rb
74
+ - lib/rack_my_openid/version.rb
75
+ - lib/rack_my_openid/views/decide.erb
76
+ - lib/rack_my_openid/views/endpoint.erb
77
+ - lib/rack_my_openid/views/expired.erb
78
+ - lib/rack_my_openid/views/layout.erb
79
+ - rack_my_openid.gemspec
80
+ - spec/handler_spec.rb
81
+ - spec/provider/authentication_spec.rb
82
+ - spec/provider/decision_spec.rb
83
+ - spec/provider/endpoint_spec.rb
84
+ - spec/provider/http_auth_spec.rb
85
+ - spec/spec_helper.rb
86
+ homepage: https://github.com/leonid-shevtsov/rack_my_openid
87
+ licenses: []
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project: rack_my_openid
106
+ rubygems_version: 1.8.11
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: Single-user OpenID provider implemented as a Rack (Sinatra) application.
110
+ test_files:
111
+ - spec/handler_spec.rb
112
+ - spec/provider/authentication_spec.rb
113
+ - spec/provider/decision_spec.rb
114
+ - spec/provider/endpoint_spec.rb
115
+ - spec/provider/http_auth_spec.rb
116
+ - spec/spec_helper.rb