pager-restful_open_id_authentication 1.0.20080507
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/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
|