authengine 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. data/.gitignore +10 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +4 -0
  4. data/README.md +86 -0
  5. data/Rakefile +31 -0
  6. data/app/assets/images/message_block/back.gif +0 -0
  7. data/app/assets/images/message_block/back_m.gif +0 -0
  8. data/app/assets/images/message_block/confirmation.gif +0 -0
  9. data/app/assets/images/message_block/confirmation_m.gif +0 -0
  10. data/app/assets/images/message_block/error.gif +0 -0
  11. data/app/assets/images/message_block/error_m.gif +0 -0
  12. data/app/assets/images/message_block/info.gif +0 -0
  13. data/app/assets/images/message_block/info_m.gif +0 -0
  14. data/app/assets/images/message_block/notice.gif +0 -0
  15. data/app/assets/images/message_block/notice_m.gif +0 -0
  16. data/app/assets/images/message_block/warn.gif +0 -0
  17. data/app/assets/images/message_block/warn_m.gif +0 -0
  18. data/app/assets/stylesheets/authengine.css +3 -0
  19. data/app/assets/stylesheets/message_block.css +45 -0
  20. data/app/controllers/authengine/accounts_controller.rb +56 -0
  21. data/app/controllers/authengine/action_roles_controller.rb +22 -0
  22. data/app/controllers/authengine/actions_controller.rb +17 -0
  23. data/app/controllers/authengine/roles_controller.rb +35 -0
  24. data/app/controllers/authengine/sessions_controller.rb +75 -0
  25. data/app/controllers/authengine/user_roles_controller.rb +55 -0
  26. data/app/controllers/authengine/useractions_controller.rb +17 -0
  27. data/app/controllers/authengine/users_controller.rb +137 -0
  28. data/app/helpers/application_helper.rb +2 -0
  29. data/app/helpers/authengine/users_helper.rb +11 -0
  30. data/app/helpers/roles_helper.rb +2 -0
  31. data/app/mailers/authengine/user_mailer.rb +53 -0
  32. data/app/models/action.rb +54 -0
  33. data/app/models/action_role.rb +29 -0
  34. data/app/models/authenticated_system.rb +179 -0
  35. data/app/models/authorized_system.rb +41 -0
  36. data/app/models/controller.rb +124 -0
  37. data/app/models/role.rb +71 -0
  38. data/app/models/session.rb +3 -0
  39. data/app/models/session_role.rb +17 -0
  40. data/app/models/user.rb +191 -0
  41. data/app/models/user_observer.rb +14 -0
  42. data/app/models/user_role.rb +4 -0
  43. data/app/models/useraction.rb +56 -0
  44. data/app/views/authengine/accounts/edit.html.erb +19 -0
  45. data/app/views/authengine/actions/create.html.erb +2 -0
  46. data/app/views/authengine/actions/destroy.html.erb +2 -0
  47. data/app/views/authengine/actions/edit.html.erb +80 -0
  48. data/app/views/authengine/actions/index.html.haml +26 -0
  49. data/app/views/authengine/actions/new.html.erb +2 -0
  50. data/app/views/authengine/actions/show.html.erb +8 -0
  51. data/app/views/authengine/actions/update.html.erb +11 -0
  52. data/app/views/authengine/admin/_show.html.haml +5 -0
  53. data/app/views/authengine/layouts/authengine.html.haml +9 -0
  54. data/app/views/authengine/roles/index.html.haml +12 -0
  55. data/app/views/authengine/roles/new.html.haml +15 -0
  56. data/app/views/authengine/roles/show.html.erb +8 -0
  57. data/app/views/authengine/sessions/new.html.haml +18 -0
  58. data/app/views/authengine/user_mailer/activation.html.erb +5 -0
  59. data/app/views/authengine/user_mailer/forgot_password.html.erb +3 -0
  60. data/app/views/authengine/user_mailer/message_to_admin.html.erb +2 -0
  61. data/app/views/authengine/user_mailer/reset_password.html.erb +1 -0
  62. data/app/views/authengine/user_mailer/signup_notification.html.erb +5 -0
  63. data/app/views/authengine/user_roles/edit.html.haml +10 -0
  64. data/app/views/authengine/user_roles/index.html.haml +14 -0
  65. data/app/views/authengine/user_roles/new.html.haml +8 -0
  66. data/app/views/authengine/useractions/_useraction.html.erb +6 -0
  67. data/app/views/authengine/useractions/index.html.erb +13 -0
  68. data/app/views/authengine/useractions/show.html.haml +14 -0
  69. data/app/views/authengine/useractions/update.html.erb +2 -0
  70. data/app/views/authengine/users/_no_privacy_policy.html.haml +1 -0
  71. data/app/views/authengine/users/_privacy_policy_example.html.haml +36 -0
  72. data/app/views/authengine/users/_user.html.haml +19 -0
  73. data/app/views/authengine/users/edit.html.haml +24 -0
  74. data/app/views/authengine/users/index.html.haml +10 -0
  75. data/app/views/authengine/users/new.html.haml +31 -0
  76. data/app/views/authengine/users/show.html.haml +19 -0
  77. data/app/views/authengine/users/signup.html.haml +52 -0
  78. data/authengine.gemspec +44 -0
  79. data/config/application.rb +1 -0
  80. data/config/routes.rb +43 -0
  81. data/db/migrate/20110320171029_create_authengine_tables.rb +90 -0
  82. data/db/migrate/20110924165900_add_parent_id_to_roles_table.rb +5 -0
  83. data/db/migrate/20110925202800_add_type_field_to_user_roles_table.rb +5 -0
  84. data/db/migrate/20111003074700_add_indexes_to_several_tables.rb +7 -0
  85. data/db/seeds.rb +7 -0
  86. data/lib/application_helper.rb +19 -0
  87. data/lib/authengine.rb +5 -0
  88. data/lib/authengine/engine.rb +44 -0
  89. data/lib/authengine/testing_support/factories/user_factory.rb +13 -0
  90. data/lib/authengine/version.rb +3 -0
  91. data/lib/rails/generators/authengine/authengine_generator.rb +160 -0
  92. data/lib/rails/generators/authengine/templates/initializer.rb +3 -0
  93. data/lib/rails/generators/authengine/templates/migration.rb +16 -0
  94. data/lib/rails/generators/authengine/templates/pre_populate_database.rb +20 -0
  95. data/lib/rails/generators/authengine/templates/schema.rb +69 -0
  96. data/lib/tasks/bootstrap.rake +29 -0
  97. data/spec/authengine_spec.rb +7 -0
  98. data/spec/dummy/.rspec +1 -0
  99. data/spec/dummy/Gemfile +3 -0
  100. data/spec/dummy/Rakefile +8 -0
  101. data/spec/dummy/app/assets/javascripts/jasmine_examples/Player.js +22 -0
  102. data/spec/dummy/app/assets/javascripts/jasmine_examples/Song.js +7 -0
  103. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  104. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  105. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  106. data/spec/dummy/config.ru +4 -0
  107. data/spec/dummy/config/application.rb +50 -0
  108. data/spec/dummy/config/boot.rb +10 -0
  109. data/spec/dummy/config/database.yml +22 -0
  110. data/spec/dummy/config/environment.rb +5 -0
  111. data/spec/dummy/config/environments/development.rb +26 -0
  112. data/spec/dummy/config/environments/production.rb +49 -0
  113. data/spec/dummy/config/environments/test.rb +35 -0
  114. data/spec/dummy/config/initializers/application.rb +1 -0
  115. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  116. data/spec/dummy/config/initializers/inflections.rb +10 -0
  117. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  118. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  119. data/spec/dummy/config/initializers/session_store.rb +8 -0
  120. data/spec/dummy/config/locales/en.yml +5 -0
  121. data/spec/dummy/config/routes.rb +3 -0
  122. data/spec/dummy/db/development.sqlite3 +0 -0
  123. data/spec/dummy/db/schema.rb +87 -0
  124. data/spec/dummy/lib/constants.rb +5 -0
  125. data/spec/dummy/log/development.log +117 -0
  126. data/spec/dummy/log/production.log +0 -0
  127. data/spec/dummy/log/server.log +0 -0
  128. data/spec/dummy/public/404.html +26 -0
  129. data/spec/dummy/public/422.html +26 -0
  130. data/spec/dummy/public/500.html +26 -0
  131. data/spec/dummy/public/favicon.ico +0 -0
  132. data/spec/dummy/public/javascripts/application.js +2 -0
  133. data/spec/dummy/public/javascripts/controls.js +965 -0
  134. data/spec/dummy/public/javascripts/dragdrop.js +974 -0
  135. data/spec/dummy/public/javascripts/effects.js +1123 -0
  136. data/spec/dummy/public/javascripts/prototype.js +6001 -0
  137. data/spec/dummy/public/javascripts/rails.js +191 -0
  138. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  139. data/spec/dummy/script/rails +6 -0
  140. data/spec/dummy/spec/javascripts/helpers/.gitkeep +0 -0
  141. data/spec/dummy/spec/javascripts/helpers/SpecHelper.js +9 -0
  142. data/spec/dummy/spec/javascripts/jasmine_examples/PlayerSpec.js +58 -0
  143. data/spec/dummy/spec/javascripts/support/jasmine.yml +76 -0
  144. data/spec/generators/authengine_generator_spec.rb +11 -0
  145. data/spec/integration/navigation_spec.rb +9 -0
  146. data/spec/javascripts/spec.css +3 -0
  147. data/spec/javascripts/spec.js.coffee +2 -0
  148. data/spec/models/action_role_spec.rb +59 -0
  149. data/spec/models/authenticated_system_spec.rb +109 -0
  150. data/spec/models/role_spec.rb +38 -0
  151. data/spec/models/user_factory_spec.rb +7 -0
  152. data/spec/models/user_spec.rb +16 -0
  153. data/spec/requests/sessions_spec.rb +11 -0
  154. data/spec/spec_helper.rb +57 -0
  155. metadata +405 -0
