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.
Files changed (29) hide show
  1. data/README +63 -0
  2. data/Rakefile +22 -0
  3. data/generators/open_id_authenticated/USAGE +1 -0
  4. data/generators/open_id_authenticated/open_id_authenticated_generator.rb +215 -0
  5. data/generators/open_id_authenticated/templates/activation.rhtml +3 -0
  6. data/generators/open_id_authenticated/templates/authenticated_system.rb +120 -0
  7. data/generators/open_id_authenticated/templates/authenticated_test_helper.rb +113 -0
  8. data/generators/open_id_authenticated/templates/controller.rb +94 -0
  9. data/generators/open_id_authenticated/templates/fixtures.yml +17 -0
  10. data/generators/open_id_authenticated/templates/functional_test.rb +85 -0
  11. data/generators/open_id_authenticated/templates/helper.rb +2 -0
  12. data/generators/open_id_authenticated/templates/login.rhtml +20 -0
  13. data/generators/open_id_authenticated/templates/migration.rb +45 -0
  14. data/generators/open_id_authenticated/templates/model.rb +93 -0
  15. data/generators/open_id_authenticated/templates/model_controller.rb +30 -0
  16. data/generators/open_id_authenticated/templates/model_functional_test.rb +72 -0
  17. data/generators/open_id_authenticated/templates/model_helper.rb +2 -0
  18. data/generators/open_id_authenticated/templates/notifier.rb +25 -0
  19. data/generators/open_id_authenticated/templates/notifier_test.rb +31 -0
  20. data/generators/open_id_authenticated/templates/observer.rb +11 -0
  21. data/generators/open_id_authenticated/templates/open_id_form.rhtml +14 -0
  22. data/generators/open_id_authenticated/templates/signup.rhtml +22 -0
  23. data/generators/open_id_authenticated/templates/signup_notification.rhtml +8 -0
  24. data/generators/open_id_authenticated/templates/unit_test.rb +101 -0
  25. data/install.rb +1 -0
  26. data/lib/controller_methods.rb +137 -0
  27. data/lib/open_id_store.rb +69 -0
  28. data/rails/init.rb +15 -0
  29. metadata +81 -0
