fortifier 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +29 -0
- data/app/controllers/fortifier/application_controller.rb +17 -0
- data/app/controllers/fortifier/auth_users_controller.rb +107 -0
- data/app/helpers/fortifier/application_helper.rb +4 -0
- data/app/helpers/fortifier/auth_users_helper.rb +4 -0
- data/app/helpers/fortifier/date_helper.rb +46 -0
- data/app/helpers/fortifier/passwords_helper.rb +4 -0
- data/app/mailers/fortifier/notifier_mailer.rb +66 -0
- data/app/models/fortifier/auth_log.rb +18 -0
- data/app/models/fortifier/auth_rule.rb +11 -0
- data/app/models/fortifier/auth_steps/check_for_blocked_ip.rb +22 -0
- data/app/models/fortifier/auth_steps/check_for_blocked_user.rb +16 -0
- data/app/models/fortifier/auth_steps/check_for_us_external_ip.rb +14 -0
- data/app/models/fortifier/auth_steps/check_for_whitelisted_ip.rb +38 -0
- data/app/models/fortifier/auth_steps/initialize_auth_attempt.rb +19 -0
- data/app/models/fortifier/auth_steps/initialize_batch_sso_auth_attempt.rb +18 -0
- data/app/models/fortifier/auth_steps/initialize_on_demand_sso_auth_attempt.rb +18 -0
- data/app/models/fortifier/auth_steps/messaging.rb +16 -0
- data/app/models/fortifier/auth_user.rb +256 -0
- data/app/models/fortifier/auth_user_api.rb +356 -0
- data/app/models/fortifier/auth_users_auth_rule.rb +8 -0
- data/app/models/fortifier/authentication.rb +17 -0
- data/app/models/fortifier/authentication_steps.rb +46 -0
- data/app/models/fortifier/batch_updater.rb +148 -0
- data/app/models/fortifier/max_mind.rb +64 -0
- data/app/models/fortifier/max_mind_reference_ip.rb +5 -0
- data/app/models/fortifier/rufus/rufus_password_expiration.rb +23 -0
- data/app/models/fortifier/secret.rb +189 -0
- data/app/views/fortifier/notifier_mailer/account_ip_blocked.html.erb +30 -0
- data/app/views/fortifier/notifier_mailer/account_ip_blocked_providigm.html.erb +20 -0
- data/app/views/fortifier/notifier_mailer/exception_notification.html.erb +88 -0
- data/app/views/fortifier/notifier_mailer/foreign_access.html.erb +22 -0
- data/app/views/fortifier/notifier_mailer/password_expiration.html.erb +28 -0
- data/app/views/fortifier/notifier_mailer/password_reset_token.html.erb +28 -0
- data/app/views/fortifier/notifier_mailer/task_exception.html.erb +18 -0
- data/app/views/layouts/fortifier/application.html.erb +14 -0
- data/config/Initializers/bcrypt.rb +1 -0
- data/config/Initializers/ipaddr.rb +1 -0
- data/config/database.yml +18 -0
- data/config/routes.rb +27 -0
- data/db/migrate/20130916194012_create_fortifier_tables.rb +63 -0
- data/db/migrate/20140415210139_add_auth_user_search_keywords_field.rb +9 -0
- data/db/migration_scripts/20140403_temp_whitelist_migration.rb +5 -0
- data/lib/fortifier/engine.rb +40 -0
- data/lib/fortifier/version.rb +3 -0
- data/lib/fortifier.rb +4 -0
- data/lib/tasks/fortifier_tasks.rake +4 -0
- metadata +176 -0
@@ -0,0 +1,256 @@
|
|
1
|
+
require 'attr-csv' # Don't delete! Questions? See Dave.
|
2
|
+
|
3
|
+
module Fortifier
|
4
|
+
class AuthUser < ActiveRecord::Base
|
5
|
+
ABAQIS = 1
|
6
|
+
EMP_SAT = 2
|
7
|
+
|
8
|
+
has_many :secrets
|
9
|
+
has_many :auth_logs
|
10
|
+
has_many :auth_users_auth_rules
|
11
|
+
|
12
|
+
attr_csv :app_uuids, :account_uuids, :search_keywords
|
13
|
+
|
14
|
+
before_create :create_unique_uuid
|
15
|
+
before_create :set_consecutive_failed_login_count
|
16
|
+
|
17
|
+
validate :login_is_present
|
18
|
+
validate :login_is_correct_length
|
19
|
+
validate :login_is_unique
|
20
|
+
validate :email_is_correct_length
|
21
|
+
validate :email_is_unique
|
22
|
+
validate :email_is_correct_format
|
23
|
+
validate :name_is_correct_format
|
24
|
+
validate :name_is_correct_length
|
25
|
+
validate :note_is_correct_length
|
26
|
+
|
27
|
+
# NOTE: These custom validations are used so that symbols/codes
|
28
|
+
# can be returned to applications, rather than explicit text.
|
29
|
+
# Fortifier provides the symbols and the apps are responsible
|
30
|
+
# for handling error messaging.
|
31
|
+
def login_is_present
|
32
|
+
return if login.present?
|
33
|
+
errors[:base] << :login_blank
|
34
|
+
end
|
35
|
+
|
36
|
+
def login_is_correct_length
|
37
|
+
return if login.blank?
|
38
|
+
return if (login.length >= 3 && login.length <= 60) unless login.blank?
|
39
|
+
errors[:base] << :login_length_incorrect
|
40
|
+
end
|
41
|
+
|
42
|
+
def login_is_unique
|
43
|
+
return if login.blank?
|
44
|
+
return if unique?(:login, login)
|
45
|
+
|
46
|
+
errors[:base] << :login_not_unique
|
47
|
+
end
|
48
|
+
|
49
|
+
def email_is_correct_length
|
50
|
+
return if (email.blank?)
|
51
|
+
return if (email.length >= 6 && email.length <= 100)
|
52
|
+
errors[:base] << :email_length_incorrect
|
53
|
+
end
|
54
|
+
|
55
|
+
def email_is_unique
|
56
|
+
return if (email.blank?)
|
57
|
+
return if unique?(:email, email)
|
58
|
+
errors[:base] << :email_not_unique
|
59
|
+
end
|
60
|
+
|
61
|
+
def email_is_correct_format
|
62
|
+
return if (email.blank?)
|
63
|
+
return if (Fortifier::Authentication::EMAIL_REGEX.match email).present?
|
64
|
+
errors[:base] << :email_format_incorrect
|
65
|
+
end
|
66
|
+
|
67
|
+
def name_is_correct_format
|
68
|
+
return if (name.blank?)
|
69
|
+
return if (Fortifier::Authentication::NAME_REGEX.match name).present?
|
70
|
+
errors[:base] << :name_format_incorrect
|
71
|
+
end
|
72
|
+
|
73
|
+
def name_is_correct_length
|
74
|
+
return if (name.blank? || name.length <= 100)
|
75
|
+
errors[:base] << :name_length_incorrect
|
76
|
+
end
|
77
|
+
|
78
|
+
def note_is_correct_length
|
79
|
+
return if (note.blank? || note.length <= 255)
|
80
|
+
errors[:base] << :note_length_incorrect
|
81
|
+
end
|
82
|
+
|
83
|
+
def current_secret
|
84
|
+
self.secrets.order("created_at, id asc").last
|
85
|
+
end
|
86
|
+
|
87
|
+
def current_secret_non_token
|
88
|
+
self.secrets.where(enc_type: (Secret::BCRYPT || Secret::SHA)).order("created_at, id asc").last
|
89
|
+
end
|
90
|
+
|
91
|
+
def secrets_match?(secret_string)
|
92
|
+
current_secret_model = current_secret_non_token
|
93
|
+
return false if current_secret_model.blank?
|
94
|
+
|
95
|
+
auth_result = current_secret_model.matches?(secret_string)
|
96
|
+
current_secret_model.update_encryption_method(secret_string) if (auth_result && current_secret_model.enc_type == "SHA")
|
97
|
+
|
98
|
+
return auth_result
|
99
|
+
end
|
100
|
+
|
101
|
+
def authenticated?(secret_string)
|
102
|
+
# TODO: (DK) do not increase consecutive_failed_logins if user is attempting a pw change
|
103
|
+
# move consecutive_failed_logins updates to a different method so this method
|
104
|
+
# doesn't do multiple things (code smell)
|
105
|
+
# TODO: (DK) remove if self
|
106
|
+
if self
|
107
|
+
if self.secrets_match?(secret_string)
|
108
|
+
self.update_column(:consecutive_failed_logins, 0)
|
109
|
+
true
|
110
|
+
else
|
111
|
+
self.update_column(:consecutive_failed_logins, self.consecutive_failed_logins + 1)
|
112
|
+
false
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.authenticate_batch_sso(account_uuid, token)
|
118
|
+
return nil if token.blank?
|
119
|
+
AuthUser.
|
120
|
+
joins(:secrets).
|
121
|
+
where("find_in_set(? , account_uuids_csv)
|
122
|
+
AND (expired IS NULL OR expired = false)
|
123
|
+
AND enc_type = '#{Secret::SSO_TOKEN}'
|
124
|
+
AND secret_value = ?", account_uuid, token).
|
125
|
+
first
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.authenticate_on_demand_sso(account_uuid, token)
|
129
|
+
return nil if token.blank?
|
130
|
+
AuthUser.
|
131
|
+
joins(:secrets).
|
132
|
+
where("find_in_set(? , account_uuids_csv)
|
133
|
+
AND (expired IS NULL OR expired = false)
|
134
|
+
AND enc_type = '#{Secret::SSO_TOKEN}'
|
135
|
+
AND secret_value = ?", account_uuid, token).
|
136
|
+
first
|
137
|
+
end
|
138
|
+
|
139
|
+
def blocked?
|
140
|
+
self.consecutive_failed_logins > 5
|
141
|
+
end
|
142
|
+
|
143
|
+
def disable!
|
144
|
+
self.update_column(:app_uuids_csv, nil)
|
145
|
+
self.update_column(:account_uuids_csv, nil)
|
146
|
+
end
|
147
|
+
|
148
|
+
def disabled?
|
149
|
+
self.app_uuids.blank? && self.account_uuids.blank?
|
150
|
+
end
|
151
|
+
|
152
|
+
def successful_log_in?
|
153
|
+
# TODO: clarify this so there's no confusion (user can be logged out with a count of 0,
|
154
|
+
# so technically they wouldn't be succesfully logged in, even though this method would
|
155
|
+
# say otherwise).
|
156
|
+
self.consecutive_failed_logins == 0
|
157
|
+
end
|
158
|
+
|
159
|
+
def matching_whitelist_ip?(ip)
|
160
|
+
return false if ip.blank?
|
161
|
+
au_auth_rule_ids = AuthUsersAuthRule.where(auth_user_id: self.id).pluck(:auth_rule_id)
|
162
|
+
auth_rule_ips = au_auth_rule_ids.map {|auid| AuthRule.find(auid).rule_value}
|
163
|
+
auth_rule_ips.flatten.include?(ip)
|
164
|
+
end
|
165
|
+
|
166
|
+
def public_attribute_hash
|
167
|
+
auth_log = self.auth_logs.last
|
168
|
+
{
|
169
|
+
uuid: self.uuid,
|
170
|
+
email: self.email,
|
171
|
+
login: self.login,
|
172
|
+
name: self.name,
|
173
|
+
note: self.note,
|
174
|
+
disabled: self.disabled?,
|
175
|
+
last_auth_log: ({user_agent: auth_log.user_agent, status: auth_log.status, created_at: auth_log.created_at.to_time} if auth_log)
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.paged_and_sorted_search(params)
|
180
|
+
search = params[:search]
|
181
|
+
sort_col = params[:sortcol] || 'name'
|
182
|
+
sort_dir = params[:sortdir] || 'asc'
|
183
|
+
per_page = (params[:per_page].blank? || params[:per_page]<25) ? 25 : params[:per_page]
|
184
|
+
page = params[:page] || 1
|
185
|
+
app_uuid = params[:app_uuid]
|
186
|
+
account_uuid = params[:account_uuid]
|
187
|
+
search_keywords = params[:search_keywords]
|
188
|
+
|
189
|
+
if search.blank?
|
190
|
+
user_search_query = ''
|
191
|
+
else
|
192
|
+
user_search_query = [:login, :email, :name, :note].map{|f| "#{f} LIKE '%#{search}%'"}.join(" OR ")
|
193
|
+
end
|
194
|
+
|
195
|
+
# aggregate_query is conditional b/c account_uuid isn't always provided (such as the Users page for providigm users in abaqis)
|
196
|
+
app_uuids_query = "app_uuids_csv = '#{app_uuid}'"
|
197
|
+
account_uuids_query = account_uuid ? " AND account_uuids_csv = '#{account_uuid}'" : ""
|
198
|
+
search_keywords_query = search_keywords ? " AND search_keywords_csv = '#{search_keywords}'" : ''
|
199
|
+
aggregate_query = app_uuids_query + account_uuids_query + search_keywords_query
|
200
|
+
|
201
|
+
# b/c there's no 'enabled' field on auth user and abaqis allows this sorting (providigm/users in abaqis)
|
202
|
+
if sort_col=='enabled'
|
203
|
+
Fortifier::AuthUser.where(aggregate_query)
|
204
|
+
.where(user_search_query)
|
205
|
+
.order("app_uuids_csv #{sort_dir}, account_uuids_csv #{sort_dir}")
|
206
|
+
.paginate(:page=>page, :per_page=>per_page)
|
207
|
+
elsif sort_col=='last_login_at'
|
208
|
+
# In other words, pull all users associated with the app in question (if available),
|
209
|
+
# joined with their most recent AuthLog,
|
210
|
+
# sort by the AuthLog created_at date (according to user preference),
|
211
|
+
# then paginate with WillPaginate. Capisce?
|
212
|
+
Fortifier::AuthUser.find_by_sql("SELECT * FROM fortifier_auth_users fau
|
213
|
+
LEFT OUTER JOIN ( select auth_user_id, max(created_at)
|
214
|
+
AS last_login_at
|
215
|
+
FROM fortifier_auth_logs fal
|
216
|
+
GROUP BY auth_user_id)
|
217
|
+
AS last_seen
|
218
|
+
ON fau.id = last_seen.auth_user_id
|
219
|
+
#{'WHERE ' + aggregate_query}
|
220
|
+
#{'AND ' + user_search_query if user_search_query.present?}
|
221
|
+
ORDER BY last_login_at #{sort_dir}")
|
222
|
+
.paginate(:page=>page, :per_page=>per_page)
|
223
|
+
else
|
224
|
+
Fortifier::AuthUser.where(aggregate_query)
|
225
|
+
.where(user_search_query)
|
226
|
+
.order("#{sort_col} #{sort_dir}")
|
227
|
+
.paginate(:page=>page, :per_page=>per_page)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
private
|
232
|
+
|
233
|
+
def create_unique_uuid
|
234
|
+
self.uuid = loop do
|
235
|
+
uuid = SecureRandom.uuid
|
236
|
+
break uuid unless Fortifier::AuthUser.where(:uuid=>uuid).exists?
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def set_consecutive_failed_login_count
|
241
|
+
self.consecutive_failed_logins = 0
|
242
|
+
end
|
243
|
+
|
244
|
+
def unique?(type, value)
|
245
|
+
case type
|
246
|
+
when :login then active_relation = AuthUser.where("login = ?", value)
|
247
|
+
when :email then active_relation = AuthUser.where("email = ?", value)
|
248
|
+
end
|
249
|
+
|
250
|
+
matching_auth_users = active_relation.to_a # converted to an array so the AuthUser isn't deleted from the db
|
251
|
+
matching_auth_users.delete(self) # don't check for uniqueness against self
|
252
|
+
matching_auth_users.blank?
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
end
|
@@ -0,0 +1,356 @@
|
|
1
|
+
# TODO: (DK) DCI!
|
2
|
+
module Fortifier
|
3
|
+
|
4
|
+
class AuthUserApi
|
5
|
+
|
6
|
+
def authenticate(login, pw, user_agent="IE", request_ip="127.0.0.1", account={})
|
7
|
+
results = AuthenticationSteps.stepper(auth_type: "standard_auth",
|
8
|
+
login: login,
|
9
|
+
secret: pw,
|
10
|
+
user_agent: user_agent,
|
11
|
+
remote_addr: request_ip,
|
12
|
+
account: account)
|
13
|
+
auth_user = results[:auth_user]
|
14
|
+
uuid = auth_user ? auth_user.uuid : nil
|
15
|
+
failed_logins = auth_user ? auth_user.consecutive_failed_logins : nil
|
16
|
+
error_msg = results[:auth_msg]
|
17
|
+
|
18
|
+
if failed_logins==0 && !error_msg #successful authentication
|
19
|
+
{ uuid: uuid,
|
20
|
+
status: true }
|
21
|
+
else
|
22
|
+
{ uuid: uuid,
|
23
|
+
status: false,
|
24
|
+
errors: [error_msg],
|
25
|
+
consecutive_failed_logins: failed_logins }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def authenticate_uuid(uuid, pw, user_agent="IE", request_ip="127.0.0.1", account={})
|
30
|
+
login = AuthUser.where(uuid: uuid).pluck(:login).first
|
31
|
+
authenticate(login, pw, user_agent, request_ip, account)
|
32
|
+
end
|
33
|
+
|
34
|
+
def authenticate_batch_sso(account_uuid, token, user_agent="IE", request_ip="127.0.0.1")
|
35
|
+
results = Fortifier::AuthenticationSteps.stepper(auth_type: "batch_sso_auth",
|
36
|
+
account_uuid: account_uuid,
|
37
|
+
token: token,
|
38
|
+
user_agent: user_agent,
|
39
|
+
remote_addr: request_ip)
|
40
|
+
auth_user = results[:auth_user]
|
41
|
+
|
42
|
+
if auth_user
|
43
|
+
{ uuid: results[:auth_user].uuid,
|
44
|
+
status: true }
|
45
|
+
else
|
46
|
+
{ status: false,
|
47
|
+
errors: [results[:auth_msg]] }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def authenticate_on_demand_sso(login_token, user_agent="IE", request_ip="127.0.0.1")
|
52
|
+
results = Fortifier::AuthenticationSteps.stepper(auth_type: "on_demand_sso_auth",
|
53
|
+
token: login_token,
|
54
|
+
user_agent: user_agent,
|
55
|
+
remote_addr: request_ip)
|
56
|
+
auth_user = results[:auth_user]
|
57
|
+
|
58
|
+
if auth_user
|
59
|
+
{ uuid: results[:auth_user].uuid,
|
60
|
+
status: true }
|
61
|
+
else
|
62
|
+
{ status: false,
|
63
|
+
errors: [results[:auth_msg]] }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def validate(params)
|
68
|
+
uuid,login,email,note = params[:uuid],params[:login],params[:email],params[:note]
|
69
|
+
enc_type = params[:sso_user].present? ? Secret::SSO_TOKEN : nil
|
70
|
+
validation_type = params[:validation_type]
|
71
|
+
|
72
|
+
auth_user = (validation_type=='create') ? AuthUser.new() : AuthUser.where("uuid = ?", uuid).first
|
73
|
+
|
74
|
+
if params[:sso_user]
|
75
|
+
secret, secret_confirmation, enc_type = Secret.make_sso_token, secret, Secret::SSO_TOKEN
|
76
|
+
else
|
77
|
+
secret, secret_confirmation, enc_type = params[:password], params[:password_confirmation], nil
|
78
|
+
end
|
79
|
+
|
80
|
+
auth_user.assign_attributes(login:login, email:email, note:note)
|
81
|
+
new_secret = Secret.new(auth_user: auth_user,
|
82
|
+
secret: secret,
|
83
|
+
secret_confirmation: secret_confirmation,
|
84
|
+
enc_type: enc_type)
|
85
|
+
auth_user.valid?
|
86
|
+
new_secret.valid?
|
87
|
+
|
88
|
+
if validation_type=='update' && (secret.blank? && secret_confirmation.blank?)
|
89
|
+
secret_errors = []
|
90
|
+
else
|
91
|
+
secret_errors = new_secret.errors.full_messages
|
92
|
+
end
|
93
|
+
|
94
|
+
errors = auth_user.errors.full_messages
|
95
|
+
secret_errors = secret_errors
|
96
|
+
|
97
|
+
validation_status = (errors.blank? && secret_errors.blank?) ? true : false
|
98
|
+
|
99
|
+
{ status: validation_status, errors: errors, secret_errors: secret_errors }
|
100
|
+
end
|
101
|
+
|
102
|
+
def batch_update(user_info)
|
103
|
+
updater = BatchUpdater.new(user_info)
|
104
|
+
updater.perform_updates
|
105
|
+
updater.user_status
|
106
|
+
end
|
107
|
+
|
108
|
+
def create(params)
|
109
|
+
auth_user = AuthUser.new()
|
110
|
+
auth_user.login = params[:login]
|
111
|
+
auth_user.email = params[:email]
|
112
|
+
auth_user.name = params[:name]
|
113
|
+
auth_user.note = params[:note]
|
114
|
+
auth_user.app_uuids = params[:app_uuids]
|
115
|
+
auth_user.account_uuids = params[:account_uuids]
|
116
|
+
auth_user.search_keywords = params[:search_keywords]
|
117
|
+
|
118
|
+
sso_user = params[:sso_user]
|
119
|
+
skip_validation = params[:skip_validation]
|
120
|
+
|
121
|
+
secret = sso_user ? Secret.make_sso_token : params[:password]
|
122
|
+
secret_confirmation = sso_user ? secret : params[:password_confirmation]
|
123
|
+
enc_type = sso_user ? Secret::SSO_TOKEN : nil
|
124
|
+
|
125
|
+
new_secret = Secret.new(secret_value: (params[:crypted_password] if skip_validation),
|
126
|
+
salt: (params[:salt] if skip_validation),
|
127
|
+
secret: (secret if !skip_validation),
|
128
|
+
secret_confirmation: (secret_confirmation if !skip_validation),
|
129
|
+
enc_type: (skip_validation ? Secret::SHA : enc_type),
|
130
|
+
skip_validation: (true if skip_validation))
|
131
|
+
|
132
|
+
valid_auth_user = auth_user.valid?
|
133
|
+
valid_secret = new_secret.valid?
|
134
|
+
|
135
|
+
if valid_auth_user && valid_secret
|
136
|
+
auth_user.save
|
137
|
+
auth_user.secrets << new_secret
|
138
|
+
{ uuid: auth_user.uuid,
|
139
|
+
token: (auth_user.current_secret.secret_value if sso_user),
|
140
|
+
status: true }
|
141
|
+
else
|
142
|
+
{ status: false,
|
143
|
+
errors: auth_user.errors.full_messages,
|
144
|
+
secret_errors: new_secret.errors.full_messages }
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def update(params)
|
149
|
+
# TODO: (DK) change user pw by way of email reset rather than admin reset?
|
150
|
+
# could clean some of this code up
|
151
|
+
auth_user = Fortifier::AuthUser.where("uuid = ?", params[:uuid]).first
|
152
|
+
return {status: false, errors: [:auth_user_not_found]} if auth_user.blank?
|
153
|
+
|
154
|
+
auth_user.login = params[:login] if params[:login]
|
155
|
+
auth_user.email = params[:email] if params[:email]
|
156
|
+
auth_user.name = params[:name] if params[:name]
|
157
|
+
auth_user.note = params[:note] if params[:note]
|
158
|
+
sso_user = params[:sso_user]
|
159
|
+
|
160
|
+
if params[:password] || sso_user
|
161
|
+
secret = sso_user ? Secret.make_sso_token : params[:password]
|
162
|
+
secret_confirmation = sso_user ? secret : params[:password_confirmation]
|
163
|
+
enc_type = sso_user ? Secret::SSO_TOKEN : nil
|
164
|
+
end
|
165
|
+
|
166
|
+
if secret && secret_confirmation
|
167
|
+
new_secret = Secret.new(secret: secret,
|
168
|
+
secret_confirmation: secret_confirmation)
|
169
|
+
end
|
170
|
+
|
171
|
+
valid_auth_user = auth_user.valid?
|
172
|
+
valid_secret = new_secret ? new_secret.valid? : true
|
173
|
+
|
174
|
+
if valid_auth_user && valid_secret
|
175
|
+
auth_user.save
|
176
|
+
auth_user.secrets << new_secret if new_secret
|
177
|
+
{ uuid: auth_user.uuid,
|
178
|
+
token: (auth_user.current_secret.secret_value if sso_user),
|
179
|
+
status: true }
|
180
|
+
else
|
181
|
+
{ status: false,
|
182
|
+
errors: auth_user.errors.full_messages,
|
183
|
+
secret_errors: (new_secret.errors.full_messages if new_secret) }
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def change_password(params)
|
188
|
+
return {status: false, errors: [:uuid_not_provided]} if params[:uuid].blank?
|
189
|
+
|
190
|
+
pw = params[:password]
|
191
|
+
pwc = params[:password_confirmation]
|
192
|
+
auth_user = Fortifier::AuthUser.where("uuid = ?", params[:uuid]).first
|
193
|
+
correct_current_pw = auth_user.authenticated?(params[:current]).present? unless auth_user.blank?
|
194
|
+
|
195
|
+
return {status: false, errors: [:bad_current_password]} if !correct_current_pw
|
196
|
+
return {status: false, errors: [:auth_user_not_found]} if !auth_user
|
197
|
+
|
198
|
+
new_secret = Secret.new(auth_user: auth_user, secret: pw, secret_confirmation: pwc)
|
199
|
+
|
200
|
+
if new_secret.valid?
|
201
|
+
new_secret.save
|
202
|
+
{ status: true }
|
203
|
+
else
|
204
|
+
{ status: false,
|
205
|
+
errors: new_secret.errors.full_messages }
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def reset_password(params)
|
210
|
+
return {status: false, errors: [:uuid_not_provided]} if params[:uuid].blank?
|
211
|
+
|
212
|
+
pw = params[:password]
|
213
|
+
pwc = params[:password_confirmation]
|
214
|
+
auth_user = Fortifier::AuthUser.where("uuid = ?", params[:uuid]).first
|
215
|
+
|
216
|
+
return {status: false, errors: [:auth_user_not_found]} if !auth_user
|
217
|
+
|
218
|
+
new_secret = Secret.new(auth_user: auth_user, secret: pw, secret_confirmation: pwc)
|
219
|
+
|
220
|
+
if new_secret.valid?
|
221
|
+
new_secret.save
|
222
|
+
{ status: true }
|
223
|
+
else
|
224
|
+
{ status: false,
|
225
|
+
errors: new_secret.errors.full_messages }
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def create_password_reset_token(email)
|
230
|
+
return {status: false, errors: [:email_not_provided]} if email.blank?
|
231
|
+
|
232
|
+
auth_user = Fortifier::AuthUser.where("email = ?", email).first
|
233
|
+
return {status: false, errors: [:auth_user_not_found]} if !auth_user
|
234
|
+
|
235
|
+
pw = Secret.make_token
|
236
|
+
|
237
|
+
new_secret = Secret.new(auth_user: auth_user, secret: pw, secret_confirmation: pw, reset_token: true)
|
238
|
+
|
239
|
+
if new_secret.valid?
|
240
|
+
new_secret.save
|
241
|
+
{ status: true, token: pw }
|
242
|
+
else
|
243
|
+
{ status: false,
|
244
|
+
errors: new_secret.errors.full_messages }
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def link(params)
|
249
|
+
return {status: false, errors: [:uuid_not_provided]} if params[:uuid].blank?
|
250
|
+
|
251
|
+
app_uuids = params[:app_uuids] || []
|
252
|
+
account_uuids = params[:account_uuids] || []
|
253
|
+
search_keywords = params[:search_keywords] || []
|
254
|
+
return {status: false, errors: [:app_and_account_uuids_not_provided]} if (app_uuids.blank? && account_uuids.blank?)
|
255
|
+
|
256
|
+
auth_user = AuthUser.where(uuid: params[:uuid]).first
|
257
|
+
return {status: false, errors: [:auth_user_not_found]} if auth_user.blank?
|
258
|
+
|
259
|
+
auth_user.app_uuids.concat(app_uuids)
|
260
|
+
auth_user.account_uuids.concat(account_uuids)
|
261
|
+
auth_user.search_keywords.concat(search_keywords)
|
262
|
+
result = auth_user.save
|
263
|
+
|
264
|
+
result ? {status: true} : {status: false, errors: (result.errors.full_messages if result)}
|
265
|
+
end
|
266
|
+
|
267
|
+
def unlink(params)
|
268
|
+
return {status: false, errors: [:uuid_not_provided]} if params[:uuid].blank?
|
269
|
+
|
270
|
+
app_uuids = params[:app_uuids] || []
|
271
|
+
account_uuids = params[:account_uuids] || []
|
272
|
+
search_keywords = params[:search_keywords] || []
|
273
|
+
return {status: false, errors: [:app_and_account_uuids_not_provided]} if (app_uuids.blank? && account_uuids.blank?)
|
274
|
+
|
275
|
+
auth_user = AuthUser.where(uuid: params[:uuid]).first
|
276
|
+
return {status: false, errors: [:auth_user_not_found]} if auth_user.blank?
|
277
|
+
|
278
|
+
auth_user.app_uuids - app_uuids
|
279
|
+
auth_user.account_uuids - account_uuids
|
280
|
+
auth_user.search_keywords - search_keywords
|
281
|
+
result = auth_user.save
|
282
|
+
|
283
|
+
result ? {status: true} : {status: false, errors: (result.errors.full_messages if result)}
|
284
|
+
end
|
285
|
+
|
286
|
+
def find_auth_user(field, param)
|
287
|
+
field = field.to_s || ''
|
288
|
+
|
289
|
+
case field
|
290
|
+
when 'uuid' then auth_user = AuthUser.where("uuid = ?", param).first
|
291
|
+
when 'login', 'email' then auth_user = AuthUser.where("login = ? OR email = ?", param, param).first
|
292
|
+
when 'token' then auth_user = AuthUser.joins(:secrets).where("secret_value = ? AND (expired IS NULL OR expired = false)", param).first
|
293
|
+
end
|
294
|
+
|
295
|
+
auth_user.blank? ? {} : auth_user.public_attribute_hash
|
296
|
+
end
|
297
|
+
|
298
|
+
def find_auth_user_emails(array_of_uuids)
|
299
|
+
return {errors: [:no_uuids_provided]} if array_of_uuids.blank?
|
300
|
+
au_uuids_and_emails = {}
|
301
|
+
|
302
|
+
AuthUser.where(uuid: array_of_uuids).inject(au_uuids_and_emails) do |hash, auth_user|
|
303
|
+
hash[auth_user.uuid] = auth_user.email
|
304
|
+
hash
|
305
|
+
end
|
306
|
+
|
307
|
+
{uuids_with_emails: au_uuids_and_emails}
|
308
|
+
end
|
309
|
+
|
310
|
+
def search_for_auth_users(params)
|
311
|
+
act_rel = Fortifier::AuthUser.paged_and_sorted_search(params)
|
312
|
+
hash = {
|
313
|
+
# the following are will_paginate methods
|
314
|
+
total_pages: act_rel.total_pages,
|
315
|
+
current_page: act_rel.current_page,
|
316
|
+
per_page: act_rel.per_page,
|
317
|
+
total_entries: act_rel.total_entries
|
318
|
+
}
|
319
|
+
hash[:auth_users] = act_rel.map do |au|
|
320
|
+
au.public_attribute_hash
|
321
|
+
end
|
322
|
+
|
323
|
+
hash
|
324
|
+
end
|
325
|
+
|
326
|
+
def auth_users_by_uuids(uuids)
|
327
|
+
return [] unless uuids.present?
|
328
|
+
|
329
|
+
formatted_uuids = uuids.map { |uuid| "'#{uuid}'" }.join(',')
|
330
|
+
|
331
|
+
sql = <<-SQL
|
332
|
+
SELECT
|
333
|
+
fortifier_auth_users.*,
|
334
|
+
latest_secrets.last_password_change
|
335
|
+
FROM
|
336
|
+
`fortifier_auth_users` LEFT OUTER JOIN (
|
337
|
+
SELECT
|
338
|
+
auth_user_id,
|
339
|
+
MAX(created_at) AS last_password_change
|
340
|
+
FROM
|
341
|
+
fortifier_secrets
|
342
|
+
GROUP BY
|
343
|
+
auth_user_id) latest_secrets
|
344
|
+
ON
|
345
|
+
fortifier_auth_users.id = latest_secrets.auth_user_id
|
346
|
+
WHERE
|
347
|
+
`fortifier_auth_users`.`uuid` IN (#{formatted_uuids})
|
348
|
+
SQL
|
349
|
+
result = ActiveRecord::Base.connection.execute sql
|
350
|
+
auth_users = [].tap do | arr |
|
351
|
+
result.each(as: :hash) { | row | arr << row }
|
352
|
+
end
|
353
|
+
{ auth_users: auth_users }
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Fortifier
|
2
|
+
module Authentication
|
3
|
+
LOGIN_REGEX = /\A\w[\w\.\-_@']+\z/ # ASCII, strict
|
4
|
+
|
5
|
+
NAME_REGEX = /\A[^[:cntrl:]\\<>\/&]*\z/ # Unicode, permissive
|
6
|
+
|
7
|
+
EMAIL_NAME_REGEX = '[\w\.%\+\-\']+'.freeze
|
8
|
+
DOMAIN_HEAD_REGEX = '(?:[A-Z0-9\-]+\.)+'.freeze
|
9
|
+
DOMAIN_TLD_REGEX = '(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|jobs|museum)'.freeze
|
10
|
+
EMAIL_REGEX = /\A#{EMAIL_NAME_REGEX}@#{DOMAIN_HEAD_REGEX}#{DOMAIN_TLD_REGEX}\z/i
|
11
|
+
|
12
|
+
# Custom regex' (IdS)
|
13
|
+
SECRET_REGEX = /\A(?=.*\d)(?=.*([a-z]|[A-Z]))([\x20-\x7E]){10,40}\z/
|
14
|
+
|
15
|
+
IP_ADDRESS_REGEX = /\A\d{1,3}(\.\d{1,3}){3}\z/
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Fortifier
|
2
|
+
class AuthenticationSteps
|
3
|
+
STEPS = [
|
4
|
+
"InitializeAuthAttempt",
|
5
|
+
"CheckForBlockedUser",
|
6
|
+
"CheckForBlockedIp",
|
7
|
+
"CheckForWhitelistedIp",
|
8
|
+
"CheckForUsExternalIp",
|
9
|
+
]
|
10
|
+
BATCH_SSO_STEPS = [
|
11
|
+
"InitializeBatchSsoAuthAttempt",
|
12
|
+
"CheckForWhitelistedIp",
|
13
|
+
]
|
14
|
+
ON_DEMAND_SSO_STEPS = [
|
15
|
+
"InitializeOnDemandSsoAuthAttempt",
|
16
|
+
"CheckForWhitelistedIp",
|
17
|
+
]
|
18
|
+
|
19
|
+
def self.stepper(params={})
|
20
|
+
# TODO: (DK) dry up below code
|
21
|
+
|
22
|
+
case params[:auth_type]
|
23
|
+
when "standard_auth"
|
24
|
+
STEPS.each do |step|
|
25
|
+
step_class = ("Fortifier::AuthSteps::" + step).constantize
|
26
|
+
next if step_class.respond_to?(:skip_step?) && step_class.skip_step?(params)
|
27
|
+
params = step_class.invoke(params)
|
28
|
+
end
|
29
|
+
when "batch_sso_auth"
|
30
|
+
BATCH_SSO_STEPS.each do |step|
|
31
|
+
step_class = ("Fortifier::AuthSteps::" + step).constantize
|
32
|
+
next if step_class.respond_to?(:skip_step?) && step_class.skip_step?(params)
|
33
|
+
params = step_class.invoke(params)
|
34
|
+
end
|
35
|
+
when "on_demand_sso_auth"
|
36
|
+
ON_DEMAND_SSO_STEPS.each do |step|
|
37
|
+
step_class = ("Fortifier::AuthSteps::" + step).constantize
|
38
|
+
next if step_class.respond_to?(:skip_step?) && step_class.skip_step?(params)
|
39
|
+
params = step_class.invoke(params)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
params
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|