authengine 0.0.2

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 (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