@@ -0,0 +1,94 @@
1
+ # This controller handles the login/logout function of the site.
2
+ class <%= controller_class_name %>Controller < ApplicationController
3
+ open_id_consumer :required => [:email, :nickname]
4
+ # Be sure to include AuthenticationSystem in Application Controller instead
5
+ include AuthenticatedSystem
6
+ # If you want "remember me" functionality, add this before_filter to Application Controller
7
+ before_filter :login_from_cookie
8
+
9
+ # render new.rhtml
10
+ def new
11
+ @<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>])
12
+ end
13
+
14
+ def create
15
+ self.current_<%= file_name %> = <%= class_name %>.authenticate(params[:login], params[:password])
16
+ if logged_in?
17
+ if params[:remember_me] == "1"
18
+ self.current_<%= file_name %>.remember_me
19
+ cookies[:auth_token] = { :value => self.current_<%= file_name %>.remember_token , :expires => self.current_<%= file_name %>.remember_token_expires_at }
20
+ end
21
+ redirect_back_or_default('/')
22
+ flash[:notice] = "Logged in successfully"
23
+ else
24
+ flash[:notice] = "Login failed"
25
+ render :action => 'new'
26
+ end
27
+ end
28
+
29
+ def destroy
30
+ self.current_<%= file_name %>.forget_me if logged_in?
31
+ cookies.delete :auth_token
32
+ reset_session
33
+ flash[:notice] = "You have been logged out."
34
+ redirect_back_or_default('/')
35
+ end
36
+
37
+ def begin
38
+ # If the URL was unusable (either because of network conditions, a server error,
39
+ # or that the response returned was not an OpenID identity page), the library
40
+ # will return HTTP_FAILURE or PARSE_ERROR. Let the user know that the URL is unusable.
41
+ case open_id_response.status
42
+ when OpenID::SUCCESS
43
+ # The URL was a valid identity URL. Now we just need to send a redirect
44
+ # to the server using the redirect_url the library created for us.
45
+
46
+ # redirect to the server
47
+ redirect_to open_id_response.redirect_url((request.protocol + request.host_with_port + "/"), complete_<%= controller_file_name %>_url)
48
+ else
49
+ flash[:error] = "Unable to find OpenID server for <q>#{params[:open_id_url]}</q>"
50
+ render :action => :new
51
+ end
52
+ end
53
+
54
+ def complete
55
+ case open_id_response.status
56
+ when OpenID::FAILURE
57
+ # In the case of failure, if info is non-nil, it is the URL that we were verifying.
58
+ # We include it in the error message to help the user figure out what happened.
59
+ flash[:notice] = if open_id_response.identity_url
60
+ "Verification of #{open_id_response.identity_url} failed. "
61
+ else
62
+ "Verification failed. "
63
+ end
64
+ flash[:notice] += open_id_response.msg.to_s
65
+ when OpenID::SUCCESS
66
+ # Success means that the transaction completed without error. If info is nil,
67
+ # it means that the user cancelled the verification.
68
+ flash[:notice] = "You have successfully verified #{open_id_response.identity_url} as your identity."
69
+ if open_id_fields.any?
70
+ @<%= file_name %> = <%= class_name %>.find_by_open_id_url(open_id_response.identity_url)
71
+ @<%= file_name %> ||= <%= class_name %>.new(:open_id_url => open_id_response.identity_url)
72
+ @<%= file_name %>.login = open_id_fields['nickname'] if open_id_fields['nickname']
73
+ @<%= file_name %>.email = open_id_fields['email'] if open_id_fields['email']
74
+ if @<%= file_name %>.save
75
+ self.current_<%= file_name %> = @<%= file_name %>
76
+ if params[:remember_me] == "1"
77
+ self.current_<%= file_name %>.remember_me
78
+ cookies[:auth_token] = { :value => self.current_<%= file_name %>.remember_token , :expires => self.current_<%= file_name %>.remember_token_expires_at }
79
+ end
80
+ flash[:notice] = "You have successfully verified #{open_id_response.identity_url} as your identity."
81
+ return redirect_back_or_default('/')
82
+ else
83
+ flash[:notice] = @<%= file_name %>.errors.full_messages.join('<br />')
84
+ render :action => 'new' and return
85
+ end
86
+ end
87
+ when OpenID::CANCEL
88
+ flash[:notice] = "Verification cancelled."
89
+ else
90
+ flash[:notice] = "Unknown response status: #{open_id_response.status}"
91
+ end
92
+ redirect_to :action => 'new'
93
+ end
94
+ end
@@ -0,0 +1,17 @@
1
+ quentin:
2
+ id: 1
3
+ login: quentin
4
+ email: quentin@example.com
5
+ salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd
6
+ crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test
7
+ created_at: <%%= 5.days.ago.to_s :db %>
8
+ <% if options[:include_activation] %> activation_code: 8f24789ae988411ccf33ab0c30fe9106fab32e9b <% end %>
9
+ <% if options[:include_activation] %> activated_at: <%%= 5.days.ago.to_s :db %> <% end %>
10
+ aaron:
11
+ id: 2
12
+ login: aaron
13
+ email: aaron@example.com
14
+ salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd
15
+ crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test
16
+ created_at: <%%= 1.days.ago.to_s :db %>
17
+ <% if options[:include_activation] %> activation_code: 8f24789ae988411ccf33ab0c30fe9106fab32e9a <% end %>
@@ -0,0 +1,85 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require '<%= controller_file_name %>_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class <%= controller_class_name %>Controller; def rescue_action(e) raise e end; end
6
+
7
+ class <%= controller_class_name %>ControllerTest < Test::Unit::TestCase
8
+ # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead
9
+ # Then, you can remove it from this and the units test.
10
+ include AuthenticatedTestHelper
11
+
12
+ fixtures :<%= table_name %>
13
+
14
+ def setup
15
+ @controller = <%= controller_class_name %>Controller.new
16
+ @request = ActionController::TestRequest.new
17
+ @response = ActionController::TestResponse.new
18
+ end
19
+
20
+ def test_should_login_and_redirect
21
+ post :create, :login => 'quentin', :password => 'test'
22
+ assert session[:<%= file_name %>]
23
+ assert_response :redirect
24
+ end
25
+
26
+ def test_should_fail_login_and_not_redirect
27
+ post :create, :login => 'quentin', :password => 'bad password'
28
+ assert_nil session[:<%= file_name %>]
29
+ assert_response :success
30
+ end
31
+
32
+ def test_should_logout
33
+ login_as :quentin
34
+ get :destroy
35
+ assert_nil session[:<%= file_name %>]
36
+ assert_response :redirect
37
+ end
38
+
39
+ def test_should_remember_me
40
+ post :create, :login => 'quentin', :password => 'test', :remember_me => "1"
41
+ assert_not_nil @response.cookies["auth_token"]
42
+ end
43
+
44
+ def test_should_not_remember_me
45
+ post :create, :login => 'quentin', :password => 'test', :remember_me => "0"
46
+ assert_nil @response.cookies["auth_token"]
47
+ end
48
+
49
+ def test_should_delete_token_on_logout
50
+ login_as :quentin
51
+ get :destroy
52
+ assert_equal @response.cookies["auth_token"], []
53
+ end
54
+
55
+ def test_should_login_with_cookie
56
+ <%= table_name %>(:quentin).remember_me
57
+ @request.cookies["auth_token"] = cookie_for(:quentin)
58
+ get :new
59
+ assert @controller.send(:logged_in?)
60
+ end
61
+
62
+ def test_should_fail_expired_cookie_login
63
+ <%= table_name %>(:quentin).remember_me
64
+ <%= table_name %>(:quentin).update_attribute :remember_token_expires_at, 5.minutes.ago
65
+ @request.cookies["auth_token"] = cookie_for(:quentin)
66
+ get :new
67
+ assert !@controller.send(:logged_in?)
68
+ end
69
+
70
+ def test_should_fail_cookie_login
71
+ <%= table_name %>(:quentin).remember_me
72
+ @request.cookies["auth_token"] = auth_token('invalid_auth_token')
73
+ get :new
74
+ assert !@controller.send(:logged_in?)
75
+ end
76
+
77
+ protected
78
+ def auth_token(token)
79
+ CGI::Cookie.new('name' => 'auth_token', 'value' => token)
80
+ end
81
+
82
+ def cookie_for(<%= file_name %>)
83
+ auth_token <%= table_name %>(<%= file_name %>).remember_token
84
+ end
85
+ end
@@ -0,0 +1,2 @@
1
+ module <%= controller_class_name %>Helper
2
+ end
@@ -0,0 +1,20 @@
1
+ <h2>Sign in with OpenID</h2>
2
+ <%%= render :partial => "<%= controller_file_name %>/open_id_form" %>
3
+
4
+ <p>If you don't already have an OpenID account you can easily create one at <%%= link_to 'MyOpenID', 'http://www.myopenid.com' -%>.</p>
5
+
6
+ <h2>Or use your login/password</h2>
7
+ <%% form_tag <%= controller_singular_name %>_path do -%>
8
+ <p><label for="login">Login</label><br/>
9
+ <%%= text_field_tag 'login' %></p>
10
+
11
+ <p><label for="password">Password</label><br/>
12
+ <%%= password_field_tag 'password' %></p>
13
+
14
+ <!-- Uncomment this if you want this functionality
15
+ <p><label for="remember_me">Remember me:</label>
16
+ <%%= check_box_tag 'remember_me' %></p>
17
+ -->
18
+
19
+ <p><%%= submit_tag 'Log in' %></p>
20
+ <%% end -%>
@@ -0,0 +1,45 @@
1
+ class <%= migration_name %> < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :<%= table_name %>, :force => true do |t|
4
+ t.column :login, :string
5
+ t.column :email, :string
6
+ t.column :crypted_password, :string, :limit => 40
7
+ t.column :salt, :string, :limit => 40
8
+ t.column :created_at, :datetime
9
+ t.column :updated_at, :datetime
10
+ t.column :remember_token, :string
11
+ t.column :remember_token_expires_at, :datetime
12
+ t.column :open_id_url, :string
13
+ <% if options[:include_activation] %>
14
+ t.column :activation_code, :string, :limit => 40
15
+ t.column :activated_at, :datetime<% end %>
16
+ end
17
+
18
+ create_table :open_id_associations, :force => true do |t|
19
+ t.column :server_url, :binary
20
+ t.column :handle, :string
21
+ t.column :secret, :binary
22
+ t.column :issued, :integer
23
+ t.column :lifetime, :integer
24
+ t.column :assoc_type, :string
25
+ end
26
+
27
+ create_table :open_id_nonces, :force => true do |t|
28
+ t.column :nonce, :string
29
+ t.column :created, :integer
30
+ end
31
+
32
+ create_table :open_id_settings, :force => true do |t|
33
+ t.column :setting, :string
34
+ t.column :value, :binary
35
+ end
36
+ end
37
+
38
+ def self.down
39
+ drop_table :<%= table_name %>
40
+ drop_table :open_id_associations
41
+ drop_table :open_id_nonces
42
+ drop_table :open_id_settings
43
+ end
44
+
45
+ end
@@ -0,0 +1,93 @@
1
+ require 'digest/sha1'
2
+ class <%= class_name %> < ActiveRecord::Base
3
+ # Virtual attribute for the unencrypted password
4
+ attr_accessor :password
5
+
6
+ validates_presence_of :login, :email
7
+ validates_presence_of :password, :if => :password_required?
8
+ validates_presence_of :password_confirmation, :if => :password_required?
9
+ validates_length_of :password, :within => 4..40, :if => :password_required?
10
+ validates_confirmation_of :password, :if => :password_required?
11
+ validates_length_of :login, :within => 3..40
12
+ validates_length_of :email, :within => 3..100
13
+ validates_uniqueness_of :login, :email, :case_sensitive => false
14
+ before_save :encrypt_password
15
+ <% if options[:include_activation] %> before_create :make_activation_code <% end %>
16
+ <% if options[:include_activation] %>
17
+ # Activates the user in the database.
18
+ def activate
19
+ @activated = true
20
+ self.attributes = {:activated_at => Time.now.utc, :activation_code => nil}
21
+ save(false)
22
+ end
23
+
24
+ def activated?
25
+ !! activation_code.nil?
26
+ end
27
+
28
+ # Returns true if the user has just been activated.
29
+ def recently_activated?
30
+ @activated
31
+ end <% end %>
32
+ # Authenticates a user by their login name and unencrypted password. Returns the user or nil.
33
+ def self.authenticate(login, password)
34
+ u = <% if options[:include_activation] %>find :first, :conditions => ['login = ? and activated_at IS NOT NULL', login]<% else %>find_by_login(login)<% end %> # need to get the salt
35
+ u && u.authenticated?(password) ? u : nil
36
+ end
37
+
38
+ # Encrypts some data with the salt.
39
+ def self.encrypt(password, salt)
40
+ Digest::SHA1.hexdigest("--#{salt}--#{password}--")
41
+ end
42
+
43
+ # Encrypts the password with the user salt
44
+ def encrypt(password)
45
+ self.class.encrypt(password, salt)
46
+ end
47
+
48
+ def authenticated?(password)
49
+ crypted_password == encrypt(password)
50
+ end
51
+
52
+ def remember_token?
53
+ remember_token_expires_at && Time.now.utc < remember_token_expires_at
54
+ end
55
+
56
+ # These create and unset the fields required for remembering users between browser closes
57
+ def remember_me
58
+ remember_me_for 2.weeks
59
+ end
60
+
61
+ def remember_me_for(time)
62
+ remember_me_until time.from_now.utc
63
+ end
64
+
65
+ def remember_me_until(time)
66
+ self.remember_token_expires_at = time
67
+ self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")
68
+ save(false)
69
+ end
70
+
71
+ def forget_me
72
+ self.remember_token_expires_at = nil
73
+ self.remember_token = nil
74
+ save(false)
75
+ end
76
+
77
+ protected
78
+ # before filter
79
+ def encrypt_password
80
+ return if password.blank?
81
+ self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
82
+ self.crypted_password = encrypt(password)
83
+ end
84
+
85
+ def password_required?
86
+ open_id_url.nil? && (crypted_password.blank? || !password.blank?)
87
+ end
88
+
89
+ <% if options[:include_activation] %>
90
+ def make_activation_code
91
+ self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
92
+ end <% end %>
93
+ end
@@ -0,0 +1,30 @@
1
+ class <%= model_controller_class_name %>Controller < ApplicationController
2
+ # Be sure to include AuthenticationSystem in Application Controller instead
3
+ include AuthenticatedSystem
4
+ # If you want "remember me" functionality, add this before_filter to Application Controller
5
+ before_filter :login_from_cookie
6
+
7
+ # render new.rhtml
8
+ def new
9
+ end
10
+
11
+ def create
12
+ @<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>])
13
+ @<%= file_name %>.save!
14
+ self.current_<%= file_name %> = @<%= file_name %>
15
+ redirect_back_or_default('/')
16
+ flash[:notice] = "Thanks for signing up!"
17
+ rescue ActiveRecord::RecordInvalid
18
+ render :action => 'new'
19
+ end
20
+ <% if options[:include_activation] %>
21
+ def activate
22
+ self.current_<%= file_name %> = <%= class_name %>.find_by_activation_code(params[:activation_code])
23
+ if logged_in? && !current_<%= file_name %>.activated?
24
+ current_<%= file_name %>.activate
25
+ flash[:notice] = "Signup complete!"
26
+ end
27
+ redirect_back_or_default('/')
28
+ end
29
+ <% end %>
30
+ end
@@ -0,0 +1,72 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require '<%= model_controller_file_name %>_controller'
3
+
4
+ # Re-raise errors caught by the controller.
5
+ class <%= model_controller_class_name %>Controller; def rescue_action(e) raise e end; end
6
+
7
+ class <%= model_controller_class_name %>ControllerTest < Test::Unit::TestCase
8
+ # Be sure to include AuthenticatedTestHelper in test/test_helper.rb instead
9
+ # Then, you can remove it from this and the units test.
10
+ include AuthenticatedTestHelper
11
+
12
+ fixtures :<%= table_name %>
13
+
14
+ def setup
15
+ @controller = <%= model_controller_class_name %>Controller.new
16
+ @request = ActionController::TestRequest.new
17
+ @response = ActionController::TestResponse.new
18
+ end
19
+
20
+ def test_should_allow_signup
21
+ assert_difference <%= class_name %>, :count do
22
+ create_<%= file_name %>
23
+ assert_response :redirect
24
+ end
25
+ end
26
+
27
+ def test_should_require_login_on_signup
28
+ assert_no_difference <%= class_name %>, :count do
29
+ create_<%= file_name %>(:login => nil)
30
+ assert assigns(:<%= file_name %>).errors.on(:login)
31
+ assert_response :success
32
+ end
33
+ end
34
+
35
+ def test_should_require_password_on_signup
36
+ assert_no_difference <%= class_name %>, :count do
37
+ create_<%= file_name %>(:password => nil)
38
+ assert assigns(:<%= file_name %>).errors.on(:password)
39
+ assert_response :success
40
+ end
41
+ end
42
+
43
+ def test_should_require_password_confirmation_on_signup
44
+ assert_no_difference <%= class_name %>, :count do
45
+ create_<%= file_name %>(:password_confirmation => nil)
46
+ assert assigns(:<%= file_name %>).errors.on(:password_confirmation)
47
+ assert_response :success
48
+ end
49
+ end
50
+
51
+ def test_should_require_email_on_signup
52
+ assert_no_difference <%= class_name %>, :count do
53
+ create_<%= file_name %>(:email => nil)
54
+ assert assigns(:<%= file_name %>).errors.on(:email)
55
+ assert_response :success
56
+ end
57
+ end
58
+ <% if options[:include_activation] %>
59
+ def test_should_activate_user
60
+ assert_nil <%= class_name %>.authenticate('aaron', 'test')
61
+ get :activate, :activation_code => <%= table_name %>(:aaron).activation_code
62
+ assert_redirected_to '/'
63
+ assert_not_nil flash[:notice]
64
+ assert_equal <%= table_name %>(:aaron), <%= class_name %>.authenticate('aaron', 'test')
65
+ end <% end %>
66
+
67
+ protected
68
+ def create_<%= file_name %>(options = {})
69
+ post :create, :<%= file_name %> => { :login => 'quire', :email => 'quire@example.com',
70
+ :password => 'quire', :password_confirmation => 'quire' }.merge(options)
71
+ end
72
+ end