adva_user 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.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +22 -0
  5. data/README +114 -0
  6. data/README.md +29 -0
  7. data/Rakefile +2 -0
  8. data/adva_user.gemspec +17 -0
  9. data/app/controllers/admin/base_account_controller.rb +13 -0
  10. data/app/controllers/admin/users_controller.rb +95 -0
  11. data/app/controllers/password_controller.rb +36 -0
  12. data/app/controllers/session_controller.rb +30 -0
  13. data/app/helpers/users_helper.rb +27 -0
  14. data/app/models/account.rb +7 -0
  15. data/app/models/membership.rb +16 -0
  16. data/app/models/password_mailer.rb +43 -0
  17. data/app/models/user.rb +106 -0
  18. data/app/views/admin/users/_form.html.erb +29 -0
  19. data/app/views/admin/users/_sidebar.html.erb +8 -0
  20. data/app/views/admin/users/edit.html.erb +7 -0
  21. data/app/views/admin/users/index.html.erb +13 -0
  22. data/app/views/admin/users/new.html.erb +5 -0
  23. data/app/views/admin/users/show.html.erb +27 -0
  24. data/app/views/layouts/login.html.erb +24 -0
  25. data/app/views/password/edit.html.erb +14 -0
  26. data/app/views/password/new.html.erb +13 -0
  27. data/app/views/password_mailer/reset_password_email.html.erb +3 -0
  28. data/app/views/password_mailer/updated_password_email.html.erb +1 -0
  29. data/app/views/session/new.html.erb +17 -0
  30. data/config/initializers/menus.rb +25 -0
  31. data/config/routes.rb +14 -0
  32. data/db/migrate/20080402000001_create_users_table.rb +33 -0
  33. data/db/migrate/20080402000005_create_memberships_table.rb +13 -0
  34. data/db/migrate/20090625124502_create_accounts.rb +13 -0
  35. data/db/migrate/20090625133231_add_account_to_user.rb +10 -0
  36. data/lib/action_controller/authenticate_anonymous.rb +70 -0
  37. data/lib/action_controller/authenticate_user.rb +201 -0
  38. data/lib/active_record/belongs_to_author.rb +37 -0
  39. data/lib/adva_user.rb +28 -0
  40. data/lib/adva_user/version.rb +3 -0
  41. data/lib/login/helper_integration.rb +11 -0
  42. data/lib/login/mail_config.rb +39 -0
  43. data/test/contexts.rb +42 -0
  44. data/test/fixtures.rb +18 -0
  45. data/test/functional/admin/users_controller_test.rb +176 -0
  46. data/test/functional/password_controller_test.rb +96 -0
  47. data/test/functional/session_controller_test.rb +1 -0
  48. data/test/functional/user_controller_test.rb +95 -0
  49. data/test/integration/anonymous_login_test.rb +39 -0
  50. data/test/integration/edit_user_test.rb +44 -0
  51. data/test/integration/memberships_test.rb +52 -0
  52. data/test/integration/user_deletion_test.rb +27 -0
  53. data/test/integration/user_login_test.rb +53 -0
  54. data/test/integration/user_login_with_remember_me_test.rb +20 -0
  55. data/test/integration/user_registration_test.rb +64 -0
  56. data/test/test_helper.rb +1 -0
  57. data/test/unit/cells/user_cell_test.rb +13 -0
  58. data/test/unit/helpers/users_helper_test.rb +52 -0
  59. data/test/unit/models/account_test.rb +21 -0
  60. data/test/unit/models/anonymous_test.rb +54 -0
  61. data/test/unit/models/password_mailer_test.rb +26 -0
  62. data/test/unit/models/user_mailer_test.rb +16 -0
  63. data/test/unit/models/user_test.rb +173 -0
  64. data/vendor/gems/authentication/.gitignore +17 -0
  65. data/vendor/gems/authentication/Gemfile +4 -0
  66. data/vendor/gems/authentication/LICENSE +22 -0
  67. data/vendor/gems/authentication/MIT-LICENSE +38 -0
  68. data/vendor/gems/authentication/README +39 -0
  69. data/vendor/gems/authentication/README.md +29 -0
  70. data/vendor/gems/authentication/RUNNING_UNIT_TESTS +13 -0
  71. data/vendor/gems/authentication/Rakefile +61 -0
  72. data/vendor/gems/authentication/authentication.gemspec +17 -0
  73. data/vendor/gems/authentication/lib/authentication.rb +270 -0
  74. data/vendor/gems/authentication/lib/authentication/active_record_extensions.rb +11 -0
  75. data/vendor/gems/authentication/lib/authentication/bogus.rb +13 -0
  76. data/vendor/gems/authentication/lib/authentication/hash_helper.rb +26 -0
  77. data/vendor/gems/authentication/lib/authentication/ldap.rb +49 -0
  78. data/vendor/gems/authentication/lib/authentication/remember_me.rb +52 -0
  79. data/vendor/gems/authentication/lib/authentication/salted_hash.rb +53 -0
  80. data/vendor/gems/authentication/lib/authentication/single_token.rb +53 -0
  81. data/vendor/gems/authentication/lib/authentication/version.rb +3 -0
  82. data/vendor/gems/authentication/lib/radius/dictionary +207 -0
  83. data/vendor/gems/authentication/test_backup/abstract_unit.rb +30 -0
  84. data/vendor/gems/authentication/test_backup/active_record_extension_test.rb +17 -0
  85. data/vendor/gems/authentication/test_backup/authentication_test.rb +231 -0
  86. data/vendor/gems/authentication/test_backup/database.yml +12 -0
  87. data/vendor/gems/authentication/test_backup/fixtures/user.rb +3 -0
  88. data/vendor/gems/authentication/test_backup/fixtures/users.yml +3 -0
  89. data/vendor/gems/authentication/test_backup/options_test.rb +100 -0
  90. data/vendor/gems/authentication/test_backup/remember_me_test.rb +41 -0
  91. data/vendor/gems/authentication/test_backup/salted_hash_test.rb +38 -0
  92. data/vendor/gems/authentication/test_backup/schema.rb +10 -0
  93. data/vendor/gems/authentication/test_backup/single_token_test.rb +44 -0
  94. data/vendor/gems/authentication/test_backup/test_helper.rb +8 -0
  95. metadata +157 -0
