ateam-merb-auth 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/LICENSE +44 -0
  2. data/README +212 -0
  3. data/Rakefile +50 -0
  4. data/TODO +14 -0
  5. data/activerecord_generators/ma_migration/ma_migration_generator.rb +41 -0
  6. data/activerecord_generators/ma_migration/templates/schema/migrations/%time_stamp%_add_ma_user.rb +21 -0
  7. data/app/controllers/application.rb +7 -0
  8. data/app/controllers/sessions.rb +3 -0
  9. data/app/controllers/users.rb +3 -0
  10. data/app/helpers/application_helper.rb +64 -0
  11. data/app/mailers/user_mailer.rb +24 -0
  12. data/app/mailers/views/user_mailer/activation.text.erb +1 -0
  13. data/app/mailers/views/user_mailer/forgot_password.text.erb +5 -0
  14. data/app/mailers/views/user_mailer/signup.text.erb +7 -0
  15. data/app/views/layout/merb_auth.html.erb +16 -0
  16. data/app/views/sessions/new.html.erb +14 -0
  17. data/app/views/users/new.html.erb +18 -0
  18. data/datamapper_generators/ma_migration/ma_migration_generator.rb +38 -0
  19. data/datamapper_generators/ma_migration/templates/schema/migrations/add_ma_user.rb +21 -0
  20. data/lib/merb-auth.rb +184 -0
  21. data/lib/merb-auth/adapters/activerecord/init.rb +26 -0
  22. data/lib/merb-auth/adapters/activerecord/map.rb +44 -0
  23. data/lib/merb-auth/adapters/activerecord/model.rb +81 -0
  24. data/lib/merb-auth/adapters/common.rb +161 -0
  25. data/lib/merb-auth/adapters/datamapper/init.rb +28 -0
  26. data/lib/merb-auth/adapters/datamapper/map.rb +35 -0
  27. data/lib/merb-auth/adapters/datamapper/model.rb +72 -0
  28. data/lib/merb-auth/adapters/map.rb +0 -0
  29. data/lib/merb-auth/adapters/sequel/init.rb +26 -0
  30. data/lib/merb-auth/adapters/sequel/map.rb +35 -0
  31. data/lib/merb-auth/adapters/sequel/model.rb +86 -0
  32. data/lib/merb-auth/controller/controller.rb +113 -0
  33. data/lib/merb-auth/controller/sessions_base.rb +41 -0
  34. data/lib/merb-auth/controller/users_base.rb +55 -0
  35. data/lib/merb-auth/initializer.rb +47 -0
  36. data/lib/merb-auth/merbtasks.rb +168 -0
  37. data/lib/merb-auth/slicetasks.rb +102 -0
  38. data/plugins/forgotten_password/app/controllers/passwords.rb +90 -0
  39. data/plugins/forgotten_password/app/models/user.rb +52 -0
  40. data/plugins/forgotten_password/app/views/passwords/edit.html.erb +9 -0
  41. data/plugins/forgotten_password/app/views/passwords/new.html.erb +4 -0
  42. data/plugins/forgotten_password/forgotten_password.rb +6 -0
  43. data/plugins/forgotten_password/init.rb +8 -0
  44. data/plugins/forgotten_password/spec/controller_spec.rb +236 -0
  45. data/plugins/forgotten_password/spec/model_spec.rb +52 -0
  46. data/plugins/forgotten_password/spec/spec_helper.rb +36 -0
  47. data/public/javascripts/master.js +0 -0
  48. data/public/stylesheets/master.css +2 -0
  49. data/spec/controllers/plugins/test_plugin.rb +17 -0
  50. data/spec/controllers/sessions_spec.rb +118 -0
  51. data/spec/controllers/users_spec.rb +119 -0
  52. data/spec/mailers/user_mailer_spec.rb +75 -0
  53. data/spec/merb_auth_spec.rb +231 -0
  54. data/spec/models/ar_model_spec.rb +50 -0
  55. data/spec/models/common_spec.rb +0 -0
  56. data/spec/models/model_spec.rb +23 -0
  57. data/spec/models/sq_model_spec.rb +50 -0
  58. data/spec/shared_specs/shared_model_spec.rb +445 -0
  59. data/spec/spec_helper.rb +114 -0
  60. data/spec/spec_helpers/helpers.rb +18 -0
  61. data/spec/spec_helpers/valid_model_hashes.rb +10 -0
  62. data/stubs/app/controllers/application.rb +2 -0
  63. data/stubs/app/controllers/main.rb +2 -0
  64. data/stubs/app/mailers/views/activation.text.erb +1 -0
  65. data/stubs/app/mailers/views/signup.text.erb +7 -0
  66. data/stubs/app/views/sessions/new.html.erb +14 -0
  67. data/stubs/app/views/users/new.html.erb +18 -0
  68. metadata +119 -0
