fortifier 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
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