@@ -0,0 +1,41 @@
1
+ # AuthorizedSystem is 'include'd in ActionController by the authengine engine
2
+ # see lib/authengine/engine.rb
3
+ module AuthorizedSystem
4
+ # established for the session when the user logs in
5
+ # may be modified later if user's roles are modified
6
+ # or if session is downgraded
7
+ def current_role_ids=(ids)
8
+ session[:role].current_role_ids = ids
9
+ end
10
+
11
+ def current_role_ids
12
+ session[:role].current_role_ids
13
+ end
14
+
15
+ def action_permitted?(controller, action)
16
+ ActionRole.permits_access_for(controller, action, current_role_ids)
17
+ end
18
+
19
+ def permitted?(controller, action)
20
+ action_permitted?(controller, action) && logged_in?
21
+ end
22
+
23
+ # for each and every action, we check the configured permission
24
+ # for the role(s) assigned to the logged-in user
25
+ # The controller and action can be passed as parameters, to check whether or not to display a link/button
26
+ # or else the current request controller/action are used to check whether or not to display a page
27
+ def check_permissions(controller = request.parameters["controller"], action = request.parameters["action"])
28
+ permission = false
29
+ if !logged_in?
30
+ logger.info "access denied: not logged in"
31
+ access_denied
32
+ elsif permitted?(controller, action)
33
+ permission = true
34
+ else
35
+ logger.info "permission denied, #{controller}, #{action}"
36
+ permission_denied
37
+ end
38
+ permission
39
+ end
40
+
41
+ end
@@ -0,0 +1,124 @@
1
+ # the table is managed so as to make it mirror the file system
2
+ # the only reason this is necessary is to be able to store in the
3
+ # database a last_modified attribute for a file, in order to
4
+ # know whether the actions table is up to date for this controller
5
+ # or should be regenerated from the file contents
6
+ # The class represents both filesystem objects and database objects
7
+ # hmmm... is that really the best way to design it, shouldn't it be two classes?
8
+ class Controller < ActiveRecord::Base
9
+ has_many :actions, :dependent=>:delete_all
10
+ CONTROLLERS_DIR = "#{Rails.root.to_s}/app/controllers"
11
+
12
+ cattr_accessor :controllers
13
+
14
+ # returns an array of strings, each one is a controller name
15
+ def self.all_controller_names
16
+ @@controllers ||= all_files.map { |file| file.camelize.gsub(".rb","") }
17
+ end
18
+
19
+ def self.all_files
20
+ application_files + engine_files
21
+ end
22
+
23
+ def self.engine_files
24
+ engine_controller_paths.inject([]) do |array, path|
25
+ # entries may be controller files, but if there are namespaced controllers
26
+ # then entries are directories
27
+ directories, files = Dir.new(path).entries.reject{|c|c.match(/^\./)}.partition{|c| File.directory?(File.new(File.join(path,c)))}
28
+ array += files
29
+ directories.each do |directory|
30
+ files = Dir.new(File.join(path,directory)).reject{|c|c.match(/^\./)}.entries.map{|file| File.join(directory,file)}
31
+ array += files
32
+ end
33
+ array
34
+ end
35
+ end
36
+
37
+ def self.engine_controller_paths
38
+ Rails::Engine.subclasses.collect { |engine|
39
+ engine.config.eager_load_paths.detect{|p| p=~/controller/}
40
+ }.flatten.compact
41
+ end
42
+
43
+ def self.application_files
44
+ Dir.new(CONTROLLERS_DIR).entries.reject{|c| c.match(/^\./)}.reject{|c| c == 'application_controller.rb'}
45
+ end
46
+
47
+ def self.all_modified_files
48
+ all_controller_names.select(&:modified?)
49
+ end
50
+
51
+ # a file is declared to be modified if it's either older or newer than the last_modified date
52
+ # this triggers re-parsing the actions in the file whether it's older or newer
53
+ # and so responds both to the file being edited and also the database being restored
54
+ # from an older version.
55
+ # Only by converting to string could I persuade two apparently equal DateTime objects to match!
56
+ def modified?
57
+ file_modification_time.to_s != last_modified.getutc.to_datetime.to_s
58
+ end
59
+
60
+ def file_modification_time
61
+ file.mtime.getutc.to_datetime
62
+ end
63
+
64
+ def file
65
+ all_paths = Controller.engine_controller_paths << CONTROLLERS_DIR
66
+ controller_path = all_paths.detect do |path|
67
+ full_path = File.join(path, "#{controller_name}_controller.rb")
68
+ File.exists?(full_path)
69
+ end
70
+ File.new(File.join(controller_path, "#{controller_name}_controller.rb"))
71
+ end
72
+
73
+ # updates the controllers table so that it contains a record for each controller file
74
+ # in the /app/controllers directory
75
+ def self.update_table
76
+ cc = Controller.all.inject({}){ |hash,controller| hash[controller.controller_name]=controller; hash } # from the database
77
+ all_controller_names.each do |f| # f is of the form "ItemsController"
78
+ cont = f.tableize.gsub!("_controllers","") # cont is of the form "items"
79
+ admin_name = Role.find_by_name("administrator") ? "administrator" : "admin"
80
+ if !cc.keys.include?(cont) # it's not in the db
81
+ new_controller = new(:controller_name=>f.underscore.gsub!("_controller", ""), :last_modified=>Date.today) # add controller to controllers table as there's not a record corresponding with the file
82
+ new_controller.actions << new_controller.action_list.map { |a| Action.new(:action_name=>a[1]) }# when a new controller is added, its actions must be added to the actions.file
83
+ new_controller.save
84
+ elsif cc[cont].modified? # file was modified since db was updated, so read the actions from the file, and add/delete as necessary
85
+ action_names = cc[cont].actions_from_file # action_names is of the form ["index", "new", "edit", "create", "update"]
86
+ Action.update_table_for(cc[cont],action_names)
87
+ # finally modify the last_modified date of the controller record to match the actual file
88
+ cc[cont].update_attribute(:last_modified,cc[cont].file_modification_time)
89
+ end
90
+ end
91
+ ActionRole.assign_developer_access
92
+ # delete any records in the controllers table for which there's no xx_controller.rb file... it must've been deleted
93
+ cc.each { |name,controller| controller.destroy if !all_controller_names.map{|cn| cn.tableize.gsub!("_controllers","")}.include?(name) }
94
+
95
+ end
96
+
97
+ def self.all_actions_from_files
98
+ all_controller_names.inject([]){ |ar,c| ar+=c.action_list; ar }
99
+ end
100
+
101
+ def model_name
102
+ controller_name.gsub("Controller","").underscore
103
+ end
104
+
105
+ # the actions returned are those that were in the file when it was loaded
106
+ # this is reasonable only because the actions are changed by the developer
107
+ # and never get changed "on the fly". However this fact should be considered when testing!
108
+ def actions_from_file
109
+ # there's a workaround here for some strangeness that appeared in Rails 3
110
+ # where public_instance_methods returns some spurious methods with the format
111
+ # _one_time_conditions_valid_nnn?
112
+ controller.public_instance_methods(false).reject{|m| m.match(/one_time_conditions_valid/)}.map(&:to_s)
113
+ end
114
+
115
+ def controller
116
+ (controller_name+"_controller").classify.constantize
117
+ end
118
+
119
+ # returns an array of arrays, each contains the controller controller_name and action name
120
+ def action_list
121
+ actions_from_file.collect { |m| [model_name,m] }
122
+ end
123
+
124
+ end
@@ -0,0 +1,71 @@
1
+ # Roles are hierarchically organized, so that the current role
2
+ # for a session can be downgraded to a lower role.
3
+ # The hierarchy gives meaning to "lower role".
4
+ class Role < ActiveRecord::Base
5
+ has_many :user_roles
6
+ has_many :users, :through=>:user_roles
7
+
8
+ has_many :action_roles
9
+ has_many :actions, :through => :action_roles
10
+
11
+ belongs_to :parent, :class_name => 'Role'
12
+
13
+
14
+ validates_presence_of :name
15
+ validates_uniqueness_of :name
16
+
17
+ before_destroy do
18
+ if users.empty?
19
+ action_roles.clear
20
+ else
21
+ false # don't delete a role if there are users assigned
22
+ end
23
+ end
24
+
25
+ # takes either an array of roles or a single role object
26
+ def self.equal_or_lower_than(role)
27
+ roles = role.is_a?(Array) ? role : [role]
28
+ (lower_than(role) + roles).uniq
29
+ end
30
+
31
+ # takes either an array of roles or a single role object
32
+ def self.lower_than(role)
33
+ roles = role.is_a?(Array) ? role : [role]
34
+ collection = roles.inject([]) do |ar, r|
35
+ ar + with_ancestor(r)
36
+ end
37
+ (collection).uniq
38
+ end
39
+
40
+ # returns an array of roles that have the passed-in role as an
41
+ # ancestor
42
+ def self.with_ancestor(role)
43
+ all.select{|r| r.has_ancestor?(role)}
44
+ end
45
+
46
+ def has_ancestor?(role)
47
+ ancestors.include?(role)
48
+ end
49
+
50
+ def ancestors
51
+ node, nodes = self, []
52
+ nodes << node = node.parent while node.parent
53
+ nodes
54
+ end
55
+
56
+ def is_developer?
57
+ name == 'developer'
58
+ end
59
+
60
+ def self.developer
61
+ where('name = "developer"').first
62
+ end
63
+
64
+ def self.developer_id
65
+ developer && developer.id
66
+ end
67
+
68
+ def to_s
69
+ name
70
+ end
71
+ end
@@ -0,0 +1,3 @@
1
+ class Session <ActiveRecord::Base
2
+
3
+ end
@@ -0,0 +1,17 @@
1
+ class SessionRole
2
+
3
+ attr_accessor :current_role_ids
4
+
5
+ def initialize
6
+ @current_role_ids = []
7
+ end
8
+
9
+ def add_roles(ids)
10
+ @current_role_ids += ids
11
+ end
12
+
13
+ def create(id)
14
+ @current_role_ids << id
15
+ end
16
+
17
+ end
@@ -0,0 +1,191 @@
1
+ require 'digest/sha1'
2
+ class User < ActiveRecord::Base
3
+ # Virtual attribute for the unencrypted password
4
+ attr_accessor :password
5
+
6
+ # when user account is first created, only firstname, lastname and email are required
7
+ validates_presence_of :firstName, :lastName, :email
8
+ validates_length_of :email, :within => 6..100
9
+ validates_uniqueness_of :email, :case_sensitive=>false, :if=>Proc.new { |user| all_active=true; User.find_all_by_email(user.email).each { |u| all_active=false if u.status=='inactive' }; all_active }, :message=>'There is already an active user with that email address.'
10
+ #validates_format_of :email, :with => EMAIL_REGEX
11
+
12
+ # the next action on the user's record is account activation
13
+ # at this time, login and password must be present and valid
14
+ validates_presence_of :login, :on => :update
15
+ validates_presence_of :password, :if => :password_required?, :on=>:update
16
+ validates_presence_of :password_confirmation, :if => :password_required?, :on=>:update
17
+ validates_length_of :password, :within => 4..40, :if => :password_required?, :on=>:update
18
+ validates_confirmation_of :password, :if => :password_required?, :on=>:update
19
+ validates_length_of :login, :within => 3..40, :on => :update
20
+ validates_uniqueness_of :login, :case_sensitive => false, :on => :update
21
+
22
+ has_many :user_roles, :dependent=>:delete_all
23
+ accepts_nested_attributes_for :user_roles
24
+ has_many :roles, :through=>:user_roles
25
+
26
+ has_many :useractions, :dependent=>:delete_all
27
+ has_many :actions, :through=>:useractions
28
+
29
+ before_save :encrypt_password
30
+ before_create :make_activation_code
31
+
32
+
33
+ # prevents a user from submitting a crafted form that bypasses activation
34
+ # anything else you want your user to change should be added here.
35
+ # If the ability to add users was extended, may wish to change the attr_accessible to make sure a user cannot assign
36
+ # themselves to a higher-privileged role
37
+ # TODO in this application, users are trusted, but see how this should be implemented if users are not trusted
38
+ attr_accessible :login, :email, :password, :password_confirmation, :firstName, :lastName, :user_roles_attributes
39
+
40
+ class PermissionsNotConfigured < StandardError
41
+ attr_reader :message
42
+ def initialize(controller,action)
43
+ @message = "Permissions not yet configured for #{controller}/#{action}"
44
+ end
45
+ end
46
+ class ActivationCodeNotFound < StandardError; end
47
+ class ArgumentError < StandardError; end
48
+ class AlreadyActivated < StandardError
49
+ attr_reader :user, :message;
50
+ def initialize(user, message=nil)
51
+ @message, @user = message, user
52
+ end
53
+ end
54
+
55
+ def first_last_name
56
+ firstName+' '+lastName
57
+ end
58
+
59
+
60
+ # Finds the user with the corresponding activation code, activates their account and returns the user.
61
+ #
62
+ # Raises:
63
+ # +User::ActivationCodeNotFound+ if there is no user with the corresponding activation code
64
+ # +User::AlreadyActivated+ if the user with the corresponding activation code has already activated their account
65
+ def self.find_and_activate!(activation_code)
66
+ raise ArgumentError if activation_code.nil?
67
+ user = find_by_activation_code(activation_code)
68
+ raise ActivationCodeNotFound if !user
69
+ raise AlreadyActivated.new(user) if user.active?
70
+ user.send(:activate!)
71
+ user
72
+ end
73
+
74
+ def self.find_with_activation_code(activation_code)
75
+ raise ArgumentError if activation_code.nil?
76
+ user = find_by_activation_code(activation_code)
77
+ raise ActivationCodeNotFound if !user
78
+ raise AlreadyActivated.new(user) if user.active?
79
+ user
80
+ end
81
+
82
+ def active?
83
+ # the presence of an activation date means they have activated
84
+ !activated_at.nil?
85
+ end
86
+
87
+ # Returns true if the user has just been activated.
88
+ def pending?
89
+ @activated
90
+ end
91
+
92
+ # Authenticates a user by their login name and unencrypted password. Returns the user or nil.
93
+ def self.authenticate(login, password)
94
+ u = find :first, :conditions => ['login = ?', login]
95
+ u && u.authenticated?(password) ? u : nil
96
+ end
97
+
98
+ # Encrypts some data with the salt.
99
+ def self.encrypt(password, salt)
100
+ Digest::SHA1.hexdigest("--#{salt}--#{password}--")
101
+ end
102
+
103
+ # Encrypts the password with the user salt
104
+ def encrypt(password)
105
+ self.class.encrypt(password, salt)
106
+ end
107
+
108
+ def authenticated?(password)
109
+ crypted_password == encrypt(password)
110
+ end
111
+
112
+ def remember_token?
113
+ remember_token_expires_at && Time.now.utc < remember_token_expires_at
114
+ end
115
+
116
+ def forgot_password
117
+ @forgotten_password = true
118
+ self.make_password_reset_code
119
+ end
120
+
121
+ def reset_password
122
+ # First update the password_reset_code before setting the
123
+ # reset_password flag to avoid duplicate email notifications.
124
+ update_attribute(:password_reset_code, nil)
125
+ @reset_password = true
126
+ end
127
+
128
+ #used in user_observer
129
+ def recently_forgot_password?
130
+ @forgotten_password
131
+ end
132
+
133
+ def recently_reset_password?
134
+ @reset_password
135
+ end
136
+
137
+ def forget_me
138
+ self.remember_token_expires_at = nil
139
+ self.remember_token = nil
140
+ save(:validate => false)
141
+ end
142
+
143
+ def has_role?(name)
144
+ self.roles.find_by_name(name) ? true : false
145
+ end
146
+
147
+ def self.create_by_sql(attributes)
148
+ user = User.new(attributes)
149
+ user.send('encrypt_password')
150
+ user.send('make_activation_code')
151
+ now = DateTime.now.to_formatted_s(:db)
152
+ query = <<-SQL
153
+ INSERT INTO users
154
+ (activated_at, activation_code, created_at, crypted_password, email, enabled, firstName, lastName, login, password_reset_code, remember_token, remember_token_expires_at, salt, status, type, updated_at)
155
+ VALUES
156
+ ( '#{now}','#{user.activation_code}','#{now}', '#{user.crypted_password}', NULL, 1, '#{user.firstName}', '#{user.lastName}', '#{user.login}', NULL, NULL, NULL, '#{user.salt}', NULL, NULL,'#{now}')
157
+ SQL
158
+ #can't use ActiveRecord#create here as it would trigger a notification email
159
+ ActiveRecord::Base.connection.insert_sql(query)
160
+ end
161
+
162
+ protected
163
+
164
+ # before filter
165
+ def encrypt_password
166
+ return if password.blank?
167
+ self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if salt.blank?
168
+ self.crypted_password = encrypt(password)
169
+ end
170
+
171
+ def password_required?
172
+ crypted_password.blank? || !password.blank?
173
+ end
174
+
175
+ def make_activation_code
176
+ self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
177
+ end
178
+
179
+ def make_password_reset_code
180
+ self.password_reset_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
181
+ end
182
+
183
+ private
184
+
185
+ def activate!
186
+ @activated = true
187
+ self.update_attribute(:activated_at, Time.now.utc)
188
+ @activated = false
189
+ end
190
+
191
+ end