fortifier 0.1.4

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 (51) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +29 -0
  5. data/app/controllers/fortifier/application_controller.rb +17 -0
  6. data/app/controllers/fortifier/auth_users_controller.rb +107 -0
  7. data/app/helpers/fortifier/application_helper.rb +4 -0
  8. data/app/helpers/fortifier/auth_users_helper.rb +4 -0
  9. data/app/helpers/fortifier/date_helper.rb +46 -0
  10. data/app/helpers/fortifier/passwords_helper.rb +4 -0
  11. data/app/mailers/fortifier/notifier_mailer.rb +66 -0
  12. data/app/models/fortifier/auth_log.rb +18 -0
  13. data/app/models/fortifier/auth_rule.rb +11 -0
  14. data/app/models/fortifier/auth_steps/check_for_blocked_ip.rb +22 -0
  15. data/app/models/fortifier/auth_steps/check_for_blocked_user.rb +16 -0
  16. data/app/models/fortifier/auth_steps/check_for_us_external_ip.rb +14 -0
  17. data/app/models/fortifier/auth_steps/check_for_whitelisted_ip.rb +38 -0
  18. data/app/models/fortifier/auth_steps/initialize_auth_attempt.rb +19 -0
  19. data/app/models/fortifier/auth_steps/initialize_batch_sso_auth_attempt.rb +18 -0
  20. data/app/models/fortifier/auth_steps/initialize_on_demand_sso_auth_attempt.rb +18 -0
  21. data/app/models/fortifier/auth_steps/messaging.rb +16 -0
  22. data/app/models/fortifier/auth_user.rb +256 -0
  23. data/app/models/fortifier/auth_user_api.rb +356 -0
  24. data/app/models/fortifier/auth_users_auth_rule.rb +8 -0
  25. data/app/models/fortifier/authentication.rb +17 -0
  26. data/app/models/fortifier/authentication_steps.rb +46 -0
  27. data/app/models/fortifier/batch_updater.rb +148 -0
  28. data/app/models/fortifier/max_mind.rb +64 -0
  29. data/app/models/fortifier/max_mind_reference_ip.rb +5 -0
  30. data/app/models/fortifier/rufus/rufus_password_expiration.rb +23 -0
  31. data/app/models/fortifier/secret.rb +189 -0
  32. data/app/views/fortifier/notifier_mailer/account_ip_blocked.html.erb +30 -0
  33. data/app/views/fortifier/notifier_mailer/account_ip_blocked_providigm.html.erb +20 -0
  34. data/app/views/fortifier/notifier_mailer/exception_notification.html.erb +88 -0
  35. data/app/views/fortifier/notifier_mailer/foreign_access.html.erb +22 -0
  36. data/app/views/fortifier/notifier_mailer/password_expiration.html.erb +28 -0
  37. data/app/views/fortifier/notifier_mailer/password_reset_token.html.erb +28 -0
  38. data/app/views/fortifier/notifier_mailer/task_exception.html.erb +18 -0
  39. data/app/views/layouts/fortifier/application.html.erb +14 -0
  40. data/config/Initializers/bcrypt.rb +1 -0
  41. data/config/Initializers/ipaddr.rb +1 -0
  42. data/config/database.yml +18 -0
  43. data/config/routes.rb +27 -0
  44. data/db/migrate/20130916194012_create_fortifier_tables.rb +63 -0
  45. data/db/migrate/20140415210139_add_auth_user_search_keywords_field.rb +9 -0
  46. data/db/migration_scripts/20140403_temp_whitelist_migration.rb +5 -0
  47. data/lib/fortifier/engine.rb +40 -0
  48. data/lib/fortifier/version.rb +3 -0
  49. data/lib/fortifier.rb +4 -0
  50. data/lib/tasks/fortifier_tasks.rake +4 -0
  51. 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,8 @@
1
+ module Fortifier
2
+ class AuthUsersAuthRule < ActiveRecord::Base
3
+ # attr_accessible :auth_user_id, :auth_rule_id
4
+
5
+ belongs_to :auth_users
6
+ belongs_to :auth_rules
7
+ end
8
+ 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