pager-restful_open_id_authentication 1.0.20080507
Sign up to get free protection for your applications and to get access to all the features.
- data/README +63 -0
- data/Rakefile +22 -0
- data/generators/open_id_authenticated/USAGE +1 -0
- data/generators/open_id_authenticated/open_id_authenticated_generator.rb +215 -0
- data/generators/open_id_authenticated/templates/activation.rhtml +3 -0
- data/generators/open_id_authenticated/templates/authenticated_system.rb +120 -0
- data/generators/open_id_authenticated/templates/authenticated_test_helper.rb +113 -0
- data/generators/open_id_authenticated/templates/controller.rb +94 -0
- data/generators/open_id_authenticated/templates/fixtures.yml +17 -0
- data/generators/open_id_authenticated/templates/functional_test.rb +85 -0
- data/generators/open_id_authenticated/templates/helper.rb +2 -0
- data/generators/open_id_authenticated/templates/login.rhtml +20 -0
- data/generators/open_id_authenticated/templates/migration.rb +45 -0
- data/generators/open_id_authenticated/templates/model.rb +93 -0
- data/generators/open_id_authenticated/templates/model_controller.rb +30 -0
- data/generators/open_id_authenticated/templates/model_functional_test.rb +72 -0
- data/generators/open_id_authenticated/templates/model_helper.rb +2 -0
- data/generators/open_id_authenticated/templates/notifier.rb +25 -0
- data/generators/open_id_authenticated/templates/notifier_test.rb +31 -0
- data/generators/open_id_authenticated/templates/observer.rb +11 -0
- data/generators/open_id_authenticated/templates/open_id_form.rhtml +14 -0
- data/generators/open_id_authenticated/templates/signup.rhtml +22 -0
- data/generators/open_id_authenticated/templates/signup_notification.rhtml +8 -0
- data/generators/open_id_authenticated/templates/unit_test.rb +101 -0
- data/install.rb +1 -0
- data/lib/controller_methods.rb +137 -0
- data/lib/open_id_store.rb +69 -0
- data/rails/init.rb +15 -0
- metadata +81 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
class <%= class_name %>Notifier < ActionMailer::Base
|
2
|
+
def signup_notification(<%= file_name %>)
|
3
|
+
setup_email(<%= file_name %>)
|
4
|
+
@subject += 'Please activate your new account'
|
5
|
+
<% if options[:include_activation] %>
|
6
|
+
@body[:url] = "http://YOURSITE/activate/#{<%= file_name %>.activation_code}"
|
7
|
+
<% else %>
|
8
|
+
@body[:url] = "http://YOURSITE/login/" <% end %>
|
9
|
+
end
|
10
|
+
|
11
|
+
def activation(<%= file_name %>)
|
12
|
+
setup_email(<%= file_name %>)
|
13
|
+
@subject += 'Your account has been activated!'
|
14
|
+
@body[:url] = "http://YOURSITE/"
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
def setup_email(<%= file_name %>)
|
19
|
+
@recipients = "#{<%= file_name %>.email}"
|
20
|
+
@from = "ADMINEMAIL"
|
21
|
+
@subject = "[YOURSITE] "
|
22
|
+
@sent_on = Time.now
|
23
|
+
@body[:<%= file_name %>] = <%= file_name %>
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
require '<%= file_name %>_notifier'
|
3
|
+
|
4
|
+
class <%= class_name %>NotifierTest < Test::Unit::TestCase
|
5
|
+
FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures'
|
6
|
+
CHARSET = "utf-8"
|
7
|
+
|
8
|
+
include ActionMailer::Quoting
|
9
|
+
|
10
|
+
def setup
|
11
|
+
ActionMailer::Base.delivery_method = :test
|
12
|
+
ActionMailer::Base.perform_deliveries = true
|
13
|
+
ActionMailer::Base.deliveries = []
|
14
|
+
|
15
|
+
@expected = TMail::Mail.new
|
16
|
+
@expected.set_content_type "text", "plain", { "charset" => CHARSET }
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_dummy_test
|
20
|
+
#do nothing
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def read_fixture(action)
|
25
|
+
IO.readlines("#{FIXTURES_PATH}/<%= file_name %>_notifier/#{action}")
|
26
|
+
end
|
27
|
+
|
28
|
+
def encode(subject)
|
29
|
+
quoted_printable(subject, CHARSET)
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class <%= class_name %>Observer < ActiveRecord::Observer
|
2
|
+
def after_create(<%= file_name %>)
|
3
|
+
<%= class_name %>Notifier.deliver_signup_notification(<%= file_name %>)
|
4
|
+
end
|
5
|
+
|
6
|
+
def after_save(<%= file_name %>)
|
7
|
+
<% if options[:include_activation] %>
|
8
|
+
<%= class_name %>Notifier.deliver_activation(<%= file_name %>) if <%= file_name %>.recently_activated?
|
9
|
+
<% end %>
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<%% form_tag begin_<%= controller_singular_name %>_path do -%>
|
2
|
+
<p>
|
3
|
+
<label>
|
4
|
+
Identity URL:
|
5
|
+
<%%= text_field_tag :open_id_url, nil, :style => 'width:250px' %>
|
6
|
+
</label>
|
7
|
+
<%%= submit_tag :Verify %>
|
8
|
+
</p>
|
9
|
+
<!-- Uncomment this if you want this functionality
|
10
|
+
<p><label for="remember_me">Remember me:</label>
|
11
|
+
<%%= check_box_tag 'remember_me' %></p>
|
12
|
+
-->
|
13
|
+
|
14
|
+
<%% end -%>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<h2>Signup with OpenID</h2>
|
2
|
+
|
3
|
+
<%%= render :partial => "<%= controller_file_name %>/open_id_form" %>
|
4
|
+
|
5
|
+
<h2>or create a <%= class_name %> Profile</h2>
|
6
|
+
|
7
|
+
<%%= error_messages_for :<%= file_name %> %>
|
8
|
+
<%% form_for @<%= file_name %> do |f| -%>
|
9
|
+
<p><label for="login">Login</label><br/>
|
10
|
+
<%%= f.text_field :login %></p>
|
11
|
+
|
12
|
+
<p><label for="email">Email</label><br/>
|
13
|
+
<%%= f.text_field :email %></p>
|
14
|
+
|
15
|
+
<p><label for="password">Password</label><br/>
|
16
|
+
<%%= f.password_field :password %></p>
|
17
|
+
|
18
|
+
<p><label for="password_confirmation">Confirm Password</label><br/>
|
19
|
+
<%%= f.password_field :password_confirmation %></p>
|
20
|
+
|
21
|
+
<p><%%= submit_tag 'Sign up' %></p>
|
22
|
+
<%% end -%>
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class <%= class_name %>Test < Test::Unit::TestCase
|
4
|
+
# Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead.
|
5
|
+
# Then, you can remove it from this and the functional test.
|
6
|
+
include AuthenticatedTestHelper
|
7
|
+
fixtures :<%= table_name %>
|
8
|
+
|
9
|
+
def test_should_create_<%= file_name %>
|
10
|
+
assert_difference <%= class_name %>, :count do
|
11
|
+
<%= file_name %> = create_<%= file_name %>
|
12
|
+
assert !<%= file_name %>.new_record?, "#{<%= file_name %>.errors.full_messages.to_sentence}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_should_require_login
|
17
|
+
assert_no_difference <%= class_name %>, :count do
|
18
|
+
u = create_<%= file_name %>(:login => nil)
|
19
|
+
assert u.errors.on(:login)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_should_require_password
|
24
|
+
assert_no_difference <%= class_name %>, :count do
|
25
|
+
u = create_<%= file_name %>(:password => nil)
|
26
|
+
assert u.errors.on(:password)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_should_require_password_confirmation
|
31
|
+
assert_no_difference <%= class_name %>, :count do
|
32
|
+
u = create_<%= file_name %>(:password_confirmation => nil)
|
33
|
+
assert u.errors.on(:password_confirmation)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_should_require_email
|
38
|
+
assert_no_difference <%= class_name %>, :count do
|
39
|
+
u = create_<%= file_name %>(:email => nil)
|
40
|
+
assert u.errors.on(:email)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_should_reset_password
|
45
|
+
<%= table_name %>(:quentin).update_attributes(:password => 'new password', :password_confirmation => 'new password')
|
46
|
+
assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin', 'new password')
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_should_not_rehash_password
|
50
|
+
<%= table_name %>(:quentin).update_attributes(:login => 'quentin2')
|
51
|
+
assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin2', 'test')
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_should_authenticate_<%= file_name %>
|
55
|
+
assert_equal <%= table_name %>(:quentin), <%= class_name %>.authenticate('quentin', 'test')
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_should_set_remember_token
|
59
|
+
<%= table_name %>(:quentin).remember_me
|
60
|
+
assert_not_nil <%= table_name %>(:quentin).remember_token
|
61
|
+
assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_should_unset_remember_token
|
65
|
+
<%= table_name %>(:quentin).remember_me
|
66
|
+
assert_not_nil <%= table_name %>(:quentin).remember_token
|
67
|
+
<%= table_name %>(:quentin).forget_me
|
68
|
+
assert_nil <%= table_name %>(:quentin).remember_token
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_should_remember_me_for_one_week
|
72
|
+
before = 1.week.from_now.utc
|
73
|
+
<%= table_name %>(:quentin).remember_me_for 1.week
|
74
|
+
after = 1.week.from_now.utc
|
75
|
+
assert_not_nil <%= table_name %>(:quentin).remember_token
|
76
|
+
assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
|
77
|
+
assert <%= table_name %>(:quentin).remember_token_expires_at.between?(before, after)
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_should_remember_me_until_one_week
|
81
|
+
time = 1.week.from_now.utc
|
82
|
+
<%= table_name %>(:quentin).remember_me_until time
|
83
|
+
assert_not_nil <%= table_name %>(:quentin).remember_token
|
84
|
+
assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
|
85
|
+
assert_equal <%= table_name %>(:quentin).remember_token_expires_at, time
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_should_remember_me_default_two_weeks
|
89
|
+
before = 2.weeks.from_now.utc
|
90
|
+
<%= table_name %>(:quentin).remember_me
|
91
|
+
after = 2.weeks.from_now.utc
|
92
|
+
assert_not_nil <%= table_name %>(:quentin).remember_token
|
93
|
+
assert_not_nil <%= table_name %>(:quentin).remember_token_expires_at
|
94
|
+
assert <%= table_name %>(:quentin).remember_token_expires_at.between?(before, after)
|
95
|
+
end
|
96
|
+
|
97
|
+
protected
|
98
|
+
def create_<%= file_name %>(options = {})
|
99
|
+
<%= class_name %>.create({ :login => 'quire', :email => 'quire@example.com', :password => 'quire', :password_confirmation => 'quire' }.merge(options))
|
100
|
+
end
|
101
|
+
end
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
puts IO.read(File.join(File.dirname(__FILE__), 'README'))
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module OpenIdConsumer
|
2
|
+
module ControllerMethods
|
3
|
+
def self.included(controller)
|
4
|
+
controller.class_eval do
|
5
|
+
verify :method => :post, :only => :begin, :params => :open_id_url, :redirect_to => { :action => 'index' },
|
6
|
+
:add_flash => { :error => "Enter an Identity URL to verify." }
|
7
|
+
verify :method => :get, :only => :complete, :redirect_to => { :action => 'index' }
|
8
|
+
before_filter :begin_open_id_auth, :only => :begin
|
9
|
+
before_filter :complete_open_id_auth, :only => :complete
|
10
|
+
attr_reader :open_id_response
|
11
|
+
attr_reader :open_id_fields
|
12
|
+
cattr_accessor :open_id_consumer_options
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def open_id_consumer
|
19
|
+
@open_id_consumer ||= OpenID::Consumer.new(
|
20
|
+
session[:open_id_session] ||= {},
|
21
|
+
ActiveRecordStore.new)
|
22
|
+
end
|
23
|
+
|
24
|
+
def begin_open_id_auth
|
25
|
+
@open_id_response = open_id_consumer.begin(params[:open_id_url])
|
26
|
+
def @open_id_response.status; OpenID::Consumer::SUCCESS end
|
27
|
+
add_sreg_params!(@open_id_response)# if @open_id_response.status == OpenID::SUCCESS
|
28
|
+
rescue OpenID::DiscoveryFailure
|
29
|
+
def @open_id_response.status; OpenID::Consumer::FAILURE end
|
30
|
+
end
|
31
|
+
|
32
|
+
def complete_open_id_auth
|
33
|
+
return_to = url_for :only_path => false, :action => 'complete'
|
34
|
+
parameters = params.reject{|k,v| request.path_parameters[k]}
|
35
|
+
@open_id_response = open_id_consumer.complete(parameters, return_to)
|
36
|
+
return unless open_id_response.status == OpenID::Consumer::SUCCESS
|
37
|
+
|
38
|
+
@open_id_fields = open_id_response.extension_response('sreg', true)
|
39
|
+
logger.debug "***************** sreg params ***************"
|
40
|
+
logger.debug @open_id_fields.inspect
|
41
|
+
logger.debug "***************** sreg params ***************"
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def authenticate_with_open_id(identity_url)
|
46
|
+
@open_id_response = open_id_consumer.begin(identity_url)
|
47
|
+
yield(@open_id_response.status.to_sum)
|
48
|
+
|
49
|
+
# If the URL was unusable (either because of network conditions, a server error,
|
50
|
+
# or that the response returned was not an OpenID identity page), the library
|
51
|
+
# will return HTTP_FAILURE or PARSE_ERROR. Let the user know that the URL is unusable.
|
52
|
+
case open_id_response.status
|
53
|
+
when OpenID::Consumer::SUCCESS
|
54
|
+
add_sreg_params!(@open_id_response)
|
55
|
+
redirect_to open_id_response.redirect_url((request.protocol + request.host_with_port + "/"), complete_session_url)
|
56
|
+
else
|
57
|
+
flash[:error] = "Unable to find OpenID server for <q>#{params[:open_id_url]}</q>"
|
58
|
+
render :action => :new
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def complete
|
63
|
+
case open_id_response.status
|
64
|
+
when OpenID::Consumer::FAILURE
|
65
|
+
# In the case of failure, if info is non-nil, it is the URL that we were verifying.
|
66
|
+
# We include it in the error message to help the user figure out what happened.
|
67
|
+
flash[:notice] = if open_id_response.identity_url
|
68
|
+
"Verification of #{open_id_response.identity_url} failed. "
|
69
|
+
else
|
70
|
+
"Verification failed. "
|
71
|
+
end
|
72
|
+
flash[:notice] += open_id_response.msg.to_s
|
73
|
+
when OpenID::Consumer::SUCCESS
|
74
|
+
# Success means that the transaction completed without error. If info is nil,
|
75
|
+
# it means that the user cancelled the verification.
|
76
|
+
flash[:notice] = "You have successfully verified #{open_id_response.identity_url} as your identity."
|
77
|
+
if open_id_fields.any?
|
78
|
+
@user = User.find_by_open_id_url(open_id_response.identity_url)
|
79
|
+
@user ||= User.new(:open_id_url => open_id_response.identity_url)
|
80
|
+
@user.login = open_id_fields['nickname'] if open_id_fields['nickname']
|
81
|
+
@user.email = open_id_fields['email'] if open_id_fields['email']
|
82
|
+
if @user.save
|
83
|
+
self.current_user = @user
|
84
|
+
if params[:remember_me] == "1"
|
85
|
+
self.current_user.remember_me
|
86
|
+
cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
|
87
|
+
end
|
88
|
+
flash[:notice] = "You have successfully verified #{open_id_response.identity_url} as your identity."
|
89
|
+
return redirect_back_or_default('/')
|
90
|
+
else
|
91
|
+
flash[:notice] = @user.errors.full_messages.join('<br />')
|
92
|
+
render :action => 'new' and return
|
93
|
+
end
|
94
|
+
end
|
95
|
+
when OpenID::CANCEL
|
96
|
+
flash[:notice] = "Verification cancelled."
|
97
|
+
else
|
98
|
+
flash[:notice] = "Unknown response status: #{open_id_response.status}"
|
99
|
+
end
|
100
|
+
redirect_to :action => 'new'
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
def authenticate_with_open_id(identity_url)
|
105
|
+
|
106
|
+
|
107
|
+
case status
|
108
|
+
when :missing
|
109
|
+
failed_authentication "Sorry, the OpenID server couldn't be found"
|
110
|
+
|
111
|
+
when :canceled
|
112
|
+
failed_authentication "OpenID verification was canceled"
|
113
|
+
|
114
|
+
when :failed
|
115
|
+
failed_authentication "Sorry, the OpenID verification failed"
|
116
|
+
|
117
|
+
when :successful
|
118
|
+
if @current_user =
|
119
|
+
@account.users.find_by_identity_url(identity_url)
|
120
|
+
successful_authentication
|
121
|
+
else
|
122
|
+
failed_authentication "Sorry, no user by that identity URL exists"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
def add_sreg_params!(openid_response)
|
130
|
+
open_id_consumer_options.keys.inject({}) do |params, key|
|
131
|
+
value = open_id_consumer_options[key]
|
132
|
+
value = value.collect { |v| v.to_s.strip } * ',' if value.respond_to?(:collect)
|
133
|
+
openid_response.add_extension_arg('sreg', key.to_s, value.to_s)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'openid/store/interface'
|
2
|
+
|
3
|
+
class ActiveRecordStore < OpenID::Store::Interface
|
4
|
+
def store_association(server_url, assoc)
|
5
|
+
remove_association(server_url, assoc.handle)
|
6
|
+
Association.create(:server_url => server_url,
|
7
|
+
:handle => assoc.handle,
|
8
|
+
:secret => assoc.secret,
|
9
|
+
:issued => assoc.issued,
|
10
|
+
:lifetime => assoc.lifetime,
|
11
|
+
:assoc_type => assoc.assoc_type)
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_association(server_url, handle=nil)
|
15
|
+
assocs = if handle.blank?
|
16
|
+
Association.find_all_by_server_url(server_url)
|
17
|
+
else
|
18
|
+
Association.find_all_by_server_url_and_handle(server_url, handle)
|
19
|
+
end
|
20
|
+
|
21
|
+
assocs.reverse.each do |assoc|
|
22
|
+
a = assoc.from_record
|
23
|
+
if a.expires_in == 0
|
24
|
+
assoc.destroy
|
25
|
+
else
|
26
|
+
return a
|
27
|
+
end
|
28
|
+
end if assocs.any?
|
29
|
+
|
30
|
+
return nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove_association(server_url, handle)
|
34
|
+
Association.delete_all(['server_url = ? AND handle = ?', server_url, handle]) > 0
|
35
|
+
end
|
36
|
+
|
37
|
+
def use_nonce(server_url, timestamp, salt)
|
38
|
+
return false if Nonce.find_by_server_url_and_timestamp_and_salt(server_url, timestamp, salt)
|
39
|
+
return false if (timestamp - Time.now.to_i).abs > OpenID::Nonce.skew
|
40
|
+
Nonce.create(:server_url => server_url, :timestamp => timestamp, :salt => salt)
|
41
|
+
return true
|
42
|
+
end
|
43
|
+
|
44
|
+
def cleanup_nonces
|
45
|
+
now = Time.now.to_i
|
46
|
+
Nonce.delete_all(["timestamp > ? OR timestamp < ?", now + OpenID::Nonce.skew, now - OpenID::Nonce.skew])
|
47
|
+
end
|
48
|
+
|
49
|
+
def cleanup_associations
|
50
|
+
now = Time.now.to_i
|
51
|
+
Association.delete_all(['issued + lifetime > ?',now])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Setting < ActiveRecord::Base
|
56
|
+
set_table_name 'open_id_settings'
|
57
|
+
validates_uniqueness_of :setting
|
58
|
+
end
|
59
|
+
|
60
|
+
class Nonce < ActiveRecord::Base
|
61
|
+
set_table_name 'open_id_nonces'
|
62
|
+
end
|
63
|
+
|
64
|
+
class Association < ActiveRecord::Base
|
65
|
+
set_table_name 'open_id_associations'
|
66
|
+
def from_record
|
67
|
+
OpenID::Association.new(handle, secret, issued, lifetime, assoc_type)
|
68
|
+
end
|
69
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
begin
|
2
|
+
gem 'ruby-openid'
|
3
|
+
require 'openid'
|
4
|
+
rescue LoadError
|
5
|
+
puts "Install the ruby-openid gem to enable OpenID support"
|
6
|
+
end
|
7
|
+
require 'open_id_store'
|
8
|
+
require 'controller_methods'
|
9
|
+
|
10
|
+
class << ActionController::Base
|
11
|
+
def open_id_consumer(options = {})
|
12
|
+
include OpenIdConsumer::ControllerMethods
|
13
|
+
self.open_id_consumer_options = options
|
14
|
+
end
|
15
|
+
end
|