rapid 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.
- data/Rakefile +66 -0
- data/lib/rad/http_controller/acts_as/authenticated.rb +131 -0
- data/lib/rad/http_controller/acts_as/authenticated_master_domain.rb +119 -0
- data/lib/rad/http_controller/acts_as/authorized.rb +83 -0
- data/lib/rad/http_controller/acts_as/localized.rb +27 -0
- data/lib/rad/http_controller/acts_as/multitenant.rb +53 -0
- data/lib/rad/http_controller/helpers/service_mix_helper.rb +50 -0
- data/lib/rad/http_controller.rb +15 -0
- data/lib/rad/lib/text_utils.rb +334 -0
- data/lib/rad/locales/en.yml +80 -0
- data/lib/rad/locales/ru.yml +83 -0
- data/lib/rad/locales.rb +2 -0
- data/lib/rad/models/account.rb +88 -0
- data/lib/rad/models/default_permissions.yml +26 -0
- data/lib/rad/models/micelaneous.rb +1 -0
- data/lib/rad/models/role.rb +88 -0
- data/lib/rad/models/secure_token.rb +33 -0
- data/lib/rad/models/space.rb +184 -0
- data/lib/rad/models/user.rb +158 -0
- data/lib/rad/models.rb +41 -0
- data/lib/rad/mongo_mapper/acts_as/authenticated_by_open_id.rb +29 -0
- data/lib/rad/mongo_mapper/acts_as/authenticated_by_password.rb +120 -0
- data/lib/rad/mongo_mapper/acts_as/authorized.rb +197 -0
- data/lib/rad/mongo_mapper/acts_as/authorized_object.rb +171 -0
- data/lib/rad/mongo_mapper/multitenant.rb +34 -0
- data/lib/rad/mongo_mapper/rad_micelaneous.rb +43 -0
- data/lib/rad/mongo_mapper/space_keys.rb +62 -0
- data/lib/rad/mongo_mapper/text_processor.rb +47 -0
- data/lib/rad/mongo_mapper.rb +20 -0
- data/lib/rad/paperclip/callbacks.rb +40 -0
- data/lib/rad/paperclip/extensions.rb +64 -0
- data/lib/rad/paperclip/integration.rb +165 -0
- data/lib/rad/paperclip/mime.rb +5 -0
- data/lib/rad/paperclip/validations.rb +64 -0
- data/lib/rad/paperclip.rb +11 -0
- data/lib/rad/spec/controller.rb +51 -0
- data/lib/rad/spec/model/factories.rb +65 -0
- data/lib/rad/spec/model.rb +85 -0
- data/lib/rad/spec/rem_helper.rb +145 -0
- data/lib/rad/spec.rb +4 -0
- data/lib/rad/tasks/backup.rake +64 -0
- data/lib/rad/tasks/initialize.rake +35 -0
- data/lib/rad.rb +32 -0
- data/readme.md +3 -0
- data/spec/controller/authorization_spec.rb +146 -0
- data/spec/controller/helper.rb +14 -0
- data/spec/lib/helper.rb +7 -0
- data/spec/lib/text_utils_spec.rb +238 -0
- data/spec/models/authorization_spec.rb +93 -0
- data/spec/models/authorized_object_spec.rb +258 -0
- data/spec/models/file_audit_spec/100.txt +1 -0
- data/spec/models/file_audit_spec/302.txt +3 -0
- data/spec/models/file_audit_spec.rb +168 -0
- data/spec/models/helper.rb +11 -0
- data/spec/models/space_key_spec.rb +68 -0
- data/spec/models/user_spec.rb +80 -0
- data/spec/mongo_mapper/basic_spec.rb +41 -0
- data/spec/mongo_mapper/helper.rb +10 -0
- data/spec/spec.opts +4 -0
- metadata +138 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
class Role
|
2
|
+
ORDERED_ROLES = %w{manager member user}
|
3
|
+
SYSTEM_ROLES = %w{admin anonymous manager member owner registered user}.sort.freeze
|
4
|
+
PRESERVED_USER_NAMES = (SYSTEM_ROLES + ['admin']).sort.freeze
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def normalize_roles roles
|
9
|
+
ordinary_roles, ordered_roles = split roles
|
10
|
+
ordinary_roles << lower_role(ordered_roles)
|
11
|
+
ordinary_roles.sort
|
12
|
+
end
|
13
|
+
|
14
|
+
def denormalize_to_higher_roles roles
|
15
|
+
ordinary_roles, ordered_roles = split roles
|
16
|
+
ordinary_roles.push *higher_roles(lower_role(ordered_roles))
|
17
|
+
ordinary_roles.sort
|
18
|
+
end
|
19
|
+
|
20
|
+
def denormalize_to_lower_roles roles
|
21
|
+
ordinary_roles, ordered_roles = split roles
|
22
|
+
ordinary_roles.push *lower_roles(higher_role(ordered_roles))
|
23
|
+
ordinary_roles.sort
|
24
|
+
end
|
25
|
+
|
26
|
+
def higher_role roles
|
27
|
+
ORDERED_ROLES.each do |role|
|
28
|
+
return role if roles.include? role
|
29
|
+
end
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def lower_role roles
|
34
|
+
ORDERED_ROLES.reverse.each do |role|
|
35
|
+
return role if roles.include? role
|
36
|
+
end
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def major_roles roles
|
41
|
+
major_roles = roles.select{|role| !SYSTEM_ROLES.include?(role)}
|
42
|
+
if higher_role = higher_role(roles)
|
43
|
+
major_roles << higher_role
|
44
|
+
end
|
45
|
+
major_roles.sort
|
46
|
+
end
|
47
|
+
|
48
|
+
def minor_roles roles
|
49
|
+
minor_roles = roles.select{|role| !SYSTEM_ROLES.include?(role)}
|
50
|
+
if lower_role = lower_role(roles)
|
51
|
+
minor_roles << lower_role
|
52
|
+
end
|
53
|
+
minor_roles.sort
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
def split roles
|
58
|
+
ordinary_roles = []
|
59
|
+
ordered_roles = []
|
60
|
+
|
61
|
+
roles.collect do |role|
|
62
|
+
if ORDERED_ROLES.include? role
|
63
|
+
ordered_roles << role
|
64
|
+
else
|
65
|
+
ordinary_roles << role
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
[ordinary_roles, ordered_roles]
|
70
|
+
end
|
71
|
+
|
72
|
+
def lower_roles role
|
73
|
+
return [] if role.nil?
|
74
|
+
|
75
|
+
role.must_be.in ORDERED_ROLES
|
76
|
+
index = ORDERED_ROLES.index role
|
77
|
+
ORDERED_ROLES[index..-1]
|
78
|
+
end
|
79
|
+
|
80
|
+
def higher_roles role
|
81
|
+
return [] if role.nil?
|
82
|
+
|
83
|
+
role.must_be.in ORDERED_ROLES
|
84
|
+
index = ORDERED_ROLES.index role
|
85
|
+
ORDERED_ROLES[0..index]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class SecureToken
|
2
|
+
include MongoMapper::Document
|
3
|
+
|
4
|
+
connect_to_global_database
|
5
|
+
|
6
|
+
key :_type, String
|
7
|
+
|
8
|
+
key :values, Hash
|
9
|
+
key :token, String, :default => lambda{String.secure_token}
|
10
|
+
key :expires_at, Time, :default => lambda{30.minutes.from_now}
|
11
|
+
timestamps!
|
12
|
+
|
13
|
+
validates_presence_of :token, :expires_at
|
14
|
+
|
15
|
+
def expired?
|
16
|
+
expires_at >= Time.now.utc
|
17
|
+
end
|
18
|
+
|
19
|
+
# defer do
|
20
|
+
ensure_index :token, :unique => true
|
21
|
+
ensure_index :expires_at
|
22
|
+
ensure_index :user_id
|
23
|
+
# end
|
24
|
+
|
25
|
+
def self.by_token token
|
26
|
+
return nil if token.blank?
|
27
|
+
first :token => token, :expires_at.gte => Time.now.utc
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.by_token! token
|
31
|
+
return by_token(token) || raise(MongoMapper::DocumentNotFound)
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
class Space
|
2
|
+
include MongoMapper::Document
|
3
|
+
|
4
|
+
#
|
5
|
+
# Multitenant
|
6
|
+
#
|
7
|
+
connect_to_global_database
|
8
|
+
|
9
|
+
|
10
|
+
key :name, String, :protected => true
|
11
|
+
key :title, String
|
12
|
+
key :account_id, ObjectId, :protected => true
|
13
|
+
|
14
|
+
timestamps!
|
15
|
+
|
16
|
+
|
17
|
+
#
|
18
|
+
# Indexes
|
19
|
+
#
|
20
|
+
# defer do
|
21
|
+
ensure_index :name
|
22
|
+
ensure_index :account_id
|
23
|
+
# end
|
24
|
+
|
25
|
+
def default?; name == 'default' end
|
26
|
+
def self.default? name; name == 'default' end
|
27
|
+
|
28
|
+
|
29
|
+
belongs_to :account
|
30
|
+
|
31
|
+
validates_presence_of :name, :account
|
32
|
+
validates_uniqueness_of :name, :scope => :account_id
|
33
|
+
validates_format_of :name, :with => STRONG_NAME
|
34
|
+
|
35
|
+
def self.current= space
|
36
|
+
Thread.current['current_space'] = space
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.current
|
40
|
+
Thread.current['current_space'].must_be.defined
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.current?
|
44
|
+
!!Thread.current['current_space']
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Validation
|
49
|
+
#
|
50
|
+
validate :validate_default
|
51
|
+
def validate_default
|
52
|
+
errors.add :base, t(:forbiden_to_change_default_space) if name_changed? and name_was == 'default'
|
53
|
+
end
|
54
|
+
protected :validate_default
|
55
|
+
|
56
|
+
|
57
|
+
#
|
58
|
+
# Roles and Permissions
|
59
|
+
#
|
60
|
+
key :custom_roles, Array
|
61
|
+
def custom_roles_as_string
|
62
|
+
custom_roles.join("\n")
|
63
|
+
end
|
64
|
+
def custom_roles_as_string= str
|
65
|
+
self.custom_roles = str.strip.split("\n")
|
66
|
+
end
|
67
|
+
|
68
|
+
SPECIAL_PERMISSIONS = {
|
69
|
+
'global_administration' => ['admin'],
|
70
|
+
'account_administration' => ['admin'],
|
71
|
+
'view' => ['owner', 'manager']
|
72
|
+
}
|
73
|
+
|
74
|
+
def permissions
|
75
|
+
self.class.permissions
|
76
|
+
end
|
77
|
+
|
78
|
+
@@permissions = nil
|
79
|
+
def self.permissions
|
80
|
+
unless @@permissions
|
81
|
+
@@permissions = YAML.load_file("#{File.dirname __FILE__}/default_permissions.yml")
|
82
|
+
@@permissions.merge!(SPECIAL_PERMISSIONS)
|
83
|
+
end
|
84
|
+
@@permissions
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
#
|
89
|
+
# Links
|
90
|
+
#
|
91
|
+
key :default_url, String
|
92
|
+
key :menu, Array
|
93
|
+
|
94
|
+
def menu_as_string
|
95
|
+
menu.to_a.collect{|name, url| "#{name}:#{url}"}.join("\n")
|
96
|
+
end
|
97
|
+
|
98
|
+
def menu_as_string=(str)
|
99
|
+
self.menu = []
|
100
|
+
lines = str.split("\n")
|
101
|
+
lines.each do |line|
|
102
|
+
name, url = line.split(':').collect(&:strip)
|
103
|
+
menu << [name, url] unless name.blank? or url.blank?
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
#
|
109
|
+
# Language
|
110
|
+
#
|
111
|
+
AVAILABLE_LANGUAGES = %w{en ru}
|
112
|
+
crystal.after :config do |config|
|
113
|
+
key :language, String, :default => config.default_language('en')
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
#
|
118
|
+
# Files audit
|
119
|
+
#
|
120
|
+
key :max_user_files_size, Integer, :default => 0
|
121
|
+
|
122
|
+
|
123
|
+
#
|
124
|
+
# Other
|
125
|
+
#
|
126
|
+
plugin MongoMapper::Plugins::TextProcessor
|
127
|
+
markup_key :bottom_text
|
128
|
+
|
129
|
+
|
130
|
+
#
|
131
|
+
# Theme support
|
132
|
+
#
|
133
|
+
key :theme, String, :default => 'default'
|
134
|
+
def self.available_themes
|
135
|
+
config.available_themes(['default'])
|
136
|
+
end
|
137
|
+
# defer do
|
138
|
+
validates_inclusion_of :theme, :within => Space.available_themes
|
139
|
+
# end
|
140
|
+
|
141
|
+
include Paperclip
|
142
|
+
has_attached_file :logo
|
143
|
+
validates_maximum_file_size :logo
|
144
|
+
|
145
|
+
def slug; name end
|
146
|
+
|
147
|
+
|
148
|
+
#
|
149
|
+
# Wigets
|
150
|
+
#
|
151
|
+
# has_many :resources #, :dependent => :destroy
|
152
|
+
# has_many :votes #, :dependent => :destroy
|
153
|
+
|
154
|
+
|
155
|
+
# def self.account_inheritable_key key, type, options = {}
|
156
|
+
# key = key.to_s
|
157
|
+
#
|
158
|
+
# self.key key, type, options
|
159
|
+
# Account.send :key, key, type, options
|
160
|
+
#
|
161
|
+
# define_method key do
|
162
|
+
# unless merged_value = cache[key]
|
163
|
+
# account_value = account.send akey
|
164
|
+
# value = send(key)
|
165
|
+
# merged_value = merge account_value, value
|
166
|
+
# cache[key] = merged_value
|
167
|
+
# end
|
168
|
+
# merged_value
|
169
|
+
# end
|
170
|
+
# end
|
171
|
+
#
|
172
|
+
# protected
|
173
|
+
# def self.merge parent_value, value
|
174
|
+
# return value || parent_value if value.nil? or parent_value.nil?
|
175
|
+
#
|
176
|
+
# if parent_value.is_a? Hash
|
177
|
+
# parent_value.merge(value)
|
178
|
+
# elsif parent_value.is_a? Array
|
179
|
+
# (parent_value + value).uniq
|
180
|
+
# else
|
181
|
+
# value
|
182
|
+
# end
|
183
|
+
# end
|
184
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
class User
|
2
|
+
include MongoMapper::Document
|
3
|
+
|
4
|
+
connect_to_global_database
|
5
|
+
|
6
|
+
set_collection_name 'users'
|
7
|
+
|
8
|
+
key :_type, String
|
9
|
+
|
10
|
+
key :name, String, :protected => true
|
11
|
+
key :email, String, :protected => true
|
12
|
+
key :state, String, :protected => true
|
13
|
+
timestamps!
|
14
|
+
|
15
|
+
def name= value
|
16
|
+
# write_attribute :name, (value ? value.downcase : nil)
|
17
|
+
super(value ? value.downcase : nil)
|
18
|
+
end
|
19
|
+
|
20
|
+
def email= value
|
21
|
+
# write_attribute :email, (value ? value.downcase : nil)
|
22
|
+
super(value ? value.downcase : nil)
|
23
|
+
end
|
24
|
+
|
25
|
+
# key :remember_token, String, :protected => true
|
26
|
+
# key :remember_token_expires_at, Time, :protected => true
|
27
|
+
# key :secure_token, String, :protected => true
|
28
|
+
# key :secure_token_expires_at, Time, :protected => true
|
29
|
+
|
30
|
+
#
|
31
|
+
# Validations
|
32
|
+
#
|
33
|
+
validates_presence_of :name
|
34
|
+
validates_length_of :name, :within => 4..40
|
35
|
+
validates_uniqueness_of :name
|
36
|
+
validates_format_of :name, :with => STRONG_NAME
|
37
|
+
|
38
|
+
EMAIL_NAME_REGEX = '[\w\.%\+\-]+'.freeze
|
39
|
+
DOMAIN_HEAD_REGEX = '(?:[A-Z0-9\-]+\.)+'.freeze
|
40
|
+
DOMAIN_TLD_REGEX = '(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|jobs|museum)'.freeze
|
41
|
+
EMAIL_REGEX = /\A#{EMAIL_NAME_REGEX}@#{DOMAIN_HEAD_REGEX}#{DOMAIN_TLD_REGEX}\z/i
|
42
|
+
|
43
|
+
validates_length_of :email, :within => 6..100, :allow_blank => true
|
44
|
+
validates_uniqueness_of :email, :allow_blank => true
|
45
|
+
validates_format_of :email, :with => EMAIL_REGEX, :allow_blank => true
|
46
|
+
|
47
|
+
|
48
|
+
#
|
49
|
+
# Indexes
|
50
|
+
#
|
51
|
+
# defer do
|
52
|
+
ensure_index :name, :unique => true
|
53
|
+
ensure_index :email
|
54
|
+
# ensure_index :remember_token #, :unique => true
|
55
|
+
ensure_index :state
|
56
|
+
ensure_index :created_at
|
57
|
+
ensure_index :updated_at
|
58
|
+
# end
|
59
|
+
|
60
|
+
|
61
|
+
#
|
62
|
+
# Autentication
|
63
|
+
#
|
64
|
+
plugin MongoMapper::Plugins::OpenIdAuthentication
|
65
|
+
acts_as_authenticated_by_open_id
|
66
|
+
|
67
|
+
plugin MongoMapper::Plugins::PasswordAuthentication
|
68
|
+
acts_as_authenticated_by_password
|
69
|
+
|
70
|
+
def validate_authentication
|
71
|
+
if crypted_password.blank? and open_ids.blank?
|
72
|
+
errors.add :password, t(:should_not_be_blank)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
protected :validate_authentication
|
76
|
+
validate :validate_authentication
|
77
|
+
|
78
|
+
|
79
|
+
#
|
80
|
+
# Lifecycle
|
81
|
+
#
|
82
|
+
state_machine :state, :initial => :inactive do
|
83
|
+
|
84
|
+
# after_transition :on => :wait_for_email_confirmation do |_self, trans|
|
85
|
+
# _self.generate_secure_token!
|
86
|
+
# UserStatusMailer.deliver_signup_notification _self
|
87
|
+
# end
|
88
|
+
|
89
|
+
# after_transition :on => :activate do |_self, trans|
|
90
|
+
# _self.clear_secure_token!
|
91
|
+
# UserStatusMailer.deliver_activation_notification _self
|
92
|
+
# end
|
93
|
+
|
94
|
+
# on :wait_for_email_confirmation do
|
95
|
+
# transition any => :inactive
|
96
|
+
# end
|
97
|
+
|
98
|
+
on :activate do
|
99
|
+
transition all => :active
|
100
|
+
end
|
101
|
+
|
102
|
+
on :inactivate do
|
103
|
+
transition all => :inactive
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
#
|
110
|
+
# Authorization
|
111
|
+
#
|
112
|
+
plugin MongoMapper::Plugins::SpaceKeys
|
113
|
+
plugin MongoMapper::Plugins::Authorized
|
114
|
+
acts_as_authorized
|
115
|
+
|
116
|
+
|
117
|
+
#
|
118
|
+
# Profile
|
119
|
+
#
|
120
|
+
key :first_name, String
|
121
|
+
key :last_name, String
|
122
|
+
|
123
|
+
|
124
|
+
#
|
125
|
+
# Helpers
|
126
|
+
#
|
127
|
+
class << self
|
128
|
+
|
129
|
+
def [] name
|
130
|
+
find_by_name name.to_s
|
131
|
+
end
|
132
|
+
|
133
|
+
def current= current
|
134
|
+
Thread.current['current_user'] = current
|
135
|
+
end
|
136
|
+
|
137
|
+
def current
|
138
|
+
Thread.current['current_user'].must_be.defined
|
139
|
+
end
|
140
|
+
|
141
|
+
def current?
|
142
|
+
Thread.current['current_user'] != nil
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
#
|
149
|
+
# Other
|
150
|
+
#
|
151
|
+
space_key :files_size, Integer, :default => 0
|
152
|
+
def to_param; name end
|
153
|
+
validate do |u|
|
154
|
+
u.space_keys_containers.size.must == 0 if u.anonymous?
|
155
|
+
end
|
156
|
+
|
157
|
+
def slug; name end
|
158
|
+
end
|
data/lib/rad/models.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# support
|
2
|
+
require 'state_machine'
|
3
|
+
require 'crystal/environment'
|
4
|
+
require 'rad/mongo_mapper'
|
5
|
+
require 'rad/paperclip'
|
6
|
+
|
7
|
+
# config
|
8
|
+
# use database in config if provided
|
9
|
+
if crystal.include? :config and crystal.config.database?
|
10
|
+
config = crystal.config
|
11
|
+
db_config_for_current_environment = config.database!.send("#{config.environment}!").to_hash
|
12
|
+
MongoMapper.db_config = db_config_for_current_environment
|
13
|
+
|
14
|
+
|
15
|
+
# hide index creation from logging
|
16
|
+
# MongoMapper.logger.info "Checking and creating MongoMapper indexes"
|
17
|
+
# MongoMapper.temporary_silince_logger do
|
18
|
+
# MongoMapper.call_deferred
|
19
|
+
# end
|
20
|
+
end
|
21
|
+
|
22
|
+
MongoMapper.logger = crystal.logger
|
23
|
+
|
24
|
+
# models
|
25
|
+
module Rad
|
26
|
+
def self.multitenant_mode?
|
27
|
+
Space.current?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
%w(
|
32
|
+
micelaneous
|
33
|
+
role
|
34
|
+
|
35
|
+
secure_token
|
36
|
+
user
|
37
|
+
account
|
38
|
+
space
|
39
|
+
).each{|n| require "rad/models/#{n}"}
|
40
|
+
|
41
|
+
require 'rad/locales'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Plugins
|
3
|
+
module OpenIdAuthentication
|
4
|
+
|
5
|
+
module InstanceMethods
|
6
|
+
def authenticated_by_open_id? open_id
|
7
|
+
self.open_id == open_id
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def acts_as_authenticated_by_open_id
|
13
|
+
key :open_ids, Array
|
14
|
+
|
15
|
+
# defer do
|
16
|
+
ensure_index :open_ids
|
17
|
+
# end
|
18
|
+
|
19
|
+
validates_uniqueness_of :open_ids, :allow_blank => true
|
20
|
+
end
|
21
|
+
|
22
|
+
def authenticate_by_open_id open_id
|
23
|
+
return nil if open_id.blank?
|
24
|
+
User.first :conditions => {:state => 'active', :open_ids => open_id}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Plugins
|
3
|
+
module PasswordAuthentication
|
4
|
+
SITE_KEY = '3eed5a60c1bf8d43de5d0560e9fc2442fe74fdad'
|
5
|
+
DIGEST_STRETCHES = 10
|
6
|
+
PASSWORD_LENGTH = 3..40
|
7
|
+
|
8
|
+
|
9
|
+
module InstanceMethods
|
10
|
+
def authenticated_by_password? password
|
11
|
+
return false if crypted_password.blank? or password.blank?
|
12
|
+
self.crypted_password == self.class.encrypt_password(password, salt)
|
13
|
+
end
|
14
|
+
|
15
|
+
# def reset_password password, password_confirmation
|
16
|
+
# self.password, self.password_confirmation = password, password_confirmation
|
17
|
+
# self.secure_token = nil
|
18
|
+
# end
|
19
|
+
|
20
|
+
def update_password password, password_confirmation, old_password
|
21
|
+
if crypted_password.blank?
|
22
|
+
self.password, self.password_confirmation = password, password_confirmation
|
23
|
+
elsif authenticated_by_password? old_password
|
24
|
+
self.password, self.password_confirmation = password, password_confirmation
|
25
|
+
true
|
26
|
+
else
|
27
|
+
errors.add :base, t(:invalid_old_password)
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# def update_password password, confirmation
|
33
|
+
# self.password, self.password_confirmation = password, confirmation
|
34
|
+
# encrypt_password
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# def generate_secure_token!
|
38
|
+
# self.secure_token = AuthStrategy.generate_token
|
39
|
+
# self.secure_token_expires_at = AuthStrategy::SECURE_TOKEN_EXPIRATION.from_now
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# def clear_secure_token!
|
43
|
+
# self.secure_token = nil
|
44
|
+
# self.secure_token_expires_at = nil
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# def forgot_password
|
48
|
+
# generate_secure_token!
|
49
|
+
# UserStatusMailer.deliver_forgot_password self
|
50
|
+
# end
|
51
|
+
|
52
|
+
def password= password
|
53
|
+
@password = password
|
54
|
+
encrypt_password!
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
def encrypt_password!
|
59
|
+
if password.blank?
|
60
|
+
self.crypted_password = ""
|
61
|
+
else
|
62
|
+
self.salt ||= self.class.generate_token
|
63
|
+
self.crypted_password = self.class.encrypt_password password, salt
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def validate_password?
|
68
|
+
!password.nil?
|
69
|
+
# crypted_password.blank? or !password.blank?
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
module ClassMethods
|
74
|
+
def acts_as_authenticated_by_password
|
75
|
+
key :crypted_password, String, :protected => true
|
76
|
+
key :salt, String, :protected => true
|
77
|
+
|
78
|
+
attr_reader :password
|
79
|
+
validates_confirmation_of :password, :if => :validate_password?
|
80
|
+
validates_length_of :password, :within => PASSWORD_LENGTH, :if => :validate_password?
|
81
|
+
end
|
82
|
+
|
83
|
+
def authenticate_by_password name, password
|
84
|
+
return nil if name.blank? or password.blank?
|
85
|
+
u = User.first :conditions => {:state => 'active', :name => name}
|
86
|
+
u && u.authenticated_by_password?(password) ? u : nil
|
87
|
+
end
|
88
|
+
|
89
|
+
# def by_secure_token token
|
90
|
+
# first :conditions => {
|
91
|
+
# :secure_token => token,
|
92
|
+
# :secure_token_expires_at => {:$gt => Time.now.utc}
|
93
|
+
# }
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# def by_open_id id
|
97
|
+
# return nil if id.blank?
|
98
|
+
# first :open_ids => id
|
99
|
+
# end
|
100
|
+
|
101
|
+
def encrypt_password password, salt
|
102
|
+
digest = SITE_KEY
|
103
|
+
DIGEST_STRETCHES.times do
|
104
|
+
digest = secure_digest(digest, salt, password, SITE_KEY)
|
105
|
+
end
|
106
|
+
digest
|
107
|
+
end
|
108
|
+
|
109
|
+
def generate_token
|
110
|
+
secure_digest Time.now, (1..10).map{ rand.to_s }
|
111
|
+
end
|
112
|
+
|
113
|
+
protected
|
114
|
+
def secure_digest *args
|
115
|
+
Digest::SHA1.hexdigest(args.flatten.join('--'))
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|