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,148 @@
1
+ module Fortifier
2
+
3
+ class BatchUpdater
4
+
5
+ attr_reader :users_to_update, :application_uuid, :account_uuid, :user_status
6
+
7
+ def initialize(users_to_upd)
8
+ @account_uuid = users_to_upd['account_uuid']
9
+ @application_uuid = users_to_upd['application_uuid']
10
+ @users_to_update = users_to_upd['users']
11
+ @user_status = Hash.new { | h, k | h[k] = { status: :new, messages: [] }}
12
+ end
13
+
14
+ def perform_updates
15
+ identify_deltas # Determine what we need to do
16
+ users_with_unidentified_status = []
17
+
18
+ user_status.each_pair do | login, status_info |
19
+ case status_info[:status]
20
+ when :new
21
+ add_new_user(login, users_to_update[login], status_info)
22
+ when :delete
23
+ unlink_user(login, status_info)
24
+ when :update
25
+ update_user(login, users_to_update[login], status_info)
26
+ when :error
27
+ # Not doing anything with users that have an error
28
+ else
29
+ # Need to log and email users in this state
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def set_error_info(status_info, errors)
37
+ status_info[:status] = :error
38
+ status_info[:messages] += errors
39
+ end
40
+
41
+ def add_new_user(login, user_info, status_info)
42
+ auth_user = AuthUser.where(login: login).includes(:secrets).references(:secrets).first
43
+ auth_user ||= AuthUser.new
44
+ auth_user.login = login
45
+ auth_user.email = user_info['email']
46
+ auth_user.name = user_info['name']
47
+ auth_user.app_uuids = [ application_uuid ]
48
+ auth_user.account_uuids = [ account_uuid ]
49
+ auth_user.secrets << Secret.new(secret: user_info['token'],
50
+ secret_confirmation: user_info['token'],
51
+ enc_type: Secret::SSO_TOKEN)
52
+ if auth_user.valid?
53
+ auth_user.save
54
+ status_info[:uuid] = auth_user.uuid
55
+ else
56
+ set_error_info(status_info, auth_user.errors.full_messages)
57
+ end
58
+ end
59
+
60
+ def unlink_user(login, status_info)
61
+ auth_user = AuthUser.where(login: login).includes(:secrets).references(:secrets).first
62
+ if auth_user.present?
63
+ # In the case of batch SSO, the way we 'unlink' a user is to remove
64
+ # their secret (token). Removal of their secret disallows the possibility
65
+ # of begin authenticated. Leaving the auth user is necessary to allow
66
+ # app-user ressurection.
67
+ auth_user.secrets.destroy_all
68
+ status_info[:uuid] = auth_user.uuid
69
+ else
70
+ set_error_info(status_info, [ "Unable to find user to unlink: #{login}" ])
71
+ end
72
+ end
73
+
74
+ def update_user(login, user_info, status_info)
75
+ auth_user = AuthUser.where(login: login).includes(:secrets).references(:secrets).first
76
+ if auth_user.present?
77
+ auth_user.email = user_info['email']
78
+ auth_user.name = user_info['name']
79
+ status_info[:uuid] = auth_user.uuid
80
+ secret = auth_user.secrets.first
81
+ if secret.present?
82
+ secret.update_attributes(secret: user_info['token'], secret_confirmation: user_info['token'])
83
+ else
84
+ auth_user.secrets << Secret.new(secret: user_info['token'],
85
+ secret_confirmation: user_info['token'],
86
+ enc_type: Secret::SSO_TOKEN)
87
+ end
88
+ if auth_user.valid?
89
+ auth_user.save
90
+ else
91
+ set_error_info(status_info, auth_user.errors.full_messages)
92
+ end
93
+ else
94
+ set_error_info(status_info, [ "Unable to find user to update: #{login}" ])
95
+ end
96
+ end
97
+
98
+ def identify_deltas
99
+ account_auth_user_logins = AuthUser.joins(:secrets).where("FIND_IN_SET(?, account_uuids_csv) > 0", account_uuid).pluck(:login).uniq
100
+
101
+ identify_existing_users
102
+ identify_users_to_be_deleted account_auth_user_logins
103
+ identify_users_to_be_added account_auth_user_logins
104
+
105
+ user_status
106
+ end
107
+
108
+ def identify_existing_users
109
+ # Find the auth users that currently exist
110
+ existing_auth_users = AuthUser.where("login in (?)", logins)
111
+ existing_auth_users.reduce(user_status) do | u_status, auth_user |
112
+ u_status[auth_user.login][:status] = :update
113
+ unless auth_user.account_uuids.include?(account_uuid)
114
+ u_status[auth_user.login][:status] = :error
115
+ u_status[auth_user.login][:messages] << "Login already used"
116
+ end
117
+ u_status
118
+ end
119
+ end
120
+
121
+ def identify_users_to_be_deleted(account_auth_user_logins)
122
+ auth_users_to_delete = (account_auth_user_logins - logins)
123
+ auth_users_to_delete.reduce(user_status) do | u_status, login_to_delete |
124
+ u_status[login_to_delete][:status] = :delete
125
+ u_status
126
+ end
127
+ account_auth_user_logins
128
+ end
129
+
130
+ def identify_users_to_be_added(account_auth_user_logins)
131
+ # Find the auth users that need to be added
132
+ auth_users_to_add = (logins - account_auth_user_logins)
133
+ auth_users_to_add.reduce(user_status) do | u_status, login_to_add |
134
+ unless u_status.has_key?(login_to_add)
135
+ u_status[login_to_add][:status] = :new
136
+ end
137
+ u_status
138
+ end
139
+
140
+ end
141
+
142
+ def logins
143
+ users_to_update.keys
144
+ end
145
+
146
+ end
147
+
148
+ end
@@ -0,0 +1,64 @@
1
+ module Fortifier
2
+ class MaxMind
3
+
4
+ def self.valid_ip?(auth_log)
5
+ return true unless Rails.env=="production"
6
+
7
+ ip_address = auth_log[:remote_addr]
8
+
9
+ return true if local_ip?(ip_address)
10
+ return true if previously_validated_ip?(ip_address)
11
+ return true if auth_log.auth_user.try(:matching_whitelist_ip?,ip_address)
12
+ return false if previously_rejected_ip?(ip_address)
13
+
14
+ fields = [:country_code, :error]
15
+ options = { :license => "KqA72c39qtQs", :ip => ip_address }
16
+
17
+ uri = URI::HTTP.build(:scheme => 'http',
18
+ :host => 'geoip.maxmind.com',
19
+ :path => '/a',
20
+ :query => URI.encode_www_form(:l => options[:license],
21
+ :i => ip_address))
22
+ begin
23
+ response = Net::HTTP.get_response(uri)
24
+
25
+ raise Exception.new("MaxMind Request failed with status #{response.code}") unless response.is_a?(Net::HTTPSuccess)
26
+
27
+ info = Hash[fields.zip(response.body.encode('utf-8', 'iso-8859-1').parse_csv)]
28
+ country = info[:country_code]
29
+
30
+ raise Exception.new("MaxMind Request failed - IP not found: #{ip_address}") if info[:error]=="IP_NOT_FOUND"
31
+
32
+ if country=="CA" || country=="US" || country=="UM" || country=="PR" || country==nil
33
+ MaxMindReferenceIp.create(ip_address: ip_address, country: country, allowed: true)
34
+ return true
35
+ else
36
+ NotifierMailer.foreign_access(auth_log.auth_user, auth_log).deliver
37
+ MaxMindReferenceIp.create(ip_address: ip_address, country: country, allowed: false)
38
+ return false
39
+ end
40
+ rescue Exception=>e
41
+ NotifierMailer.task_exception(e).deliver
42
+ return true
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def self.local_ip?(ip)
49
+ ip_regex = /(^127\.)|(^192\.168\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1)|(^0:0:0:0:0:0:0:1)$/
50
+ return true if ip_regex.match(ip)
51
+ end
52
+
53
+ def self.previously_validated_ip?(ip)
54
+ match = MaxMindReferenceIp.where(ip_address: ip).where("updated_at > ? and allowed = ?", Date.today - 6.months, true).first
55
+ return match.present?
56
+ end
57
+
58
+ def self.previously_rejected_ip?(ip)
59
+ match = MaxMindReferenceIp.where(ip_address: ip).where("updated_at > ? and allowed = ?", Date.today - 6.months, false).first
60
+ return match.present?
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,5 @@
1
+ module Fortifier
2
+ class MaxMindReferenceIp < ActiveRecord::Base
3
+
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ # .---------------- minute (0 - 59)
2
+ # | .------------- hour (0 - 23)
3
+ # | | .---------- day of month (1 - 31)
4
+ # | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
5
+ # | | | | .----- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
6
+ # | | | | |
7
+ # * * * * * command to be executed
8
+
9
+ module Fortifier
10
+ class RufusNotificationDigest
11
+
12
+ def self.schedule(scheduler)
13
+
14
+ # DAILY - every day at half past midnight
15
+ scheduler.cron '30 0 * * * America/Denver', :blocking => true do
16
+ ActiveRecord::Base.establish_connection
17
+ ScheduledJob.log("Daily Password Expiration") do
18
+ Password.evaluate_for_expiration
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,189 @@
1
+ module Fortifier
2
+ class Secret < ActiveRecord::Base
3
+
4
+ EXPIRATION_PERIOD = 90 # days
5
+ RESTRICTION_PERIOD = 1 # year
6
+ GRACE_PERIOD = EXPIRATION_PERIOD - 8 # days
7
+ SHA = "SHA"
8
+ BCRYPT = "BCRYPT"
9
+ SSO_TOKEN = "SSO_TOKEN" # i.e. no encryption
10
+ RESET_TOKEN = 'RESET_TOKEN'
11
+
12
+ attr_accessor :secret, :secret_confirmation
13
+ attr_accessor :skip_validation
14
+ attr_accessor :reset_token
15
+
16
+ belongs_to :auth_user
17
+
18
+ scope :active, -> { where(expired: 0) }
19
+ scope :past_expiration, -> do
20
+ active.joins(:user, :account)
21
+ .where("accounts.id in (select accounts.id from accounts left outer join configurations c on c.account_id = accounts.id
22
+ where c.add_on_id=#{AddOn::PW_ENHANCEMENTS_ADD_ON_ID} and c.kee='enabled' and c.value='true')")
23
+ .where("secrets.created_at < ?", Time.now - EXPIRATION_PERIOD.days)
24
+ end
25
+ scope :week_to_expiration, -> do
26
+ active.select("users.*, secrets.*")
27
+ .joins(:user, :account)
28
+ .where("accounts.id in (select accounts.id from accounts left outer join configurations c on c.account_id = accounts.id
29
+ where c.add_on_id=#{AddOn::PW_ENHANCEMENTS_ADD_ON_ID} and c.kee='enabled' and c.value='true')")
30
+ .where("DATEDIFF(secrets.created_at, '#{Time.now - EXPIRATION_PERIOD.days}') = 7")
31
+ end
32
+
33
+ # NOTE: These custom validations are used so that symbols/codes
34
+ # can be returned to applications, rather than explicit text.
35
+ # Fortifier provides the symbols and the apps are responsible
36
+ # for handling error messaging.
37
+ validate :secret_is_present,
38
+ :unless => :skip_validation
39
+ validate :secret_confirmation_is_present,
40
+ :unless => :skip_validation
41
+ validate :secret_matches_confirmation,
42
+ :unless => :skip_validation
43
+ validate :secret_matches_regex,
44
+ :unless => :skip_validation
45
+ # TODO: (DK) turn back on when ready:
46
+ # validate :secret_not_used_recently,
47
+ # :unless=>:sso_auth_user?
48
+
49
+ before_save :encrypt_secret, :unless => :skip_validation
50
+ before_save :set_sso_token, :if => :sso_auth_user?
51
+ # TODO: (DK) remove when no longer needed: before_save :set_auth_user_id
52
+ before_save :expire_previous_secret
53
+
54
+ def secret_is_present
55
+ return if secret.present?
56
+ errors[:base] << :blank_password
57
+ end
58
+
59
+ def secret_confirmation_is_present
60
+ return if secret_confirmation.present?
61
+ errors[:base] << :blank_password_confirmation
62
+ end
63
+
64
+ def secret_matches_confirmation
65
+ return if secret == secret_confirmation
66
+ errors[:base] << :passwords_do_not_match
67
+ end
68
+
69
+ def secret_matches_regex
70
+ # 10 to 40 characters, one letter, one number
71
+ errors[:base] << :bad_password if secret and secret.match(Fortifier::Authentication::SECRET_REGEX).nil?
72
+ end
73
+
74
+ # TODO: (DK)
75
+ # def secret_not_used_recently
76
+ # # enhanced secret validation
77
+ # errors[:base] << :password_previously_used if matches_previous_secret?(secret)
78
+ # end
79
+
80
+ def sso_auth_user?; self.enc_type==SSO_TOKEN; end
81
+
82
+ def password_reset?; self.enc_type==RESET_TOKEN || self.reset_token; end
83
+
84
+ def skip_validation?
85
+ self.skip_validation || self.sso_auth_user? || self.password_reset?
86
+ end
87
+
88
+ def expire!; update_column(:expired, 1); end
89
+
90
+ def expired?; expired == 1; end
91
+
92
+ def enable!; expired == 0; end
93
+
94
+ def expiration_date; (created_at.to_time + EXPIRATION_PERIOD.days); end
95
+
96
+ def within_a_week_of_expiration?
97
+ t = (expiration_date.to_date - Date.today).to_i
98
+ t <= 7 && t > 0
99
+ end
100
+
101
+ def self.reset_token_unique?(token)
102
+ return false if token.blank?
103
+ Fortifier::Secret.where("enc_type='#{RESET_TOKEN}'
104
+ AND secret_value='#{token}'
105
+ AND (expired IS NULL OR expired=false)").blank?
106
+ end
107
+
108
+ def expire_previous_secret
109
+ return if (self.reset_token || auth_user.current_secret.blank?)
110
+ old_secrets = Secret.where("auth_user_id='#{auth_user.id}' AND (expired IS NULL OR expired=false)")
111
+ old_secrets.each{|s| s.expire!}
112
+ end
113
+
114
+ # This is called by the rufus job to determine whether users should be locked for not changing secrets
115
+ def self.evaluate_for_expiration
116
+ Secret.past_expiration.each do |secret|
117
+ secret.user.disable!
118
+ secret.expire!
119
+ end
120
+ Secret.week_to_expiration.each do |secret|
121
+ NotifierMailer.secret_expiration(secret, secret.user.email).deliver
122
+ end
123
+ end
124
+
125
+ def matches?(secret_string)
126
+ if enc_type == SHA
127
+ # deprecated pw hashing
128
+ secret_value == Digest::SHA1.hexdigest("--#{salt}--#{secret_string}--")
129
+ elsif enc_type == Secret::SSO_TOKEN
130
+ secret_value == secret_string
131
+ else
132
+ BCrypt::Password.new(secret_value) == secret_string
133
+ end
134
+ end
135
+
136
+ def update_encryption_method(secret_string)
137
+ # TODO: dave, test if this works: return if enc_type == (BCRYPT || SSO_TOKEN)
138
+ return if self.enc_type == Secret::BCRYPT || self.enc_type == Secret::SSO_TOKEN or secret_string.blank?
139
+ new_secret = Secret.new
140
+ new_secret.auth_user = self.auth_user
141
+ new_secret.secret_value = secret_string
142
+ new_secret.salt = nil
143
+ new_secret.enc_type = BCRYPT
144
+ new_secret.save!(validate: false)
145
+ end
146
+
147
+ def self.make_token
148
+ token=rand(36**30).to_s(36) while Secret.reset_token_unique?(token)==false
149
+ token
150
+ end
151
+
152
+ def self.make_sso_token
153
+ token=rand(36**30).to_s(36) while Secret.reset_token_unique?(token)==false
154
+ token
155
+ end
156
+
157
+ private
158
+
159
+ # before_save filter
160
+ def encrypt_secret
161
+ # TODO: (DK) make sure this works for old encryption changes and new records, etc
162
+ secret = new_record? ? self.secret : self.secret_value
163
+ if self.reset_token
164
+ self.secret_value = secret
165
+ self.enc_type = RESET_TOKEN
166
+ elsif self.enc_type==SSO_TOKEN
167
+ self.secret_value = secret
168
+ self.enc_type = SSO_TOKEN
169
+ else
170
+ self.secret_value = BCrypt::Password.create(secret)
171
+ self.enc_type = "BCRYPT"
172
+ end
173
+ end
174
+
175
+ def set_sso_token; self.secret_value = secret; end
176
+
177
+ def matches_previous_secret?(secret_string)
178
+ return false unless new_record?
179
+ return if !auth_user || auth_user.new_record? # || !Account.enhanced_secret_protection_enabled?(user.account_id) TODO: still necessary?
180
+ past_secrets = auth_user.secrets.where("id IS NOT NULL and created_at > '#{Time.now.utc - RESTRICTION_PERIOD.year}'")
181
+ past_secrets.detect { |past_secret| past_secret.matches?(secret_string) }
182
+ end
183
+
184
+ # TODO: (DK) remove when no longer needed
185
+ # def set_auth_user_id
186
+ # self.auth_user_id ||= auth_user.id
187
+ # end
188
+ end
189
+ end
@@ -0,0 +1,30 @@
1
+ <!-- TODO: (DK) make appropriate changes here to specify generic address and no specific app (including logo) -->
2
+
3
+ <html style="font-family:helvetica,arial,sans-serif; color:#888">
4
+ <head>
5
+ <title>Security Alert</title>
6
+ </head>
7
+ <body>
8
+ <div style="border-bottom:3px solid #AFAFAF;padding:0 20px 10px 20px;position:relative;height:52px;min-width:340px">
9
+ <h1 style="font-size:23px;color:#7F8084;line-height:52px;margin:0;padding:0;">abaqis Security Alert</h1>
10
+ <img style="position:absolute;right:20px;top:0" src="http://www.providigm.com/wp-content/uploads/2011/08/abaqis_logo_transparent2.png"/>
11
+ </div>
12
+ <div style="padding:0 20px;font-size:14px;position:relative;color:#404040;overflow:hidden;min-width:340px">
13
+ <div style="width:340px;padding:10px 20px 10px 0;float:left;">
14
+ <h2 style="font-size:16px;margin:2% 0 1% 0;">IP Block</h2>
15
+ <p style="line-height:18px">At <%=Time.now.in_time_zone(DISPLAY_TIME_ZONE).strftime("%m/%-d/%Y %H:%M:%S")%> MST we detected numerous failed attempts to access your account. The sudden increase in failed logins indicates the possibility that an unauthorized party is trying to gain access to your account.</p>
16
+ <p style="line-height:18px">To protect you from potentially fraudulent activity, we've placed a 10 minute block on the IP address(es) from where the login attempts originated: </p>
17
+ <p style="line-height:18px"><%= @remote_addr %></p>
18
+ <p style="line-height:18px">Access will be restored at <strong><%=(Time.now+600).in_time_zone(DISPLAY_TIME_ZONE).strftime("%m/%-d/%Y %H:%M:%S")%> MST</strong>.</p>
19
+ <p style="line-height:18px"> –– The Providigm team</p>
20
+ </div>
21
+ <p style="width:179px;float:left;font-size:12px;line-height:20px;border-left:1px solid #ddd;padding-left:20px;">
22
+ <strong>Need further assistance?</strong><br/>
23
+ Client Support<br/>
24
+ <strong>866-922-8655</strong><br/>
25
+ <a href="mailto:abaqis_support@providigm.com" style="color:#369">abaqis_support@providigm.com</a><br/>
26
+ </p>
27
+ </div>
28
+ </body>
29
+ </html>
30
+
@@ -0,0 +1,20 @@
1
+ <!-- TODO: (DK) make appropriate changes here to specify generic address and no specific app (including logo) -->
2
+
3
+ <html style="font-family:helvetica,arial,sans-serif; color:#888">
4
+ <head>
5
+ <title>Security Alert</title>
6
+ </head>
7
+ <body>
8
+ <div style="border-bottom:3px solid #AFAFAF;padding:0 20px 10px 20px;position:relative;height:52px;min-width:340px">
9
+ <h1 style="font-size:23px;color:#7F8084;line-height:52px;margin:0;padding:0;">abaqis Security Alert</h1>
10
+ <img style="position:absolute;right:20px;top:0" src="http://www.providigm.com/wp-content/uploads/2011/08/abaqis_logo_transparent2.png"/>
11
+ </div>
12
+ <div style="padding:0 20px;font-size:14px;position:relative;color:#404040;overflow:hidden;min-width:340px">
13
+ <div style="width:340px;padding:10px 20px 10px 0;float:left;">
14
+ <h2 style="font-size:16px;margin:2% 0 1% 0;">IP Block - <%= @account[:organization] || @account[:name] %></h2>
15
+ <p style="line-height:18px">At <%=Time.now.in_time_zone(DISPLAY_TIME_ZONE).strftime("%m/%-d/%Y %H:%M:%S")%> MST we blocked access to the following IP(s) for 10 minutes:</p>
16
+ <p style="line-height:18px"><%= @remote_addr %></p>
17
+ </div>
18
+ </div>
19
+ </body>
20
+ </html>
@@ -0,0 +1,88 @@
1
+ <%
2
+ user = User.find_by_id(@request.session[:user_id])
3
+ prov_user = User.where(id: @request.session[:sudoer]).first if @request.session[:sudo]
4
+ facility = Facility.find_by_id(@request.session[:facility_id])
5
+ def filter_params(params)
6
+ (params && params["password"]) ? params.merge!({"password"=>"*******"}) : params
7
+ (params && params["password_confirmation"]) ? params.merge!({"password_confirmation"=>"*******"}) : params
8
+ (params && params["current"]) ? params.merge!({"current"=>"*******"}) : params
9
+ (params && params["user"] && params["user"]["password"]) ? params["user"].merge!({"password"=>"*******"}) : params
10
+ (params && params["user"] && params["user"]["password_confirmation"]) ? params["user"].merge!({"password_confirmation"=>"*******"}) : params
11
+ end
12
+ %>
13
+ <img src="http://<%= @request.env["HTTP_HOST"] %>/images/kablooie.gif"/>
14
+
15
+ <h1>Transcendent Kablooie!!!</h1>
16
+ <p><strong>Something just blew up at:</strong> http://<%= @request.env["HTTP_HOST"] %></p>
17
+
18
+ <table border="0" cellpadding="5" cellspacing="0">
19
+ <tr>
20
+ <td>Facility:</td>
21
+ <td><%= "(#{facility.id}) #{facility.name}" if facility %></td>
22
+ </tr>
23
+ <tr>
24
+ <td>Account (:</td>
25
+ <td><%= "(#{user.account.id}) #{user.account.organization}" if user%></td>
26
+ </tr>
27
+ <tr>
28
+ <td>User:</td>
29
+ <td><%= "(#{user.id}) #{user.login}" if user%><%= " - via (#{prov_user.id}) #{prov_user.login}" if prov_user %></td>
30
+ </tr>
31
+ <tr>
32
+ <td>Exception Message:</td>
33
+ <td><%= @exception.message if @exception%></td>
34
+ </tr>
35
+ <tr>
36
+ <td>Controller:</td>
37
+ <td><%= @request.params["controller"].to_s if controller%></td>
38
+ </tr>
39
+ <tr>
40
+ <td>Action:</td>
41
+ <td><%= @request.params["action"].to_s.capitalize if controller %></td>
42
+ </tr>
43
+ <tr>
44
+ <td>Params:</td>
45
+ <td><%= filter_params(@request.params) if @request %></td>
46
+ </tr>
47
+ <% if @request && @request.headers -%>
48
+ <tr>
49
+ <td>Accept:</td>
50
+ <td><%= @request.headers["Accept"] %></td>
51
+ </tr>
52
+ <tr>
53
+ <td>Request Method:</td>
54
+ <td><%= @request.headers["REQUEST_METHOD"] %></td>
55
+ </tr>
56
+ <tr>
57
+ <td>Content Type:</td>
58
+ <td><%= @request.headers["CONTENT_TYPE"] %></td>
59
+ </tr>
60
+ <tr>
61
+ <td>Request URI:</td>
62
+ <td><%= @request.headers["REQUEST_URI"] %></td>
63
+ </tr>
64
+ <tr>
65
+ <td>User Agent:</td>
66
+ <td><%= @request.headers["HTTP_USER_AGENT"] %></td>
67
+ </tr>
68
+ <tr>
69
+ <td>Remote IP:</td>
70
+ <td><%= @request.headers["REMOTE_ADDR"] %></td>
71
+ </tr>
72
+ <tr>
73
+ <td>Http X Request:</td>
74
+ <td><%= @request.headers["X_REQUESTED_WITH"] %></td>
75
+ </tr>
76
+ <tr>
77
+ <td>Request Time (UTC):</td>
78
+ <td><%= Time.now.utc.to_s %></td>
79
+ </tr>
80
+ <tr>
81
+ <td>Request Time (Mountain):</td>
82
+ <td><%= Time.now.in_time_zone(DISPLAY_TIME_ZONE) %></td>
83
+ </tr>
84
+ <% end -%>
85
+ </table>
86
+
87
+ <p><strong>Exception Backtrace:</strong></p>
88
+ <%= @exception.backtrace.join("<br />\n").html_safe if @exception %> <%# DS Verified XSS potential; nothing bad in back trace %>
@@ -0,0 +1,22 @@
1
+ <html style="font-family:helvetica,arial,sans-serif; color:#888">
2
+ <head>
3
+ <title>abaqis Security Alert</title>
4
+ </head>
5
+ <body>
6
+ <div style="border-bottom:3px solid #AFAFAF;padding:0 20px 10px 20px;position:relative;height:52px;min-width:340px">
7
+ <h1 style="font-size:23px;color:#7F8084;line-height:52px;margin:0;padding:0;">abaqis Security Alert</h1>
8
+ <img style="position:absolute;right:20px;top:0" src="http://www.providigm.com/wp-content/uploads/2011/08/abaqis_logo_transparent2.png"/>
9
+ </div>
10
+ <div style="padding:0 20px;font-size:14px;position:relative;color:#404040;overflow:hidden;min-width:340px">
11
+ <div style="width:340px;padding:10px 20px 10px 0;float:left;">
12
+ <h2 style="font-size:16px;margin:2% 0 1% 0;">abaqis&reg; foreign IP access attempt - <%#TODO: (DK) fix reference to account = @user.account.organization || @user.account.name %></h2>
13
+ <p>Foreign IP access attempt at: <%#TODO: (DK) fix reference to Abaqis = Abaqis.get_host_uri %></p>
14
+ <p style="line-height:18px">At <%#TODO: (DK) fix reference to constant =Time.now.in_time_zone(DISPLAY_TIME_ZONE).strftime("%m/%-d/%Y %H:%M:%S")%> MST there was an attempt to access abaqis from a foreign IP address.</p>
15
+ <p style="line-height:18px">User: <%= @user.try(:login) %></p>
16
+ <p style="line-height:18px">Organization: <%#TODO: (DK) fix reference to account = @user.account.organization || @user.account.name %></p>
17
+ <p style="line-height:18px">IP: <%= @visitor_log.try(:remote_addr) %></p>
18
+ <p style="line-height:18px">Country Code: <%= @visitor_log.try(:country) %></p>
19
+ </div>
20
+ </div>
21
+ </body>
22
+ </html>
@@ -0,0 +1,28 @@
1
+ <% url = defined?(HOST_URI) ? HOST_URI : "http://abaqis.com" %>
2
+ <% path = url + new_password_path + "?pu=t" %>
3
+ <html style="font-family:helvetica,arial,sans-serif; color:#888">
4
+ <head>
5
+ <title>Password Expiration Notice</title>
6
+ </head>
7
+ <body>
8
+ <div style="border-bottom:3px solid #AFAFAF;padding:0 20px 10px 20px;position:relative;height:52px;min-width:340px">
9
+ <img style="position:absolute;right:20px;top:0" src="http://www.providigm.com/wp-content/uploads/2011/08/abaqis_logo_transparent2.png"/>
10
+ </div>
11
+ <div style="padding:0 20px;font-size:14px;position:relative;color:#404040;overflow:hidden;min-width:340px">
12
+ <div style="width:340px;padding:10px 20px 10px 0;float:left;">
13
+ <h2 style="font-size:16px;margin:2% 0 1% 0;">Password Expiration Notice</h2>
14
+ <p style="line-height:18px">Your abaqis password will expire on <%= @pw_expiration_date.strftime("%B %d, %Y") %>.</p>
15
+ <p style="line-height:18px">You can change your password by clicking on the link below or by copying and pasting it into your browser's address bar.</p>
16
+ <p style="line-height:18px">Password reset:<br/>
17
+ <a href='<%= path %>'><%= path %></a></p>
18
+ <p style ="line-height:18px">–– The abaqis team</p>
19
+ </div>
20
+ <p style="width:179px;float:left;font-size:12px;line-height:20px;border-left:1px solid #ddd;padding-left:20px;">
21
+ <strong>Need further assistance?</strong><br/>
22
+ Client Support<br/>
23
+ <strong>866-922-8655</strong><br/>
24
+ <a href="mailto:abaqis_support@providigm.com" style="color:#369">abaqis_support@providigm.com</a><br/>
25
+ </p>
26
+ </div>
27
+ </body>
28
+ </html>
@@ -0,0 +1,28 @@
1
+ <% url = defined?(HOST_URI) ? HOST_URI : "http://abaqis.com" %>
2
+ <html style="font-family:helvetica,arial,sans-serif; color:#888">
3
+ <head>
4
+ <title>abaqis Support Request</title>
5
+ </head>
6
+ <body>
7
+ <div style="border-bottom:3px solid #AFAFAF;padding:0 20px 10px 20px;position:relative;height:52px;min-width:340px">
8
+ <h1 style="font-size:23px;color:#7F8084;line-height:52px;margin:0;padding:0;">Support Request</h1>
9
+ <img style="position:absolute;right:20px;top:0" src="http://www.providigm.com/wp-content/uploads/2011/08/abaqis_logo_transparent2.png"/>
10
+ </div>
11
+ <div style="padding:0 20px;font-size:14px;position:relative;color:#404040;overflow:hidden;min-width:340px">
12
+ <div style="width:340px;padding:10px 20px 10px 0;float:left;">
13
+ <h2 style="font-size:16px;margin:2% 0 1% 0;">abaqis Password Reset</h2>
14
+ <p style="line-height:18px">A request to change your password was made at <%= format_time_12hr(Time.now) %> MST on <%= format_date_long(Date.today) %>, from IP address <%= @request.try(:remote_ip) %>.</p>
15
+ <p style="line-height:18px">If you want to keep your current password, please disregard this message.</p>
16
+ <p style="line-height:18px">To change your password, please click the link below or copy and paste it into your browser's address bar:</p>
17
+ <p style="line-height:18px"><a href="<%= url + "/setpw?token=#{@user.token}" %>" style="color:#369"><%= url + "/setpw?token=#{@user.token}" %></a></p>
18
+ <p style ="line-height:18px">The abaqis team</p>
19
+ </div>
20
+ <p style="width:179px;float:left;font-size:12px;line-height:20px;border-left:1px solid #ddd;padding-left:20px;">
21
+ <strong>Need further assistance?</strong><br/>
22
+ Client Support<br/>
23
+ <strong>866-922-8655</strong><br/>
24
+ <a href="mailto:abaqis_support@providigm.com" style="color:#369">abaqis_support@providigm.com</a><br/>
25
+ </p>
26
+ </div>
27
+ </body>
28
+ </html>