@@ -0,0 +1,70 @@
1
+ # Auto-registers and re-authenticates anonymous users based on a single token
2
+ # that's stored in the session. This is for anonymous posting of blog comments,
3
+ # editing wikipages etc. and allows to do such things as:
4
+ #
5
+ # * store user information in the user table (which keeps the model and db
6
+ # structure clean) and
7
+ # * allow users to (e.g.) edit their comment based on this anonymous login.
8
+
9
+ module ActionController
10
+ module AuthenticateAnonymous
11
+ def self.included(base)
12
+ base.extend ClassMethods
13
+ end
14
+
15
+ module ClassMethods
16
+ def authenticates_anonymous_user
17
+ return if authenticates_anonymous_user?
18
+ include InstanceMethods
19
+ alias_method_chain :current_user, :anonymous
20
+ alias_method_chain :authenticated?, :anonymous
21
+ end
22
+
23
+ def authenticates_anonymous_user?
24
+ included_modules.include? InstanceMethods
25
+ end
26
+ end
27
+
28
+ module InstanceMethods
29
+ def current_user_with_anonymous
30
+ @current_user ||= (current_user_without_anonymous || login_or_register_anonymous)
31
+ end
32
+
33
+ def authenticated_with_anonymous?
34
+ !!current_user and !current_user.anonymous?
35
+ end
36
+
37
+ def login_or_register_anonymous
38
+ anonymous = try_login_anonymous || User.anonymous
39
+ anonymous = register_or_update_anonymous anonymous if params[:user]
40
+ login_anonymous! anonymous if anonymous
41
+ anonymous
42
+ end
43
+
44
+ def try_login_anonymous
45
+ # try to authenticate if token is present
46
+ validate_token User, session[:anonymous_token] if session[:anonymous_token]
47
+ end
48
+
49
+ def register_or_update_anonymous(anonymous)
50
+ # if :name and :email params are passed either register a new Anonymous or update the existing one
51
+ anonymous.update_attributes params[:user].merge(request_info)
52
+ anonymous
53
+ end
54
+
55
+ def login_anonymous!(anonymous)
56
+ # set a new session token and expiration
57
+ token = anonymous.assign_token('anonymous', 3.hour.from_now)
58
+ anonymous.save
59
+ session[:anonymous_token] = "#{anonymous.id};#{token}"
60
+ cookies[:aid] = anonymous.id.to_s unless anonymous.new_record?
61
+ end
62
+
63
+ def request_info
64
+ { :ip => request.env["REMOTE_ADDR"],
65
+ :agent => request.env["HTTP_USER_AGENT"],
66
+ :referer => request.env["HTTP_REFERER"] }
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,201 @@
1
+ module ActionController
2
+
3
+ # Module automatically mixed into the all controllers making the
4
+ # application of authentication easy. See
5
+ # Login::ControllerIntegration::ClassMethods for how to apply
6
+ # authentication.
7
+ module AuthenticateUser
8
+ def self.included(target)
9
+ target.extend(ClassMethods)
10
+ target.send(:include, InstanceMethods)
11
+ target.helper_method(:logged_in?, :authenticated?)
12
+ end
13
+
14
+ # Methods available as macro-style methods on any controller
15
+ module ClassMethods
16
+
17
+ # Sets up the controller so that authentication is required. If
18
+ # the user is not authenticated then they will be redirected to
19
+ # the login screen.
20
+ #
21
+ # The page requested will be saved so that once the login has
22
+ # occured they will be sent back to the page they first
23
+ # requested. If no page was requested (they went to the login
24
+ # page directly) then they will be directed to profiles/home
25
+ # after login which is a placeholder for the app to override.
26
+ #
27
+ # Options given are passed directly to the before_filter method
28
+ # so feel free to provide :only and :except options.
29
+ def authentication_required
30
+ before_filter :require_authentication
31
+ end
32
+
33
+ # Will remove authentication from certain actions. Options given
34
+ # are passed directly to skip_before_filter so feel free to use
35
+ # :only and :except options.
36
+ #
37
+ # This method is useful in cases where you have locked down the
38
+ # entire application by putting authentication_required in your
39
+ # ApplicationController but then want to open an action back up
40
+ # in a specific controller.
41
+ def no_authentication_required
42
+ skip_before_filter :require_authentication
43
+ end
44
+ end
45
+
46
+ # Methods callable from within actions
47
+ module InstanceMethods
48
+ def authenticate_user(credentials)
49
+ User.authenticate(credentials).tap do |user|
50
+ if user
51
+ # prevent session hijacking - unnecessary according to http://dev.rubyonrails.org/ticket/10108
52
+ # reset_session_except :return_location
53
+ session[:uid] = user.id
54
+ set_user_cookie!(user)
55
+ end
56
+ end
57
+ end
58
+
59
+ # Will retrieve the current_user. Will not force a login but
60
+ # simply load the current user if a person is logged in. If
61
+ # you need the user object loaded with extra options (such as
62
+ # eager loading) then create a private method called
63
+ # "user_find_options" on your controller that returns a hash
64
+ # of the find options you want.
65
+ #
66
+ # This method will also inform the models of the current user
67
+ # if the current user is logged in and the "User" class responds
68
+ # to the class method current_user=. This is a nice way to
69
+ # communciate the current user down to the model level for
70
+ # model-level security. This means you will want to call this
71
+ # method at least once before using the model-level security.
72
+ # Usually you will call it in a before filter. This method is
73
+ # called automatically when authentication_required is applied to
74
+ # an action.
75
+ def current_user
76
+ @current_user ||= begin
77
+ # Check for session[:uid] here? That would mean that for token auth the
78
+ # user always needs to be logged out (e.g. in UserController#create).
79
+ # Looks a bit more robust this way:
80
+ try_login
81
+ if session && session[:uid]
82
+ user = find_current_user
83
+ set_user_cookie!(user)
84
+ user
85
+ end
86
+ end
87
+ end
88
+
89
+ def authenticated?
90
+ !!current_user
91
+ end
92
+ alias :logged_in? :authenticated?
93
+
94
+ # killed this because it's just the wrong way to do it
95
+ #
96
+ # # Will store the current params so that we can return here on
97
+ # # successful login. If you want to redirect to the login yourself
98
+ # # (perhaps you are applying your own security instead of just
99
+ # # determining if the user is logged in) then you will want to
100
+ # # call this before issuing your redirect to the login screen.
101
+ # def store_return_location
102
+ # session[:return_location] = params
103
+ # end
104
+
105
+ private
106
+
107
+ # Will actually test to see if the user is authorized
108
+ def require_authentication
109
+ # No matter what the app does a user can always login, forgot
110
+ # password and register. The controllers provided by this
111
+ # plugin alreaddy have these controllers/actions on an
112
+ # exception list but this prevents a mistake an overridden
113
+ # controller from preventing the normal login behavior.
114
+ %w(session password user).each do |c|
115
+ %w(new create).each do |a|
116
+ return if (controller_name == c) && (action_name == a)
117
+ end
118
+ end
119
+
120
+ # If we cannot get the current user store the requested page
121
+ # and send them to the login page.
122
+ if current_user.nil? or current_user.anonymous?
123
+ redirect_to login_url(:return_to => request.url) and false
124
+ end
125
+ end
126
+
127
+ def logout
128
+ reset_session
129
+ forget_me!
130
+ end
131
+
132
+ def forget_me!
133
+ cookies[:remember_me] = nil
134
+ cookies[:uid] = nil
135
+ cookies[:uname] = nil
136
+ end
137
+
138
+ def remember_me!
139
+ token = current_user.assign_token!('remember me')
140
+ cookies[:remember_me] = { :value => "#{current_user.id};#{token}", :expires => 10.years.from_now }
141
+ end
142
+
143
+ def set_user_cookie!(user = current_user)
144
+ if user
145
+ cookies[:uid] = user.id.to_s
146
+ cookies[:uname] = user.name
147
+ end
148
+ end
149
+
150
+ # There are a few ways that a user can login without going through
151
+ # a login screen. These methods all rely on authenticating with
152
+ # the information given in the request. If any of these methods
153
+ # are successful then session[:uid] will be set with the current
154
+ # user id and current_user will return the current user
155
+ def try_login
156
+ if user = http_auth_login || validation_login || remember_me_login
157
+ session[:uid] = user.id
158
+ end
159
+ end
160
+
161
+ # Will attempt to authenticate with HTTP Auth. HTTP Auth will not
162
+ # be required. We are just checking if it is provided mainly for
163
+ # RESTful requests.
164
+ def http_auth_login
165
+ # FIXME: Implement
166
+ end
167
+
168
+ # Will use the URL param :token to see if we can do a token
169
+ # authentication.
170
+ def validation_login
171
+ validate_token User, params[:token]
172
+ end
173
+
174
+ # Will check for a :remember_me cookie for a token that will
175
+ # authenticate the user.
176
+ def remember_me_login
177
+ validate_token User, cookies[:remember_me]
178
+ end
179
+
180
+ # The tokens are stored in various places as id;token. This method
181
+ # will split that out and validate it. If everything is successful
182
+ # then the user object is returned. Otherwise nil is returned.
183
+ # The full token should be passed in.
184
+ def validate_token(klass, token, options = {})
185
+ return nil if token.blank?
186
+ return nil unless token =~ /\;/
187
+
188
+ uid, token = token.split ';'
189
+ if object = klass.find_by_id(uid)
190
+ return object if object.authenticate(token)
191
+ end
192
+ nil
193
+ end
194
+
195
+ def find_current_user
196
+ User.find_by_id(session[:uid])
197
+ end
198
+
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,37 @@
1
+ module ActiveRecord
2
+ module BelongsToAuthor
3
+ def self.included(base)
4
+ base.extend ActMacro
5
+ end
6
+
7
+ module ActMacro
8
+ def belongs_to_user(*args)
9
+ options = args.extract_options!
10
+ args = (args.empty? ? [:user] : args)
11
+ belongs_to_cacheable *args.dup << options # FIXME should not be polymorphic!
12
+
13
+ args.each do |name|
14
+ class_eval <<-code, __FILE__, __LINE__
15
+ def #{name}_ip
16
+ #{name}.ip if #{name} && #{name}.respond_to?(:ip)
17
+ end
18
+
19
+ def #{name}_agent
20
+ #{name}.agent if #{name} && #{name}.respond_to?(:agent)
21
+ end
22
+
23
+ def #{name}_referer
24
+ #{name}.referer if #{name} && #{name}.respond_to?(:referer)
25
+ end
26
+ code
27
+ end
28
+ end
29
+
30
+ def belongs_to_author(*args)
31
+ options = args.extract_options!
32
+ args = (args.empty? ? [:author] : args) << options
33
+ belongs_to_user *args
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,28 @@
1
+ # require "adva_user/version"
2
+
3
+ # load vendored gems
4
+ Dir["#{File.expand_path("#{File.dirname(__FILE__)}/../vendor/gems")}/**/lib"].each do |vendored_gem_path|
5
+ $: << vendored_gem_path
6
+ end
7
+ require "authentication"
8
+
9
+ require "rails"
10
+
11
+ require "action_controller/authenticate_user"
12
+ require "action_controller/authenticate_anonymous"
13
+ require "active_record/belongs_to_author"
14
+ require "login/helper_integration"
15
+
16
+ module AdvaUser
17
+ class Engine < Rails::Engine
18
+ initializer "adva_user.init" do
19
+ ActionController::Base.send :include, ActionController::AuthenticateUser
20
+ ActionController::Base.send :include, ActionController::AuthenticateAnonymous
21
+ ActiveRecord::Base.send :include, ActiveRecord::BelongsToAuthor
22
+ ActionView::Base.send :include, Login::HelperIntegration
23
+
24
+ Event.observers << 'UserMailer'
25
+ Event.observers << 'PasswordMailer'
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module AdvaUser
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,11 @@
1
+ module Login
2
+ # Automatically mixed into all views for utility functions.
3
+ module HelperIntegration
4
+
5
+ # Returns the current user at the view level. Everything said
6
+ # about the current_user method in the
7
+ # Login::ControllerIntegration::InstanceMethods module
8
+ # applies to this method as well.
9
+ def current_user; controller.current_user end
10
+ end
11
+ end
@@ -0,0 +1,39 @@
1
+ module Login
2
+
3
+ # The purpose of this module is to provide an application some control
4
+ # over how the messages are sent without having to overwrite blocks
5
+ # of code. We do this through simple constants. The two constants
6
+ # currently are:
7
+ #
8
+ # SUBJECT_PREFIX::
9
+ # Text that is before every message subject. By default this is not
10
+ # used. You may want to put something like the website here.
11
+ # NOTIFICATIONS_FROM::
12
+ # Who the message appears to be coming from. By default this is
13
+ # postmaster@yourdomain.com
14
+ #
15
+ # If you want to access these same values in your own mailers just
16
+ # mix them into your mailers and the methods will be available.
17
+ module MailConfig
18
+ protected
19
+
20
+ # Will return subject prefix
21
+ def subject_prefix
22
+ return "[#{SUBJECT_PREFIX}] " if Object.const_defined?('SUBJECT_PREFIX')
23
+ ''
24
+ end
25
+
26
+ # Email message appear to come from. The constant takes priority
27
+ # but if no constant is defined then the email is extracted from
28
+ # the given param which can be any link that you want the email
29
+ # to appear to come from.
30
+ def system_email(extract_from)
31
+ return NOTIFICATIONS_FROM if Object.const_defined?('NOTIFICATIONS_FROM')
32
+ if host = URI.parse(extract_from).host
33
+ host = host.split '.'
34
+ host.shift if host.first =~ /www/i
35
+ "postmaster@#{host * '.'}"
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,42 @@
1
+ class Test::Unit::TestCase
2
+ def login(user)
3
+ @user = user
4
+ stub(@controller).current_user.returns(user)
5
+ end
6
+
7
+ share :no_user do
8
+ before do
9
+ User.delete_all
10
+ end
11
+ end
12
+
13
+ share :a_user do
14
+ before do
15
+ @user = User.first
16
+ end
17
+ end
18
+
19
+ def valid_user_params
20
+ { :first_name => 'first name',
21
+ :last_name => 'last name',
22
+ :email => 'email@email.org',
23
+ :password => 'password',
24
+ :homepage => 'http://homepage.org' }
25
+ end
26
+
27
+ share :valid_user_params do
28
+ before { @params = { :user => valid_user_params } }
29
+ end
30
+
31
+ share :invalid_user_params do
32
+ before { @params = { :user => valid_user_params.update(:first_name => '') } }
33
+ end
34
+
35
+ share :invalid_user_params do
36
+ before { @params = { :user => valid_user_params.update(:email => '') } }
37
+ end
38
+
39
+ share :invalid_user_params do
40
+ before { @params = { :user => valid_user_params.update(:password => '') } }
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ user1 = User.create! :first_name => 'user1',
2
+ :email => 'user1@example.com',
3
+ :password => 'a password',
4
+ :verified_at => Time.now
5
+ user2 = User.create! :first_name => 'user2',
6
+ :email => 'user2@example.com',
7
+ :password => 'a password',
8
+ :verified_at => Time.now
9
+ user3 = User.create! :first_name => 'user3',
10
+ :email => 'user3@example.com',
11
+ :password => 'a password',
12
+ :verified_at => Time.now
13
+ user4 = User.create! :first_name => 'user4',
14
+ :email => 'user4@example.com',
15
+ :password => 'a password',
16
+ :verified_at => Time.now
17
+
18
+ account = Account.create! :name => 'an account', :users => [ user1, user2, user3 ]