door_mat 0.0.5

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 (176) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +3 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +88 -0
  6. data/Rakefile +32 -0
  7. data/app/assets/javascripts/door_mat/application.js +13 -0
  8. data/app/assets/stylesheets/door_mat/application.css +15 -0
  9. data/app/assets/stylesheets/scaffold.css +56 -0
  10. data/app/controllers/door_mat/activities_controller.rb +106 -0
  11. data/app/controllers/door_mat/application_controller.rb +14 -0
  12. data/app/controllers/door_mat/change_password_controller.rb +32 -0
  13. data/app/controllers/door_mat/forgot_passwords_controller.rb +57 -0
  14. data/app/controllers/door_mat/manage_email_controller.rb +61 -0
  15. data/app/controllers/door_mat/password_less_session_controller.rb +121 -0
  16. data/app/controllers/door_mat/reconfirm_password_controller.rb +27 -0
  17. data/app/controllers/door_mat/sessions_controller.rb +17 -0
  18. data/app/controllers/door_mat/sign_in_controller.rb +60 -0
  19. data/app/controllers/door_mat/sign_up_controller.rb +59 -0
  20. data/app/controllers/door_mat/static_controller.rb +5 -0
  21. data/app/mailers/door_mat/activity_mailer.rb +18 -0
  22. data/app/mailers/door_mat/password_less_session_mailer.rb +12 -0
  23. data/app/models/door_mat/access_token.rb +315 -0
  24. data/app/models/door_mat/activity.rb +14 -0
  25. data/app/models/door_mat/activity_confirm_email.rb +45 -0
  26. data/app/models/door_mat/activity_download_recovery_key.rb +30 -0
  27. data/app/models/door_mat/activity_reset_password.rb +47 -0
  28. data/app/models/door_mat/actor.rb +149 -0
  29. data/app/models/door_mat/change_password.rb +12 -0
  30. data/app/models/door_mat/email.rb +58 -0
  31. data/app/models/door_mat/forgot_password.rb +12 -0
  32. data/app/models/door_mat/membership.rb +42 -0
  33. data/app/models/door_mat/session.rb +315 -0
  34. data/app/models/door_mat/sign_in.rb +31 -0
  35. data/app/models/door_mat/sign_up.rb +17 -0
  36. data/app/views/door_mat/activity_mailer/confirm_email.html.erb +11 -0
  37. data/app/views/door_mat/activity_mailer/confirm_email.text.erb +7 -0
  38. data/app/views/door_mat/activity_mailer/reset_password.html.erb +11 -0
  39. data/app/views/door_mat/activity_mailer/reset_password.text.erb +7 -0
  40. data/app/views/door_mat/change_password/new.html.erb +22 -0
  41. data/app/views/door_mat/forgot_passwords/choose_new_password.html.erb +34 -0
  42. data/app/views/door_mat/forgot_passwords/new.html.erb +14 -0
  43. data/app/views/door_mat/helpers/_errors_if_any.html.erb +10 -0
  44. data/app/views/door_mat/manage_email/new.html.erb +14 -0
  45. data/app/views/door_mat/password_less_session/access_token.html.erb +16 -0
  46. data/app/views/door_mat/password_less_session/new.html.erb +34 -0
  47. data/app/views/door_mat/password_less_session_mailer/send_token.html.erb +11 -0
  48. data/app/views/door_mat/password_less_session_mailer/send_token.text.erb +7 -0
  49. data/app/views/door_mat/reconfirm_password/new.html.erb +12 -0
  50. data/app/views/door_mat/sign_in/new.html.erb +30 -0
  51. data/app/views/door_mat/sign_up/new.html.erb +24 -0
  52. data/app/views/door_mat/static/add_email_success.html.erb +5 -0
  53. data/app/views/door_mat/static/change_password_success.html.erb +2 -0
  54. data/app/views/door_mat/static/confirm_email_success.html.erb +2 -0
  55. data/app/views/door_mat/static/email_confirmation_required.html.erb +17 -0
  56. data/app/views/door_mat/static/forgot_password_verification_mail_sent.html.erb +2 -0
  57. data/app/views/door_mat/static/reconfirm_password_success.html.erb +4 -0
  58. data/app/views/door_mat/static/sign_in_success.html.erb +5 -0
  59. data/app/views/door_mat/static/sign_out_success.html.erb +5 -0
  60. data/app/views/door_mat/static/sign_up_success.html.erb +4 -0
  61. data/bin/rails +12 -0
  62. data/config/locales/en.yml +73 -0
  63. data/config/routes.rb +48 -0
  64. data/db/migrate/20140616234935_create_door_mat_actors.rb +23 -0
  65. data/db/migrate/20140617233357_create_door_mat_sessions.rb +17 -0
  66. data/db/migrate/20140630043202_create_door_mat_emails.rb +12 -0
  67. data/db/migrate/20140702045729_create_door_mat_activities.rb +14 -0
  68. data/db/migrate/20141115183045_create_door_mat_access_tokens.rb +17 -0
  69. data/db/migrate/20141121191824_create_door_mat_memberships.rb +14 -0
  70. data/db/migrate/20150910182126_rename_session_guid_column.rb +5 -0
  71. data/db/migrate/20150918210831_add_access_token_rating_column.rb +5 -0
  72. data/door_mat.gemspec +37 -0
  73. data/lib/door_mat.rb +20 -0
  74. data/lib/door_mat/attr_asymmetric_store.rb +82 -0
  75. data/lib/door_mat/attr_symmetric_store.rb +82 -0
  76. data/lib/door_mat/configuration.rb +193 -0
  77. data/lib/door_mat/controller.rb +117 -0
  78. data/lib/door_mat/crypto.rb +49 -0
  79. data/lib/door_mat/crypto/asymmetric_store.rb +77 -0
  80. data/lib/door_mat/crypto/fast_hash.rb +17 -0
  81. data/lib/door_mat/crypto/password_hash.rb +39 -0
  82. data/lib/door_mat/crypto/secure_compare.rb +23 -0
  83. data/lib/door_mat/crypto/symmetric_store.rb +68 -0
  84. data/lib/door_mat/engine.rb +23 -0
  85. data/lib/door_mat/process/actor_password_change.rb +65 -0
  86. data/lib/door_mat/process/actor_sign_in.rb +38 -0
  87. data/lib/door_mat/process/actor_sign_up.rb +39 -0
  88. data/lib/door_mat/process/create_new_anonymous_actor.rb +36 -0
  89. data/lib/door_mat/process/manage_email.rb +42 -0
  90. data/lib/door_mat/process/reset_password.rb +50 -0
  91. data/lib/door_mat/regex.rb +17 -0
  92. data/lib/door_mat/test_helper.rb +58 -0
  93. data/lib/door_mat/url_protocol.rb +9 -0
  94. data/lib/door_mat/version.rb +3 -0
  95. data/lib/tasks/door_mat_tasks.rake +31 -0
  96. data/spec/controllers/door_mat/activities_controller_spec.rb +70 -0
  97. data/spec/controllers/door_mat/forgot_passwords_controller_spec.rb +57 -0
  98. data/spec/controllers/door_mat/manage_email_spec.rb +181 -0
  99. data/spec/controllers/door_mat/password_less_session_controller_spec.rb +344 -0
  100. data/spec/controllers/door_mat/sign_in_controller_spec.rb +211 -0
  101. data/spec/controllers/door_mat/sign_up_controller_spec.rb +90 -0
  102. data/spec/factories/door_mat_access_tokens.rb +6 -0
  103. data/spec/factories/door_mat_activitiess.rb +6 -0
  104. data/spec/factories/door_mat_actors.rb +23 -0
  105. data/spec/factories/door_mat_emails.rb +14 -0
  106. data/spec/factories/door_mat_memberships.rb +6 -0
  107. data/spec/factories/door_mat_sessions.rb +24 -0
  108. data/spec/features/password_less_session_spec.rb +165 -0
  109. data/spec/features/remember_me_spec.rb +672 -0
  110. data/spec/features/session_spec.rb +336 -0
  111. data/spec/lib/attr_store_spec.rb +237 -0
  112. data/spec/lib/crypto_spec.rb +130 -0
  113. data/spec/lib/process_spec.rb +159 -0
  114. data/spec/models/door_mat/access_token_spec.rb +134 -0
  115. data/spec/models/door_mat/activity_spec.rb +38 -0
  116. data/spec/models/door_mat/actor_spec.rb +56 -0
  117. data/spec/models/door_mat/email_spec.rb +25 -0
  118. data/spec/models/door_mat/session_spec.rb +69 -0
  119. data/spec/spec_helper.rb +223 -0
  120. data/spec/support/timecop/timecop_helper.rb +52 -0
  121. data/spec/test_app/README.rdoc +28 -0
  122. data/spec/test_app/Rakefile +6 -0
  123. data/spec/test_app/app/assets/javascripts/application.js +13 -0
  124. data/spec/test_app/app/assets/stylesheets/application.css +15 -0
  125. data/spec/test_app/app/controllers/account_controller.rb +28 -0
  126. data/spec/test_app/app/controllers/application_controller.rb +10 -0
  127. data/spec/test_app/app/controllers/password_less_sample_controller.rb +56 -0
  128. data/spec/test_app/app/controllers/static_controller.rb +7 -0
  129. data/spec/test_app/app/helpers/account_helper.rb +2 -0
  130. data/spec/test_app/app/helpers/application_helper.rb +2 -0
  131. data/spec/test_app/app/models/game.rb +62 -0
  132. data/spec/test_app/app/models/shared_data.rb +4 -0
  133. data/spec/test_app/app/models/shared_key.rb +8 -0
  134. data/spec/test_app/app/models/user_detail.rb +7 -0
  135. data/spec/test_app/app/views/account/show.html.erb +133 -0
  136. data/spec/test_app/app/views/door_mat/static/sign_out_success.html.erb +7 -0
  137. data/spec/test_app/app/views/layouts/application.html.erb +20 -0
  138. data/spec/test_app/app/views/password_less_sample/draw_results.html.erb +6 -0
  139. data/spec/test_app/app/views/password_less_sample/final_result.html.erb +7 -0
  140. data/spec/test_app/app/views/password_less_sample/play_game.html.erb +5 -0
  141. data/spec/test_app/app/views/password_less_sample/show_loosing_door.html.erb +10 -0
  142. data/spec/test_app/app/views/static/index.html.erb +12 -0
  143. data/spec/test_app/app/views/static/only_confirmed_email_allowed.html.erb +10 -0
  144. data/spec/test_app/app/views/static/page_that_require_password_reconfirmation.html.erb +16 -0
  145. data/spec/test_app/app/views/static/session_protected_page.html.erb +32 -0
  146. data/spec/test_app/bin/bundle +3 -0
  147. data/spec/test_app/bin/rails +4 -0
  148. data/spec/test_app/bin/rake +4 -0
  149. data/spec/test_app/config.ru +4 -0
  150. data/spec/test_app/config/application.rb +29 -0
  151. data/spec/test_app/config/boot.rb +5 -0
  152. data/spec/test_app/config/database.yml +25 -0
  153. data/spec/test_app/config/environment.rb +19 -0
  154. data/spec/test_app/config/environments/development.rb +50 -0
  155. data/spec/test_app/config/environments/production.rb +83 -0
  156. data/spec/test_app/config/environments/test.rb +48 -0
  157. data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
  158. data/spec/test_app/config/initializers/cookies_serializer.rb +3 -0
  159. data/spec/test_app/config/initializers/door_mat.rb +72 -0
  160. data/spec/test_app/config/initializers/filter_parameter_logging.rb +4 -0
  161. data/spec/test_app/config/initializers/inflections.rb +16 -0
  162. data/spec/test_app/config/initializers/mime_types.rb +4 -0
  163. data/spec/test_app/config/initializers/session_store.rb +3 -0
  164. data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
  165. data/spec/test_app/config/locales/en.yml +23 -0
  166. data/spec/test_app/config/routes.rb +42 -0
  167. data/spec/test_app/config/secrets.yml +31 -0
  168. data/spec/test_app/db/migrate/20140717182813_create_user_details.rb +10 -0
  169. data/spec/test_app/db/migrate/20140908225256_create_shared_data.rb +10 -0
  170. data/spec/test_app/db/migrate/20140908225604_create_shared_keys.rb +11 -0
  171. data/spec/test_app/db/migrate/20141121190714_create_games.rb +10 -0
  172. data/spec/test_app/public/404.html +67 -0
  173. data/spec/test_app/public/422.html +67 -0
  174. data/spec/test_app/public/500.html +66 -0
  175. data/spec/test_app/public/favicon.ico +0 -0
  176. metadata +552 -0