@@ -0,0 +1,161 @@
1
+ module MerbAuth
2
+ module Adapter
3
+ module Common
4
+
5
+ def self.included(base)
6
+ base.send(:include, InstanceMethods)
7
+ base.send(:extend, ClassMethods)
8
+ end
9
+
10
+
11
+ module InstanceMethods
12
+
13
+ # Encrypts the password with the user salt
14
+ def encrypt(password)
15
+ self.class.encrypt(password, salt)
16
+ end
17
+
18
+ def encrypt_password
19
+ return if password.blank?
20
+ self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{MA[:login_field]}--") if new_record?
21
+ self.crypted_password = encrypt(password)
22
+ end
23
+
24
+ def authenticated?(password)
25
+ crypted_password == encrypt(password)
26
+ end
27
+
28
+ def password_required?
29
+ crypted_password.blank? || !password.blank?
30
+ end
31
+
32
+ def activate
33
+ self.reload unless new_record? # Make sure the model is up to speed before we try to save it
34
+ set_activated_data!
35
+ self.save
36
+
37
+ # send mail for activation
38
+ send_activation_notification if MA[:use_activation]
39
+ end
40
+
41
+ # Returns true if the user has just been activated.
42
+ def recently_activated?
43
+ @activated
44
+ end
45
+
46
+ def activated?
47
+ return false if self.new_record?
48
+ !! activation_code.nil?
49
+ end
50
+
51
+ # Alias for <tt>activated?</tt>
52
+ def active?; activated?; end
53
+
54
+ def set_login
55
+ return nil unless self.login.nil?
56
+ return nil if self.email.nil?
57
+ logn = self.email.split("@").first
58
+ # Check that that login is not taken
59
+ taken_logins = self.class.find_all_with_login_like("#{logn}%").map{|u| u.login}
60
+ if taken_logins.empty?
61
+ self.login = logn
62
+ else
63
+ taken_logins.first =~ /(\d*)$/
64
+ if $1.blank?
65
+ self.login = "#{logn}000"
66
+ else
67
+ self.login ="#{logn}#{$1.succ}"
68
+ end
69
+ end
70
+ end
71
+
72
+ def make_activation_code
73
+ if MA[:use_activation]
74
+ self.activation_code = self.class.make_key
75
+ else
76
+ set_activated_data!
77
+ end
78
+ end
79
+
80
+ def remember_token?
81
+ remember_token_expires_at && DateTime.now < DateTime.parse(remember_token_expires_at.to_s)
82
+ end
83
+
84
+ def remember_me_until(time)
85
+ self.remember_token_expires_at = time
86
+ self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")
87
+ save
88
+ end
89
+
90
+ # Useses seconds for the time
91
+ def remember_me_for(time)
92
+ time = time / Merb::Const::DAY
93
+ remember_me_until (DateTime.now + time)
94
+ end
95
+
96
+ # These create and unset the fields required for remembering users between browser closes
97
+ # Default of 2 weeks
98
+ def remember_me
99
+ remember_me_for (Merb::Const::WEEK * 2)
100
+ end
101
+
102
+ def forget_me
103
+ self.remember_token_expires_at = nil
104
+ self.remember_token = nil
105
+ self.save
106
+ end
107
+
108
+ def send_activation_notification
109
+ if MA[:use_activation]
110
+ deliver_email(:activation, :subject => (MA[:activation_subject] || "Welcome" ))
111
+ end
112
+ end
113
+
114
+ def send_signup_notification
115
+ if MA[:use_activation]
116
+ deliver_email(:signup, :subject => (MA[:welcome_subject] || "Please Activate Your Account") )
117
+ end
118
+ end
119
+
120
+ def send_forgot_password
121
+ deliver_email(:forgot_password, :subject => (MA[:password_request_subject] || "Request to change your password"))
122
+ end
123
+
124
+ def deliver_email(action, params)
125
+ return if defined?(Merb) && Merb.testing?
126
+ from = MA[:from_email]
127
+ MA::UserMailer.dispatch_and_deliver(action, params.merge(:from => from, :to => self.email), MA[:single_resource] => self)
128
+ end
129
+
130
+ private
131
+ def set_activated_data!
132
+ @activated = true
133
+ self.activated_at = DateTime.now
134
+ self.activation_code = nil
135
+ true
136
+ end
137
+
138
+ end
139
+
140
+ module ClassMethods
141
+
142
+ # Encrypts some data with the salt.
143
+ def encrypt(password, salt)
144
+ Digest::SHA1.hexdigest("--#{salt}--#{password}--")
145
+ end
146
+
147
+ # Authenticates a user by their login field and unencrypted password. Returns the user or nil.
148
+ def authenticate(field, password)
149
+ @u = find_active_with_conditions(MA[:login_field] => field)
150
+ @u = @u && @u.authenticated?(password) ? @u : nil
151
+ end
152
+
153
+ def make_key
154
+ Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
155
+ end
156
+
157
+ end
158
+
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,28 @@
1
+ require 'dm-validations'
2
+ require 'dm-timestamps'
3
+ require 'dm-aggregates'
4
+
5
+ path = File.dirname(__FILE__)
6
+
7
+ if Merb.env?(:test)
8
+ # Need to make sure the class is removed when testing
9
+ # It should not impact a normal apps tests
10
+ if MA[:user]
11
+ klass = MA[:user]
12
+ Object.class_eval do
13
+ remove_const(klass.name) if klass
14
+ end
15
+ end
16
+ MA[:user] = nil
17
+ MerbAuth.module_eval do
18
+ remove_const("Adapter") if defined?(Adapter)
19
+ end
20
+ load path / ".." / "common.rb"
21
+ load path / "map.rb"
22
+ load path / "model.rb"
23
+
24
+ else
25
+ require path / ".." / "common"
26
+ require path / "map"
27
+ require path / "model"
28
+ end
@@ -0,0 +1,35 @@
1
+ module MerbAuth
2
+ module Adapter
3
+ module DataMapper
4
+ module Map
5
+
6
+ def self.included(base)
7
+ base.send(:include, InstanceMethods)
8
+ base.send(:extend, ClassMethods)
9
+ end
10
+
11
+ module InstanceMethods
12
+ end
13
+
14
+ module ClassMethods
15
+ def find_with_conditions(conditions)
16
+ first(conditions)
17
+ end
18
+
19
+ def find_all_with_login_like(logn)
20
+ all(:login.like => logn, :order => [:login.desc], :limit => 1)
21
+ end
22
+
23
+ def find_active_with_conditions(conditions)
24
+ first(conditions.merge(:activated_at.not => nil))
25
+ end
26
+
27
+ def clear_database_table
28
+ auto_migrate!
29
+ end
30
+ end # ClassMethods
31
+
32
+ end # Map
33
+ end # DataMapper
34
+ end # Adapters
35
+ end # MerbAuth
@@ -0,0 +1,72 @@
1
+ module MerbAuth
2
+ module Adapter
3
+ module DataMapper
4
+
5
+ def self.included(base)
6
+ # Ensure base is a resource
7
+ base.send(:include, ::DataMapper::Resource) unless ::DataMapper::Resource > base
8
+
9
+ # Setup the properties for the model
10
+ set_model_class_decs!(base)
11
+ base.send(:include, Map)
12
+ base.send(:include, InstanceMethods )
13
+ base.send(:include, Common)
14
+
15
+ MA[:single_resource] ||= base.name.snake_case.gsub("::", "__").to_sym
16
+ MA[:plural_resource] ||= MA[:single_resource].to_s.pluralize.to_sym
17
+
18
+ MA[:user] = base
19
+ end
20
+
21
+ module InstanceMethods
22
+
23
+ end # InstanceMethods
24
+
25
+ private
26
+ def self.set_model_class_decs!(base)
27
+ base.class_eval do
28
+ # Sets the login to a downcased version
29
+ def login=(value)
30
+ attribute_set(:login, value.downcase) unless value.nil?
31
+ end
32
+
33
+ end
34
+ end # self.set_model_class_decs!
35
+
36
+ module DefaultModelSetup
37
+
38
+ def self.included(base)
39
+ base.class_eval do
40
+ attr_accessor :password, :password_confirmation
41
+
42
+ property :id, Integer, :serial => true
43
+ property :login, String, :nullable => false, :length => 3..40, :unique => true
44
+ property :email, String, :nullable => false, :unique => true
45
+ property :created_at, DateTime
46
+ property :updated_at, DateTime
47
+ property :activated_at, DateTime
48
+ property :activation_code, String
49
+ property :crypted_password, String
50
+ property :salt, String
51
+ property :remember_token_expires_at, DateTime
52
+ property :remember_token, String
53
+ property :password_reset_key, String, :writer => :protected
54
+
55
+ validates_is_unique :password_reset_key, :if => Proc.new{|m| !m.password_reset_key.nil?}
56
+ validates_present :password, :if => proc{|m| m.password_required?}
57
+ validates_is_confirmed :password, :if => proc{|m| m.password_required?}
58
+
59
+ before :valid? do
60
+ set_login
61
+ end
62
+
63
+ before :save, :encrypt_password
64
+ before :create, :make_activation_code
65
+ after :create, :send_signup_notification
66
+ end
67
+ end
68
+ end # DefaultModelSetup
69
+
70
+ end # DataMapper
71
+ end # Adapter
72
+ end
File without changes
@@ -0,0 +1,26 @@
1
+ require 'sequel'
2
+
3
+ path = File.dirname(__FILE__)
4
+
5
+ if Merb.env?(:test)
6
+ # Need to make sure the class is removed when testing
7
+ # It should not impact a normal apps tests
8
+ if MA[:user]
9
+ klass = MA[:user]
10
+ Object.class_eval do
11
+ remove_const(klass.name) if klass
12
+ end
13
+ end
14
+ MA[:user] = nil
15
+ MerbAuth.module_eval do
16
+ remove_const("Adapter") if defined?(Adapter)
17
+ end
18
+ load path / ".." / "common.rb"
19
+ load path / "map.rb"
20
+ load path / "model.rb"
21
+
22
+ else
23
+ require path / ".." / "common"
24
+ require path / "map"
25
+ require path / "model"
26
+ end
@@ -0,0 +1,35 @@
1
+ module MerbAuth
2
+ module Adapter
3
+ module Sequel
4
+ module Map
5
+
6
+ def self.included(base)
7
+ base.send(:include, InstanceMethods)
8
+ base.send(:extend, ClassMethods)
9
+ end
10
+
11
+ module InstanceMethods
12
+ end
13
+
14
+ module ClassMethods
15
+ def find_with_conditions(conditions)
16
+ filter(conditions).first
17
+ end
18
+
19
+ def find_all_with_login_like(logn)
20
+ filter(:login.like(logn)).order(:login.desc).limit(1)
21
+ end
22
+
23
+ def find_active_with_conditions(conditions)
24
+ filter(conditions).exclude(:activated_at => nil).first
25
+ end
26
+
27
+ def clear_database_table
28
+ delete
29
+ end
30
+ end # ClassMethods
31
+
32
+ end # Map
33
+ end # Sequel
34
+ end # Adapters
35
+ end # MerbAuth
@@ -0,0 +1,86 @@
1
+ module MerbAuth
2
+ module Adapter
3
+ module Sequel
4
+
5
+ def self.included(base)
6
+ # Ensure base is a resource
7
+ raise "Mixin class is not an sequel model class" unless base.ancestors.include?(::Sequel::Model)
8
+ set_model_class_decs!(base)
9
+
10
+ base.send(:include, Map)
11
+ base.send(:include, InstanceMethods )
12
+ base.send(:include, Common)
13
+
14
+ MA[:single_resource] ||= base.name.snake_case.to_sym
15
+ MA[:plural_resource] ||= MA[:single_resource].to_s.pluralize.to_sym
16
+
17
+ MA[:user] = base
18
+ end
19
+
20
+
21
+ module InstanceMethods
22
+
23
+ def login=(login_name)
24
+ self[:login] = login_name.downcase unless login_name.nil?
25
+ end
26
+
27
+ def new_record?
28
+ new?
29
+ end
30
+ end
31
+
32
+
33
+ private
34
+ def self.set_model_class_decs!(base)
35
+ # base.instance_eval do
36
+ # # Virtual attribute for the unencrypted password
37
+ # attr_accessor :password, :password_confirmation
38
+ # validates_presence_of :login, :email
39
+ # validates_presence_of :password, :if => :password_required?
40
+ # validates_presence_of :password_confirmation, :if => :password_required?
41
+ # validates_length_of :password, :within => 4..40, :if => :password_required?
42
+ # validates_confirmation_of :password, :if => :password_required?
43
+ # validates_length_of :login, :within => 3..40
44
+ # validates_length_of :email, :within => 3..100
45
+ # validates_uniqueness_of :login, :email, :case_sensitive => false
46
+ # validates_uniqueness_of :password_reset_key, :if => Proc.new{|m| !m.password_reset_key.nil?}
47
+ #
48
+ #
49
+ # before_save :encrypt_password
50
+ # before_validation :set_login
51
+ # before_create :make_activation_code
52
+ # after_create :send_signup_notification
53
+ # end
54
+ end
55
+
56
+ module DefaultModelSetup
57
+
58
+ def self.included(base)
59
+ base.instance_eval do
60
+ # Virtual attribute for the unencrypted password
61
+ attr_accessor :password, :password_confirmation
62
+
63
+ validates_presence_of :login, :email
64
+ validates_presence_of :password, :if => :password_required?
65
+ validates_presence_of :password_confirmation, :if => :password_required?
66
+ validates_length_of :password, :within => 4..40, :if => :password_required?
67
+ validates_confirmation_of :password, :if => :password_required?
68
+ validates_length_of :login, :within => 3..40
69
+ validates_length_of :email, :within => 3..100
70
+ validates_uniqueness_of :login, :email, :case_sensitive => false
71
+ validates_uniqueness_of :password_reset_key, :if => Proc.new{|m| !m.password_reset_key.nil?}
72
+
73
+ before_save :encrypt_password
74
+ before_validation :set_login
75
+ #before_create :make_activation_code
76
+ before_save do
77
+ make_activation_code if id.nil?
78
+ end
79
+ after_create :send_signup_notification
80
+ end
81
+ end
82
+ end # DefaultModelSetup
83
+
84
+ end # Sequel
85
+ end # Adapter
86
+ end # MerbAuth