@@ -0,0 +1,48 @@
1
+ DoorMat::Engine.routes.draw do
2
+ if DoorMat.configuration.define_door_mat_routes
3
+
4
+ get '/sign_up' => 'sign_up#new', as: 'sign_up'
5
+ post '/sign_up' => 'sign_up#create'
6
+
7
+ get '/sign_in' => 'sign_in#new', as: 'sign_in'
8
+ post '/sign_in' => 'sign_in#create'
9
+ get '/sign_out' => 'sign_in#destroy', as: 'sign_out'
10
+
11
+ post '/terminate_session/:guid' => 'sessions#terminate', as: 'terminate_session'
12
+ get '/reconfirm_password' => 'reconfirm_password#new', as: 'reconfirm_password'
13
+ post '/reconfirm_password' => 'reconfirm_password#create'
14
+
15
+ get '/add_email' => 'manage_email#new', as: 'add_email'
16
+ post '/add_email' => 'manage_email#create'
17
+ post '/delete_email' => 'manage_email#destroy'
18
+ post '/set_primary_email' => 'manage_email#set_primary_email'
19
+
20
+ get '/email_confirmation_required' => 'static#email_confirmation_required', as: 'email_confirmation_required'
21
+ get '/confirm_email/:token/:email' => 'activities#confirm_email', as: 'confirm_email'
22
+ post '/resend_email_confirmation' => 'activities#resend_email_confirmation', as: 'resend_email_confirmation'
23
+ post '/download_recovery_key' => 'activities#download_recovery_key', as: 'download_recovery_key'
24
+
25
+ get '/change_password' => 'change_password#new', as: 'change_password'
26
+ post '/change_password' => 'change_password#create'
27
+
28
+ get '/sign_up_success' => 'static#sign_up_success', as: 'sign_up_success'
29
+ get '/sign_in_success' => 'static#sign_in_success', as: 'sign_in_success'
30
+ get '/add_email_success' => 'static#add_email_success', as: 'add_email_success'
31
+ get '/confirm_email_success' => 'static#confirm_email_success', as: 'confirm_email_success'
32
+ get '/change_password_success' => 'static#change_password_success', as: 'change_password_success'
33
+ get '/reconfirm_password_success' => 'static#reconfirm_password_success', as: 'reconfirm_password_success'
34
+
35
+
36
+ # skip_before_filter :require_valid_session for these routes
37
+ get '/access_token/:token_for(/:token)' => 'password_less_session#access_token', :as => 'access_token_token_for_token'
38
+ post '/access_token' => 'password_less_session#access_token_post'
39
+
40
+ get '/sign_out_success' => 'static#sign_out_success'
41
+ get '/forgot_password_verification_mail_sent' => 'static#forgot_password_verification_mail_sent'
42
+
43
+ get '/forgot_password' => 'forgot_passwords#new'
44
+ post '/forgot_password' => 'forgot_passwords#create'
45
+ get '/choose_new_password/:token/:email' => 'forgot_passwords#choose_new_password', as: 'choose_new_password'
46
+ post '/reset_password' => 'forgot_passwords#reset_password', as: 'reset_password'
47
+ end
48
+ end
@@ -0,0 +1,23 @@
1
+ class CreateDoorMatActors < ActiveRecord::Migration
2
+ def change
3
+ create_table :door_mat_actors do |t|
4
+ # The salt of the user's password derived key; used for symmetric encryption of data
5
+ t.text :key_salt, :default => '', :null => false
6
+ # The salt of the password
7
+ t.text :password_salt, :default => '', :null => false
8
+ # The resulting hash used for authentication
9
+ t.text :password_hash, :default => '', :null => false
10
+ # A symmetric encryption key used by the system to encrypt data before handing it to the user
11
+ t.text :system_key, :default => '', :null => false
12
+ # The encrypted user key, used to recover data in a password recovery scenario
13
+ t.text :recovery_key, :default => '', :null => false
14
+
15
+ # The key to decrypt the pem pbkey; it is encrypted using the user's password derived key
16
+ t.text :encrypted_pem_key, :default => '', :null => false
17
+ t.text :pem_encrypted_pkey, :default => '', :null => false
18
+ t.text :pem_public_key, :default => '', :null => false
19
+
20
+ t.timestamps :null => false
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ class CreateDoorMatSessions < ActiveRecord::Migration
2
+ def change
3
+ create_table :door_mat_sessions do |t|
4
+ t.belongs_to :actor, index: true
5
+ t.belongs_to :email
6
+ t.string :session_guid, :default => '', :null => false, index: true
7
+ t.text :type, :default => '', :null => false
8
+ t.text :agent, :default => '', :null => false
9
+ t.text :ip, :default => '', :null => false
10
+ t.text :encrypted_symmetric_actor_key, :default => '', :null => false
11
+ t.datetime :password_authenticated_at, :default => Date.new(2014,1,1), :null => false
12
+ t.integer :rating, :default => 0
13
+
14
+ t.timestamps :null => false
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ class CreateDoorMatEmails < ActiveRecord::Migration
2
+ def change
3
+ create_table :door_mat_emails do |t|
4
+ t.belongs_to :actor, index: true
5
+ t.text :address_hash, :default => '', :null => false, index: true
6
+ t.text :address, :default => '', :null => false
7
+ t.integer :status, :default => 0
8
+
9
+ t.timestamps :null => false
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ class CreateDoorMatActivities < ActiveRecord::Migration
2
+ def change
3
+ create_table :door_mat_activities do |t|
4
+ t.belongs_to :actor, index: true
5
+ t.string :type, :default => '', :null => false
6
+ t.integer :notifier_id, :null => false
7
+ t.string :notifier_type, :default => '', :null => false
8
+ t.text :link_hash, :default => '', :null => false
9
+ t.integer :status, :default => 0
10
+
11
+ t.timestamps :null => false
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ class CreateDoorMatAccessTokens < ActiveRecord::Migration
2
+ def change
3
+ create_table :door_mat_access_tokens do |t|
4
+
5
+ t.belongs_to :actor
6
+ t.text :hashed_token, :default => '', :null => false, index: true
7
+ t.text :name, :default => '', :null => false
8
+ t.integer :token_for, :default => 0
9
+ t.integer :status, :default => 0
10
+ t.text :identifier, :default => '', :null => false
11
+ t.text :data, :default => '', :null => false
12
+ t.integer :reference_id, :default => 0, :null => false
13
+
14
+ t.timestamps :null => false
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ class CreateDoorMatMemberships < ActiveRecord::Migration
2
+ def change
3
+ create_table :door_mat_memberships do |t|
4
+ t.belongs_to :member, index: true, class_name: "DoorMat::Actor"
5
+ t.belongs_to :member_of, index: true, class_name: "DoorMat::Actor"
6
+ t.integer :sponsor, :default => 0
7
+ t.integer :owner, :default => 0
8
+ t.integer :permission, :default => 0
9
+ t.text :key, :default => '', :null => false
10
+
11
+ t.timestamps :null => false
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ class RenameSessionGuidColumn < ActiveRecord::Migration
2
+ def change
3
+ rename_column :door_mat_sessions, :session_guid, :hashed_token
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddAccessTokenRatingColumn < ActiveRecord::Migration
2
+ def change
3
+ add_column :door_mat_access_tokens, :rating, :integer, :default => 0
4
+ end
5
+ end
@@ -0,0 +1,37 @@
1
+ $:.push File.expand_path('../lib', __FILE__)
2
+
3
+ require 'door_mat/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'door_mat'
7
+ s.version = DoorMat::VERSION
8
+ s.authors = ['Luc Lussier']
9
+ s.email = ['luc.lussier@gmail.com']
10
+ s.homepage = 'https://github.com/datamolecule/door_mat'
11
+ s.summary = 'User authentication and data encryption'
12
+ s.description = 'DoorMat is a Rails Engine that provides a solution for both user authentication and the encryption of user information. It aims to offer safe defaults so you can get going with what your website is really about.'
13
+ s.license = 'MIT'
14
+
15
+ s.files = Dir['{app,bin,config,db,lib}/**/*', 'MIT-LICENSE', 'Rakefile', 'README.md', '.rspec', 'door_mat.gemspec', 'Gemfile']
16
+ s.test_files = Dir['spec/**/*']
17
+
18
+ s.add_dependency 'rails', '~> 4.2'
19
+ s.add_dependency 'bcrypt', '~> 3.1' #https://github.com/codahale/bcrypt-ruby
20
+ s.add_dependency 'request_store', '~> 1.1'
21
+
22
+ s.add_development_dependency 'sprockets', '2.12.4' # to prevent https://github.com/rails/rails/issues/19853
23
+ s.add_development_dependency 'sqlite3'
24
+ s.add_development_dependency 'database_cleaner'
25
+ s.add_development_dependency 'rspec-rails', '~> 3.4.2'
26
+ s.add_development_dependency 'factory_girl_rails', '~> 4.4.1'
27
+ s.add_development_dependency 'email_spec', '~> 2.0'
28
+ s.add_development_dependency 'capybara'
29
+ s.add_development_dependency 'show_me_the_cookies'
30
+ s.add_development_dependency 'poltergeist'
31
+ s.add_development_dependency 'selenium-webdriver'
32
+ s.add_development_dependency 'byebug'
33
+ s.add_development_dependency 'timecop'
34
+ s.add_development_dependency 'better_errors'
35
+ s.add_development_dependency 'binding_of_caller'
36
+ s.add_development_dependency 'simplecov'
37
+ end
@@ -0,0 +1,20 @@
1
+ require 'request_store'
2
+
3
+ require 'door_mat/configuration'
4
+ require 'door_mat/engine'
5
+ require 'door_mat/crypto'
6
+ require 'door_mat/attr_symmetric_store'
7
+ require 'door_mat/attr_asymmetric_store'
8
+ require 'door_mat/controller'
9
+ require 'door_mat/url_protocol'
10
+ require 'door_mat/regex'
11
+
12
+ require 'door_mat/process/actor_password_change'
13
+ require 'door_mat/process/actor_sign_in'
14
+ require 'door_mat/process/actor_sign_up'
15
+ require 'door_mat/process/create_new_anonymous_actor'
16
+ require 'door_mat/process/manage_email'
17
+ require 'door_mat/process/reset_password'
18
+
19
+ module DoorMat
20
+ end
@@ -0,0 +1,82 @@
1
+ module DoorMat
2
+ module AttrAsymmetricStore
3
+ class AttrAsymmetricStoreWrapper
4
+
5
+ def initialize(attribute, actor_column)
6
+ @attribute = attribute
7
+ @actor_column = actor_column
8
+ end
9
+
10
+ def after_find(record)
11
+ return if DoorMat::Crypto.current_skip_crypto_callback.skip?
12
+
13
+ # leave attribute as-is if blank or the actor is not set
14
+ encrypted_attribute = record.send("#{@attribute}")
15
+ actor = record.send("#{@actor_column}")
16
+ return if encrypted_attribute.blank? || actor.blank?
17
+
18
+ clear_attribute = nil
19
+
20
+ DoorMat::Session.current_session.autoload_sesion_for(actor)
21
+ DoorMat::Session.current_session.with_session_for_actor(actor) do |session|
22
+ clear_attribute = actor.decrypt_shared_key(encrypted_attribute, session)
23
+ end
24
+
25
+ if clear_attribute.nil?
26
+ clear_attribute = '[ENCRYPTED SHARED KEY]'
27
+ end
28
+
29
+ record.send("#{@attribute}=", clear_attribute)
30
+ DoorMat.configuration.logger.debug "DEBUG: Decrypt #{@attribute}: #{encrypted_attribute} -> #{clear_attribute}" if Rails.env.development?
31
+ end
32
+
33
+ def around_save(record)
34
+ return yield if DoorMat::Crypto.current_skip_crypto_callback.skip?
35
+
36
+ clear_attribute = record.send("#{@attribute}")
37
+ actor = record.send("#{@actor_column}")
38
+
39
+ # leave attribute as-is if blank or the actor is not set
40
+ if clear_attribute.blank? || actor.blank?
41
+ yield
42
+ else
43
+ encrypted_attribute = actor.encrypt_shared_key(clear_attribute)
44
+
45
+ DoorMat.configuration.logger.debug "DEBUG: Encrypt #{@attribute}: #{clear_attribute} -> #{encrypted_attribute}" if Rails.env.development?
46
+ record.send("#{@attribute}=", encrypted_attribute)
47
+ yield
48
+ record.send("#{@attribute}=", clear_attribute)
49
+ DoorMat.configuration.logger.debug "DEBUG: Decrypt #{@attribute}: #{encrypted_attribute} -> #{clear_attribute}" if Rails.env.development?
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ def self.included(base)
56
+ base.extend ClassMethods
57
+ end
58
+
59
+ module ClassMethods
60
+
61
+ def attr_asymmetric_store(*args, **options)
62
+ return unless self.table_exists?
63
+
64
+ actor_column = options.fetch(:actor_column, :actor).to_s
65
+ unless self.attribute_names.include? "#{actor_column}_id"
66
+ raise ActiveRecord::ActiveRecordError, "attr_asymmetric_store records must belong to a DoorMat::Actor but could not find the actor column. Pass the actor_column: :actor_column_name option to specify it."
67
+ end
68
+
69
+ args.each do |arg|
70
+ column_type = (self.columns_hash[arg.to_s].cast_type.respond_to?(:type) && self.columns_hash[arg.to_s].cast_type.type) || self.columns_hash[arg.to_s].cast_type.to_s
71
+ if [:text, :string].include? column_type
72
+ after_find DoorMat::AttrAsymmetricStore::AttrAsymmetricStoreWrapper.new(arg.to_s, actor_column)
73
+ around_save DoorMat::AttrAsymmetricStore::AttrAsymmetricStoreWrapper.new(arg.to_s, actor_column)
74
+ else
75
+ raise ActiveRecord::ActiveRecordError, "attr_asymmetric_store only support text and string column types."
76
+ end
77
+ end
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,82 @@
1
+ module DoorMat
2
+ module AttrSymmetricStore
3
+ class AttrSymmetricStoreWrapper
4
+
5
+ def initialize(attribute, actor_column)
6
+ @attribute = attribute
7
+ @actor_column = actor_column
8
+ end
9
+
10
+ def after_find(record)
11
+ return if DoorMat::Crypto.current_skip_crypto_callback.skip?
12
+
13
+ encrypted_attribute = record.send("#{@attribute}")
14
+ actor = record.send("#{@actor_column}")
15
+ clear_attribute = nil
16
+
17
+ DoorMat::Session.current_session.autoload_sesion_for(actor)
18
+ DoorMat::Session.current_session.with_session_for_actor(actor) do |session|
19
+ clear_attribute = session.decrypt(encrypted_attribute)
20
+ end
21
+
22
+ if clear_attribute.nil?
23
+ clear_attribute = '[ENCRYPTED]'
24
+ record.readonly!
25
+ end
26
+
27
+ record.send("#{@attribute}=", clear_attribute)
28
+ DoorMat.configuration.logger.debug "DEBUG: Decrypt #{@attribute}: #{encrypted_attribute} -> #{clear_attribute}" if Rails.env.development?
29
+ end
30
+
31
+ def around_save(record)
32
+ return yield if DoorMat::Crypto.current_skip_crypto_callback.skip?
33
+
34
+ raise ActiveRecord::Rollback, "Record is read-only" if record.readonly?
35
+
36
+ clear_attribute = record.send("#{@attribute}")
37
+ actor = record.send("#{@actor_column}")
38
+ encrypted_attribute = nil
39
+
40
+ DoorMat::Session.current_session.autoload_sesion_for(actor)
41
+ DoorMat::Session.current_session.with_session_for_actor(actor) do |session|
42
+ encrypted_attribute = session.encrypt(clear_attribute)
43
+ end
44
+ raise ActiveRecord::Rollback, "DoorMat::Session is not valid" if encrypted_attribute.nil?
45
+
46
+ DoorMat.configuration.logger.debug "DEBUG: Encrypt #{@attribute}: #{clear_attribute} -> #{encrypted_attribute}" if Rails.env.development?
47
+ record.send("#{@attribute}=", encrypted_attribute)
48
+ yield
49
+ record.send("#{@attribute}=", clear_attribute)
50
+ DoorMat.configuration.logger.debug "DEBUG: Decrypt #{@attribute}: #{encrypted_attribute} -> #{clear_attribute}" if Rails.env.development?
51
+ end
52
+
53
+ end
54
+
55
+ def self.included(base)
56
+ base.extend ClassMethods
57
+ end
58
+
59
+ module ClassMethods
60
+
61
+ def attr_symmetric_store(*args, **options)
62
+ return unless self.table_exists?
63
+
64
+ actor_column = options.fetch(:actor_column, :actor).to_s
65
+ unless self.attribute_names.include? "#{actor_column}_id"
66
+ raise ActiveRecord::ActiveRecordError, "attr_symmetric_store records must belong to a DoorMat::Actor but could not find the actor column. Pass the actor_column: :actor_column_name option to specify it."
67
+ end
68
+
69
+ args.each do |arg|
70
+ column_type = (self.columns_hash[arg.to_s].cast_type.respond_to?(:type) && self.columns_hash[arg.to_s].cast_type.type) || self.columns_hash[arg.to_s].cast_type.to_s
71
+ if [:text, :string].include? column_type
72
+ after_find DoorMat::AttrSymmetricStore::AttrSymmetricStoreWrapper.new(arg.to_s, actor_column)
73
+ around_save DoorMat::AttrSymmetricStore::AttrSymmetricStoreWrapper.new(arg.to_s, actor_column)
74
+ else
75
+ raise ActiveRecord::ActiveRecordError, "attr_symmetric_store only support text and string column types."
76
+ end
77
+ end
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,193 @@
1
+ module DoorMat
2
+ class Configuration
3
+
4
+ attr_accessor \
5
+ :mailer_from_address,
6
+ :password_reconfirm_delay,
7
+ :public_computer_access_session_timeout,
8
+ :private_computer_access_session_timeout,
9
+ :forgot_password_link_request_delay_minutes,
10
+ :forgot_password_link_expiration_delay_minutes,
11
+ :allow_remember_me_feature,
12
+ :remember_me_require_private_computer_confirmation,
13
+ :remember_me_max_day_count,
14
+ :leak_email_address_at_reconfirm,
15
+ :plausible_deniability_count,
16
+ :max_email_count_per_actor,
17
+ :define_door_mat_routes,
18
+ :allow_redirect_to_requested_url,
19
+ :lockdown_default_redirect_url,
20
+ :sign_up_success_url,
21
+ :sign_in_success_url,
22
+ :add_email_success_url,
23
+ :destroy_email_redirect_url,
24
+ :set_primary_email_redirect_url,
25
+ :resend_email_confirmation_redirect_url,
26
+ :confirm_email_success_url,
27
+ :reconfirm_password_success_url,
28
+ :change_password_success_url,
29
+ :sign_out_success_url,
30
+ :forgot_password_verification_mail_sent_url,
31
+ :allow_sign_up,
32
+ :allow_sign_in_from_sign_up_form,
33
+ :transmit_cookies_only_over_https,
34
+ :crypto_pbkdf2_salt_length,
35
+ :crypto_pbkdf2_password_length,
36
+ :crypto_pbkdf2_iterations,
37
+ :crypto_bcrypt_cost,
38
+ :crypto_secure_compare_default_length,
39
+ :event_hook_before_sign_up,
40
+ :event_hook_after_sign_up,
41
+ :event_hook_after_failed_sign_up,
42
+ :event_hook_before_sign_in,
43
+ :event_hook_after_sign_in,
44
+ :event_hook_after_failed_sign_in,
45
+ :event_hook_before_confirm_email,
46
+ :event_hook_after_confirm_email,
47
+ :event_hook_after_failed_confirm_email,
48
+ :event_hook_before_download_recovery_key,
49
+ :event_hook_after_download_recovery_key,
50
+ :event_hook_after_failed_download_recovery_key,
51
+ :event_hook_before_sign_out,
52
+ :event_hook_after_sign_out,
53
+ :logger,
54
+ :password_less_sessions
55
+
56
+ def initialize
57
+ @mailer_from_address = "noreply@example.com"
58
+
59
+ # Controllers that require_password_reconfirm will only
60
+ # allow the user in without requesting an additional sign-in if the user password
61
+ # was last entered less than password_reconfirm_delay
62
+ # minutes ago.
63
+ # All sections of the site allowing access to or modification
64
+ # of sensitive information or settings should be protected this way.
65
+ # This includes operations resulting in
66
+ # a financial transaction using stored or pre-authorized payment methods.
67
+ @password_reconfirm_delay = 5
68
+
69
+ # A session from a public computer will only last
70
+ # until the browser is closed and will timeout
71
+ # after public_computer_access_session_timeout
72
+ # minutes of inactivity.
73
+ @public_computer_access_session_timeout = 30
74
+
75
+ # A session from a private computer will survive
76
+ # a browser restart but will expire in the
77
+ # browser and timeout on the system
78
+ # after private_computer_access_session_timeout
79
+ # minutes of inactivity.
80
+ @private_computer_access_session_timeout = 60
81
+
82
+
83
+ # To prevent email flooding, a new request for a recovery password
84
+ # links will only be sent after the specified delay
85
+ @forgot_password_link_request_delay_minutes = 30
86
+
87
+ # Password recovery links older than this delay become invalid
88
+ @forgot_password_link_expiration_delay_minutes = 30
89
+
90
+ # Does the system allow the remember me feature?
91
+ # High value target systems such as financial sites
92
+ # should not allow the remember me feature.
93
+ # Even when this feature is enabled, sensitive area of the site
94
+ # should require users to re-authenticate using a
95
+ # before_action -> {require_password_reconfirm()}
96
+ # filter
97
+ @allow_remember_me_feature = false
98
+
99
+ # As a safety reminder, the user must confirm that they
100
+ # are not loging in from a public computer before enabling
101
+ # the remember me feature
102
+ @remember_me_require_private_computer_confirmation = true
103
+
104
+ # A session from a private computer for which the
105
+ # cookie will remain for a number of days specified
106
+ # by remember_me_max_day_count and automatically
107
+ # renew the session for that period of time
108
+ @remember_me_max_day_count = 30
109
+
110
+ # Do not pre-populate the email address field
111
+ # in the sign_in form while doing a password reconfirmation
112
+ # as it could be considered to leak the information about which
113
+ # email address was used to login to the system before the reconfirmation request
114
+ @leak_email_address_at_reconfirm = false
115
+
116
+ # How many different accounts a single email address can be associated with on the system
117
+ @plausible_deniability_count = 1
118
+
119
+ # How many different emails can be linked to an actor
120
+ @max_email_count_per_actor = 2
121
+
122
+ # Production systems should eventually redefine their own routes explicitly
123
+ # instead of relying on those provided by the engine
124
+ @define_door_mat_routes = true
125
+
126
+ #
127
+ @allow_redirect_to_requested_url = true
128
+
129
+ # When specifying redirects in
130
+ # config/initializers/door_mat.rb you can use:
131
+ # [ :main_app, :__path__ ] or [:__engine_name_, :__path__] respectively to redirect to an
132
+ # existing path defined in your main application or loaded engine.
133
+ # [:main_app, :root_url] to redirect to the root of your main application.
134
+ # [ :request, :referer ] for an alternative to redirect_to :back.
135
+ @lockdown_default_redirect_url = [ :request, :referer ]
136
+ @sign_up_success_url = [ :sign_up_success_url ]
137
+ @sign_in_success_url = [ :sign_in_success_url ]
138
+ @add_email_success_url = [ :add_email_success_url ]
139
+ @destroy_email_redirect_url = [ :request, :referer ]
140
+ @set_primary_email_redirect_url = [ :request, :referer ]
141
+ @resend_email_confirmation_redirect_url = [ :request, :referer ]
142
+ @confirm_email_success_url = [ :confirm_email_success_url ]
143
+ @reconfirm_password_success_url = [ :reconfirm_password_success_url ]
144
+ @change_password_success_url = [ :change_password_success_url ]
145
+ @sign_out_success_url = [ :sign_out_success_url ]
146
+ @forgot_password_verification_mail_sent_url = [ :forgot_password_verification_mail_sent_url ]
147
+
148
+ @allow_sign_up = true
149
+ @allow_sign_in_from_sign_up_form = false
150
+
151
+ @transmit_cookies_only_over_https = true
152
+
153
+ @crypto_pbkdf2_salt_length = 32
154
+ @crypto_pbkdf2_password_length = 32
155
+ @crypto_pbkdf2_iterations = 10_000
156
+
157
+ @crypto_bcrypt_cost = 12
158
+
159
+ @crypto_secure_compare_default_length = 1024
160
+
161
+
162
+ @event_hook_before_sign_up = []
163
+ @event_hook_after_sign_up = []
164
+ @event_hook_after_failed_sign_up = []
165
+ @event_hook_before_sign_in = []
166
+ @event_hook_after_sign_in = []
167
+ @event_hook_after_failed_sign_in = []
168
+ @event_hook_before_confirm_email = []
169
+ @event_hook_after_confirm_email = [] # The confirmed DoorMat::Email is passed as function argument
170
+ @event_hook_after_failed_confirm_email = []
171
+ @event_hook_before_download_recovery_key = []
172
+ @event_hook_after_download_recovery_key = []
173
+ @event_hook_after_failed_download_recovery_key = []
174
+ @event_hook_before_sign_out = []
175
+ @event_hook_after_sign_out = []
176
+
177
+ @logger = Rails.logger
178
+
179
+ # By default, there are no password less sessions defined
180
+ # see test_app/config/initializers/door_mat.rb for sample usage
181
+ @password_less_sessions = {}
182
+
183
+ end
184
+ end
185
+
186
+ def self.configuration
187
+ @configuration ||= DoorMat::Configuration.new
188
+ end
189
+
190
+ def self.configure
191
+ yield configuration
192
+ end
193
+ end