orats 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,853 @@
1
+ # =====================================================================================================
2
+ # Template for generating authentication and authorization on top of the base template
3
+ # =====================================================================================================
4
+
5
+ # ----- Helper functions and variables ----------------------------------------------------------------
6
+
7
+ require 'securerandom'
8
+
9
+ def generate_token
10
+ SecureRandom.hex(64)
11
+ end
12
+
13
+ def create_migration(table_name, migration='')
14
+ utc_now = Time.now.getutc.strftime("%Y%m%d%H%M%S")
15
+ class_name = table_name.to_s.classify.pluralize
16
+
17
+ file "db/migrate/#{utc_now}_create_#{table_name}.rb", %{
18
+ class Create#{class_name} < ActiveRecord::Migration
19
+ def change
20
+ #{migration}
21
+ end
22
+ end
23
+ }
24
+ end
25
+
26
+ app_name_upper = app_name.upcase
27
+
28
+ # ----- Delete application.css ------------------------------------------------------------------------
29
+
30
+ # This gets created by rails automatically when you make a new project
31
+ run 'rm -f app/assets/stylesheets/application.css'
32
+
33
+ # ----- Modify Gemfile --------------------------------------------------------------------------------
34
+
35
+ puts
36
+ say_status 'root', 'Modifying Gemfile..', :yellow
37
+ puts '-'*80, ''; sleep 0.25
38
+
39
+ inject_into_file 'Gemfile', before: "\ngem 'kaminari'" do <<-CODE
40
+
41
+ gem 'devise', '~> 3.2.2'
42
+ gem 'devise-async', '~> 0.9.0'
43
+ gem 'pundit', '~> 0.2.1'
44
+ CODE
45
+ end
46
+
47
+ git add: '.'
48
+ git commit: "-m 'Add devise, devise-async and pundit gems'"
49
+
50
+ # ----- Run bundle install ----------------------------------------------------------------------------
51
+
52
+ puts
53
+ say_status 'action', 'Running bundle install, it should not take too long', :yellow
54
+ puts '-'*80, ''; sleep 0.25
55
+
56
+ run 'bundle install'
57
+
58
+ # ----- Modify sidekiq config -------------------------------------------------------------------------
59
+
60
+ puts
61
+ say_status 'config', 'Modifying the sidekiq config', :yellow
62
+ puts '-'*80, ''; sleep 0.25
63
+
64
+ append_file 'config/sidekiq.yml' do <<-FILE
65
+ - mailer
66
+ FILE
67
+ end
68
+
69
+ git add: '.'
70
+ git commit: "-m 'Add the devise mailer queue to the sidekiq config'"
71
+
72
+ # ----- Create the account fixtures -------------------------------------------------------------------
73
+
74
+ puts
75
+ say_status 'test', 'Creating the account fixtures...', :yellow
76
+ puts '-'*80, ''; sleep 0.25
77
+
78
+ file 'test/fixtures/accounts.yml' do <<-'CODE'
79
+ foo:
80
+ id: 1
81
+ email: foo@bar.com
82
+ encrypted_password: passwordisnotreallyencrypted
83
+ role: admin
84
+ created_at: 2012-01-01 01:45:17
85
+ current_sign_in_at: 2013-03-15 11:22:33
86
+
87
+ no_role:
88
+ id: 2
89
+ email: joey@almostcool.com
90
+ encrypted_password: hackthegibson
91
+ created_at: 1995-09-15 08:10:12
92
+
93
+ bad_role:
94
+ id: 3
95
+ email: hello@world.com
96
+ encrypted_password: reallysecure
97
+ role: ahhhh
98
+ created_at: 2011-09-20 10:10:10
99
+
100
+ beep:
101
+ id: 4
102
+ email: beep@beep.com
103
+ encrypted_password: beepbeepbeep
104
+ created_at: 2010-03-6 05:15:45
105
+ CODE
106
+ end
107
+
108
+ git add: '.'
109
+ git commit: "-m 'Add the account model'"
110
+
111
+ # ----- Modify the test helper ------------------------------------------------------------------------
112
+
113
+ puts
114
+ say_status 'test', 'Modifying the test helper...', :yellow
115
+ puts '-'*80, ''; sleep 0.25
116
+
117
+ inject_into_file 'test/test_helper.rb', after: "end\n" do <<-CODE
118
+
119
+ class ActionController::TestCase
120
+ include Devise::TestHelpers
121
+ end
122
+ CODE
123
+ end
124
+
125
+ git add: '.'
126
+ git commit: "-m 'Add the devise helpers to test helper'"
127
+
128
+ # ----- Create the account unit tests -----------------------------------------------------------------
129
+
130
+ puts
131
+ say_status 'test', 'Creating the account unit tests...', :yellow
132
+ puts '-'*80, ''; sleep 0.25
133
+
134
+ file 'test/models/account_test.rb' do <<-'CODE'
135
+ require 'test_helper'
136
+
137
+ class AccountTest < ActiveSupport::TestCase
138
+ def setup
139
+ @account = accounts(:foo)
140
+ end
141
+
142
+ def teardown
143
+ @account = nil
144
+ end
145
+
146
+ test 'expect new account' do
147
+ assert @account.valid?
148
+ assert_not_nil @account.email
149
+ assert_not_nil @account.encrypted_password
150
+ end
151
+
152
+ test 'expect guest to be default role' do
153
+ no_role = accounts(:no_role)
154
+ assert_equal 'guest', no_role.role
155
+ end
156
+
157
+ test 'expect invalid role to not save' do
158
+ bad_role = accounts(:bad_role)
159
+ assert_not bad_role.valid?
160
+ end
161
+
162
+ test 'expect e-mail to be unique' do
163
+ duplicate = Account.create(email: 'foo@bar.com')
164
+
165
+ assert_not duplicate.valid?
166
+ end
167
+
168
+ test 'expect random password if password is empty' do
169
+ @account.password = ''
170
+ @account.encrypted_password = ''
171
+ @account.save
172
+
173
+ random_password = Account.generate_password
174
+ assert_equal 10, random_password.length
175
+ end
176
+
177
+ test 'expect random password of 20 characters' do
178
+ assert_equal 20, Account.generate_password(20).length
179
+ end
180
+ end
181
+ CODE
182
+ end
183
+
184
+ git add: '.'
185
+ git commit: "-m 'Add the account unit tests'"
186
+
187
+ # ----- Create the account model ----------------------------------------------------------------------
188
+
189
+ puts
190
+ say_status 'models', 'Creating the account model...', :yellow
191
+ puts '-'*80, ''; sleep 0.25
192
+
193
+ file 'app/models/account.rb' do <<-'CODE'
194
+ class Account < ActiveRecord::Base
195
+ ROLES = %w[admin guest]
196
+
197
+ devise :database_authenticatable, :registerable, :recoverable, :rememberable,
198
+ :trackable, :timeoutable, :lockable, :validatable, :async
199
+
200
+ before_validation :ensure_password, on: :create
201
+
202
+ after_save :invalidate_cache
203
+
204
+ validates :role, inclusion: { in: ROLES }
205
+
206
+ def self.serialize_from_session(key, salt)
207
+ # store the current_account in the cache so we do not perform a db lookup on each authenticated page
208
+ single_key = key.is_a?(Array) ? key.first : key
209
+
210
+ Rails.cache.fetch("account:#{single_key}") do
211
+ Account.where(id: single_key).entries.first
212
+ end
213
+ end
214
+
215
+ def self.generate_password(length = 10)
216
+ Devise.friendly_token.first(length)
217
+ end
218
+
219
+ def is?(role_check)
220
+ role.to_sym == role_check
221
+ end
222
+
223
+ private
224
+
225
+ def ensure_password
226
+ # only generate a password if it does not exist
227
+ self.password ||= Account.generate_password
228
+ end
229
+
230
+ def invalidate_cache
231
+ Rails.cache.delete("account:#{id}")
232
+ end
233
+ end
234
+ CODE
235
+ end
236
+
237
+ git add: '.'
238
+ git commit: "-m 'Add the account model'"
239
+
240
+ # ----- Create devise migration -----------------------------------------------------------------------
241
+
242
+ puts
243
+ say_status 'db', 'Creating devise model migration...', :yellow
244
+ puts '-'*80, ''; sleep 0.25
245
+
246
+ create_migration :accounts, %{
247
+ create_table(:accounts) do |t|
248
+ ## Database authenticatable
249
+ t.string :email, :null => false, :default => ''
250
+ t.string :encrypted_password, :null => false, :default => ''
251
+
252
+ ## Recoverable
253
+ t.string :reset_password_token
254
+ t.datetime :reset_password_sent_at
255
+
256
+ ## Rememberable
257
+ t.datetime :remember_created_at
258
+
259
+ ## Trackable
260
+ t.integer :sign_in_count, :default => 0, :null => false
261
+ t.datetime :current_sign_in_at
262
+ t.datetime :last_sign_in_at
263
+ t.string :current_sign_in_ip
264
+ t.string :last_sign_in_ip
265
+
266
+ ## Lockable
267
+ t.integer :failed_attempts, :default => 0, :null => false # Only if lock strategy is :failed_attempts
268
+ t.string :unlock_token # Only if unlock strategy is :email or :both
269
+ t.datetime :locked_at
270
+
271
+ ## Role
272
+ t.string :role, default: 'guest'
273
+
274
+ t.timestamps
275
+ end
276
+
277
+ add_index :accounts, :email, :unique => true
278
+ add_index :accounts, :reset_password_token, :unique => true
279
+ add_index :accounts, :unlock_token, :unique => true
280
+ }
281
+
282
+ git add: '.'
283
+ git commit: "-m 'Add devise model migration'"
284
+
285
+ # ----- Create a seed user ----------------------------------------------------------------------------
286
+
287
+ puts
288
+ say_status 'db', 'Creating a seed user...', :yellow
289
+ puts '-'*80, ''; sleep 0.25
290
+
291
+ append_file 'db/seeds.rb', "\nAccount.create({ email: \"admin@#{app_name}.com\", password: \"password\", role: \"admin\" })"
292
+
293
+ git add: '.'
294
+ git commit: "-m 'Add a seed user'"
295
+
296
+ # ----- Create en i18n entries ------------------------------------------------------------------------
297
+
298
+ puts
299
+ say_status 'db', 'Creating en i18n entries...', :yellow
300
+ puts '-'*80, ''; sleep 0.25
301
+
302
+ gsub_file 'config/locales/en.yml', "hello: \"Hello world\"\n", ''
303
+
304
+ append_file 'config/locales/en.yml' do <<-CODE
305
+ authorization:
306
+ error: 'You are not authorized to perform this action.'
307
+ CODE
308
+ end
309
+
310
+ git add: '.'
311
+ git commit: "-m 'Add en i18n entries'"
312
+
313
+ # ----- Modify the application controller -------------------------------------------------------------
314
+
315
+ puts
316
+ say_status 'db', 'Modifying the application controller...', :yellow
317
+ puts '-'*80, ''; sleep 0.25
318
+
319
+ inject_into_file 'app/controllers/application_controller.rb', after: "::Base\n" do <<-'CODE'
320
+ alias_method :current_user, :current_account
321
+
322
+ CODE
323
+ end
324
+
325
+ git add: '.'
326
+ git commit: "-m 'Alias current_user to current_account to play nice with other gems'"
327
+
328
+ inject_into_file 'app/controllers/application_controller.rb', before: "end\n" do <<-'CODE'
329
+
330
+ private
331
+
332
+ # Override devise to customize the after sign in path.
333
+ #def after_sign_in_path_for(resource)
334
+ # if resource.is? :admin
335
+ # admin_path
336
+ # else
337
+ # somewhere_path
338
+ # end
339
+ #end
340
+ CODE
341
+ end
342
+
343
+ git add: '.'
344
+ git commit: "-m 'Change the application controller to allow overriding the devise sign in path'"
345
+
346
+ # ----- Create the devise views -----------------------------------------------------------------------
347
+
348
+ puts
349
+ say_status 'views', 'Creating the devise views...', :yellow
350
+ puts '-'*80, ''; sleep 0.25
351
+
352
+ file 'app/views/devise/confirmations/new.html.erb' do <<-HTML
353
+ <%
354
+ title 'Confirm'
355
+ meta_description '...'
356
+ heading 'Confirm'
357
+ %>
358
+
359
+ <div class="row">
360
+ <div class="col-sm-4">
361
+ <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
362
+ <div class="form-group">
363
+ <%= f.label :email %>
364
+ <%= f.email_field :email, class: 'form-control', maxlength: 254, autofocus: true,
365
+ data: {
366
+ 'rule-required' => 'true',
367
+ 'rule-maxlength' => '254'
368
+ } %>
369
+ </div>
370
+ <%= button_tag type: 'submit', class: 'btn btn-primary' do %>
371
+ Send
372
+ <% end %>
373
+ <% end %>
374
+ </div>
375
+ <div class="col-sm-6 col-sm-offset-2">
376
+ <%= render 'devise/shared/links' %>
377
+ </div>
378
+ </div>
379
+ HTML
380
+ end
381
+
382
+ file 'app/views/devise/mailer/confirmation_instructions.html.erb' do <<-HTML
383
+ <p>Welcome <%= @email %>!</p>
384
+
385
+ <p>You can confirm your account email through the link below:</p>
386
+
387
+ <p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>
388
+ HTML
389
+ end
390
+
391
+ file 'app/views/devise/mailer/reset_password_instructions.html.erb' do <<-HTML
392
+ <p>Hello <%= @resource.email %>!</p>
393
+
394
+ <p>Someone has requested a link to change your password. You can do this through the link below.</p>
395
+
396
+ <p><%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %></p>
397
+
398
+ <p>If you didn't request this, please ignore this email.</p>
399
+ <p>Your password won't change until you access the link above and create a new one.</p>
400
+ HTML
401
+ end
402
+
403
+ file 'app/views/devise/mailer/unlock_instructions.html.erb' do <<-HTML
404
+ <p>Hello <%= @resource.email %>!</p>
405
+
406
+ <p>Your account has been locked due to an excessive number of unsuccessful sign in attempts.</p>
407
+
408
+ <p>Click the link below to unlock your account:</p>
409
+
410
+ <p><%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %></p>
411
+ HTML
412
+ end
413
+
414
+ file 'app/views/devise/passwords/edit.html.erb' do <<-HTML
415
+ <%
416
+ title 'Change your password'
417
+ meta_description '...'
418
+ heading 'Change your password'
419
+ %>
420
+
421
+ <div class="row">
422
+ <div class="col-sm-4">
423
+ <%= form_for resource, as: resource_name, url: password_path(resource_name), html: { method: :put } do |f| %>
424
+ <%= f.hidden_field :reset_password_token %>
425
+ <div class="form-group">
426
+ <%= f.label :password, 'New password' %>
427
+ <%= f.password_field :password, maxlength: 128, class: 'form-control', autofocus: true,
428
+ data: {
429
+ 'rule-required' => 'true',
430
+ 'rule-minlength' => '8',
431
+ 'rule-maxlength' => '128'
432
+ } %>
433
+ </div>
434
+ <%= button_tag type: 'submit', class: 'btn btn-primary' do %>
435
+ Send
436
+ <% end %>
437
+ <% end %>
438
+ </div>
439
+ <div class="col-sm-6 col-sm-offset-2">
440
+ <%= render 'devise/shared/links' %>
441
+ </div>
442
+ </div>
443
+ HTML
444
+ end
445
+
446
+ file 'app/views/devise/passwords/new.html.erb' do <<-HTML
447
+ <%
448
+ title 'Forgot your password?'
449
+ meta_description '...'
450
+ heading 'Forgot your password?'
451
+ %>
452
+
453
+ <div class="row">
454
+ <div class="col-sm-4">
455
+ <%= form_for resource, as: resource_name, url: password_path(resource_name), html: { method: :post } do |f| %>
456
+ <div class="form-group">
457
+ <%= f.label :email %>
458
+ <%= f.email_field :email, class: 'form-control', autofocus: true, maxlength: 254,
459
+ data: {
460
+ 'rule-required' => 'true',
461
+ 'rule-maxlength' => '254'
462
+ } %>
463
+ </div>
464
+ <%= button_tag type: 'submit', class: 'btn btn-primary' do %>
465
+ Send
466
+ <% end %>
467
+ <% end %>
468
+ </div>
469
+ <div class="col-sm-6 col-sm-offset-2">
470
+ <%= render 'devise/shared/links' %>
471
+ </div>
472
+ </div>
473
+ HTML
474
+ end
475
+
476
+ file 'app/views/devise/registrations/edit.html.erb' do <<-HTML
477
+ <%
478
+ title 'Edit your account'
479
+ meta_description '...'
480
+ heading 'Edit your account'
481
+ %>
482
+
483
+ <div class="row">
484
+ <div class="col-sm-6">
485
+ <%= form_for resource, as: resource_name, url: registration_path(resource_name), html: { method: :patch } do |f| %>
486
+ <div class="form-group">
487
+ <%= f.label :current_password %>
488
+ <span class="help-block form-help-adjust-margin">
489
+ <small>
490
+ Supply your current password to make any changes
491
+ </small>
492
+ </span>
493
+ <%= f.password_field :current_password, maxlength: 128, class: 'form-control',
494
+ data: {
495
+ 'rule-required' => 'true',
496
+ 'rule-minlength' => '8',
497
+ 'rule-maxlength' => '128'
498
+ } %>
499
+ </div>
500
+ <div class="form-group">
501
+ <%= f.label :email %>
502
+ <%= f.email_field :email, class: 'form-control', maxlength: 254, autofocus: true %>
503
+ </div>
504
+ <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
505
+ <h3>Currently waiting confirmation for: <%= resource.unconfirmed_email %></h3>
506
+ <% end %>
507
+ <div class="form-group">
508
+ <%= f.label :password %>
509
+ <span class="help-block form-help-adjust-margin">
510
+ <small>
511
+ Leave this blank if you do not want to change it
512
+ </small>
513
+ </span>
514
+ <%= f.password_field :password, class: 'form-control' %>
515
+ </div>
516
+ <%= button_tag type: 'submit', class: 'btn btn-primary' do %>
517
+ Save
518
+ <% end %>
519
+ <% end %>
520
+ </div>
521
+ <div class="col-sm-6">
522
+ <p>
523
+ Unhappy? <%= button_to 'Cancel my account', registration_path(resource_name), method: :delete %>
524
+ </p>
525
+ </div>
526
+ </div>
527
+ HTML
528
+ end
529
+
530
+ file 'app/views/devise/registrations/new.html.erb' do <<-HTML
531
+ <%
532
+ title 'Register a new account'
533
+ meta_description '...'
534
+ heading 'Register a new account'
535
+ %>
536
+
537
+ <div class="row">
538
+ <div class="col-sm-4">
539
+ <%= form_for resource, as: resource_name, url: registration_path(resource_name) do |f| %>
540
+ <div class="form-group">
541
+ <%= f.label :email %>
542
+ <%= f.email_field :email, class: 'form-control', maxlength: 254, autofocus: true %>
543
+ </div>
544
+ <div class="form-group">
545
+ <%= f.label :password %>
546
+ <%= f.password_field :password, maxlength: 128, class: 'form-control',
547
+ data: {
548
+ 'rule-required' => 'true',
549
+ 'rule-minlength' => '8',
550
+ 'rule-maxlength' => '128'
551
+ } %>
552
+ </div>
553
+ <%= button_tag type: 'submit', class: 'btn btn-primary' do %>
554
+ Register
555
+ <% end %>
556
+ <% end %>
557
+ </div>
558
+ <div class="col-sm-6 col-sm-offset-2">
559
+ <%= render 'devise/shared/links' %>
560
+ </div>
561
+ </div>
562
+ HTML
563
+ end
564
+
565
+ file 'app/views/devise/sessions/new.html.erb' do <<-HTML
566
+ <%
567
+ title 'Sign in'
568
+ meta_description '...'
569
+ heading 'Sign in'
570
+ %>
571
+
572
+ <div class="row">
573
+ <div class="col-sm-4">
574
+ <%= form_for resource, as: resource_name, url: session_path(resource_name) do |f| %>
575
+ <div class="form-group">
576
+ <%= f.label :email %>
577
+ <%= f.email_field :email, maxlength: 254, class: 'form-control', autofocus: true %>
578
+ </div>
579
+ <div class="form-group">
580
+ <%= f.label :password %>
581
+ <%= f.password_field :password, maxlength: 128, class: 'form-control',
582
+ data: {
583
+ 'rule-required' => 'true',
584
+ 'rule-minlength' => '8',
585
+ 'rule-maxlength' => '128'
586
+ } %>
587
+ </div>
588
+ <% if devise_mapping.rememberable? -%>
589
+ <div class="checkbox">
590
+ <%= f.label :remember_me do %>
591
+ <%= f.check_box :remember_me %> Stay signed in
592
+ <% end %>
593
+ </div>
594
+ <% end -%>
595
+ <%= button_tag type: 'submit', class: 'btn btn-primary' do %>
596
+ Sign in
597
+ <% end %>
598
+ <% end %>
599
+ </div>
600
+ <div class="col-sm-6 col-sm-offset-2">
601
+ <h4 class="success-color">Having trouble accessing your account?</h4>
602
+ <%= render 'devise/shared/links' %>
603
+ </div>
604
+ </div>
605
+ HTML
606
+ end
607
+
608
+ file 'app/views/devise/unlocks/new.html.erb' do <<-HTML
609
+ <%
610
+ title 'Re-send unlock instructions'
611
+ meta_description '...'
612
+ heading 'Re-send unlock instructions'
613
+ %>
614
+
615
+ <div class="row">
616
+ <div class="col-sm-4">
617
+ <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
618
+ <div class="form-group">
619
+ <%= f.label :email %>
620
+ <%= f.email_field :email, class: 'form-control', maxlength: 254, autofocus: true,
621
+ data: {
622
+ 'rule-required' => 'true',
623
+ 'rule-maxlength' => '254'
624
+ } %>
625
+ </div>
626
+ <%= button_tag type: 'submit', class: 'btn btn-primary' do %>
627
+ Send
628
+ <% end %>
629
+ <% end %>
630
+ </div>
631
+ <div class="col-sm-6 col-sm-offset-2">
632
+ <%= render 'devise/shared/links' %>
633
+ </div>
634
+ </div>
635
+ HTML
636
+ end
637
+
638
+ file 'app/views/devise/shared/_links.html.erb' do <<-'HTML'
639
+ <%= content_tag(:h4, 'Or do something else') if controller_name != 'sessions' %>
640
+ <ul>
641
+ <%- if controller_name != 'sessions' %>
642
+ <li>
643
+ <%= link_to 'Sign in', new_session_path(resource_name) %>
644
+ </li>
645
+ <% end -%>
646
+
647
+ <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
648
+ <li>
649
+ <%= link_to 'Sign up', new_registration_path(resource_name) %>
650
+ </li>
651
+ <% end -%>
652
+
653
+ <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
654
+ <li>
655
+ <%= link_to 'Forgot your password?', new_password_path(resource_name) %>
656
+ </li>
657
+ <% end -%>
658
+
659
+ <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
660
+ <li>
661
+ <%= link_to 'Re-send confirmation instructions?', new_confirmation_path(resource_name) %>
662
+ </li>
663
+ <% end -%>
664
+
665
+ <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
666
+ <li>
667
+ <%= link_to 'Are you locked out of your account?', new_unlock_path(resource_name) %>
668
+ </li>
669
+ <% end -%>
670
+
671
+ <%- if devise_mapping.omniauthable? %>
672
+ <%- resource_class.omniauth_providers.each do |provider| %>
673
+ <li><%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %></li>
674
+ <% end -%>
675
+ <% end -%>
676
+ </ul>
677
+ HTML
678
+ end
679
+
680
+ git add: '.'
681
+ git commit: "-m 'Add the devise views'"
682
+
683
+ # ----- Modify the layout files ------------------------------------------------------------------------
684
+
685
+ puts
686
+ say_status 'views', 'Modifying the layout files...', :yellow
687
+ puts '-'*80, ''; sleep 0.25
688
+
689
+ file 'app/views/layouts/_navigation_auth.html.erb', <<-HTML
690
+ <% if current_account %>
691
+ <li>
692
+ <%= link_to 'Settings', edit_account_registration_path %>
693
+ </li>
694
+ <li>
695
+ <%= link_to 'Sign out', destroy_account_session_path, method: :delete %>
696
+ </li>
697
+ <% else %>
698
+ <li>
699
+ <%= link_to 'Sign in', new_account_session_path %>
700
+ </li>
701
+ <li>
702
+ <%= link_to 'Register', new_account_registration_path %>
703
+ </li>
704
+ <% end %>
705
+ HTML
706
+
707
+ inject_into_file 'app/views/layouts/_navigation.html.erb', after: "</ul>\n" do <<-CODE
708
+ <ul class="nav navbar-nav nav-auth">
709
+ <%= render 'layouts/navigation_auth' %>
710
+ </ul>
711
+ CODE
712
+ end
713
+
714
+ append_file 'app/assets/stylesheets/application.css.scss' do <<-CODE
715
+
716
+ @media (min-width: $screen-sm) {
717
+ .nav-auth {
718
+ float: right;
719
+ }
720
+ }
721
+ CODE
722
+ end
723
+
724
+ git add: '.'
725
+ git commit: "-m 'Add account management links to the layout and add the necessary css selectors'"
726
+
727
+ # ----- Modify the .env file --------------------------------------------------------------------------
728
+
729
+ puts
730
+ say_status 'root', 'Modifying the .env file...', :yellow
731
+ puts '-'*80, ''; sleep 0.25
732
+
733
+ inject_into_file '.env', before: "\n#{app_name_upper}_SMTP_ADDRESS" do <<-CODE
734
+ #{app_name_upper}_TOKEN_DEVISE_SECRET: #{generate_token}
735
+ #{app_name_upper}_TOKEN_DEVISE_PEPPER: #{generate_token}
736
+ CODE
737
+ end
738
+
739
+ inject_into_file '.env', before: "\n#{app_name_upper}_DATABASE_NAME" do <<-CODE
740
+ #{app_name_upper}_ACTION_MAILER_DEVISE_DEFAULT_EMAIL: info@#{app_name}.com
741
+ CODE
742
+ end
743
+
744
+ git add: '.'
745
+ git commit: "-m 'Add the devise tokens and default email to the .env file'"
746
+
747
+ # ----- Create the config files -----------------------------------------------------------------------
748
+
749
+ puts
750
+ say_status 'config', 'Creating the devise async initializer...', :yellow
751
+ puts '-'*80, ''; sleep 0.25
752
+
753
+ file 'config/initializers/devise_async.rb', 'Devise::Async.backend = :sidekiq'
754
+ generate 'devise:install'
755
+
756
+ git add: '.'
757
+ git commit: "-m 'Add the devise and devise async initializers'"
758
+
759
+ # ----- Modify the config files -----------------------------------------------------------------------
760
+
761
+ puts
762
+ say_status 'config', 'Modifying the devise initializer...', :yellow
763
+ puts '-'*80, ''; sleep 0.25
764
+
765
+ gsub_file 'config/initializers/devise.rb',
766
+ "'please-change-me-at-config-initializers-devise@example.com'", "ENV['#{app_name_upper}_ACTION_MAILER_DEVISE_DEFAULT_EMAIL']"
767
+ gsub_file 'config/initializers/devise.rb', /(?<=key = )'\w{128}'/, "ENV['#{app_name_upper}_TOKEN_DEVISE_SECRET']"
768
+ gsub_file 'config/initializers/devise.rb', /(?<=pepper = )'\w{128}'/, "ENV['#{app_name_upper}_TOKEN_DEVISE_PEPPER']"
769
+
770
+ gsub_file 'config/initializers/devise.rb', '# config.timeout_in = 30.minutes',
771
+ 'config.timeout_in = 2.hours'
772
+
773
+ gsub_file 'config/initializers/devise.rb', '# config.expire_auth_token_on_timeout = false',
774
+ 'config.expire_auth_token_on_timeout = true'
775
+
776
+ gsub_file 'config/initializers/devise.rb', '# config.lock_strategy = :failed_attempts',
777
+ 'config.lock_strategy = :failed_attempts'
778
+
779
+ gsub_file 'config/initializers/devise.rb', '# config.unlock_strategy = :both',
780
+ 'config.unlock_strategy = :both'
781
+
782
+ gsub_file 'config/initializers/devise.rb', '# config.maximum_attempts = 20',
783
+ 'config.maximum_attempts = 7'
784
+
785
+ gsub_file 'config/initializers/devise.rb', '# config.unlock_in = 1.hour',
786
+ 'config.unlock_in = 2.hours'
787
+
788
+ gsub_file 'config/initializers/devise.rb', '# config.last_attempt_warning = false',
789
+ 'config.last_attempt_warning = true'
790
+
791
+ git add: '.'
792
+ git commit: "-m 'Change the devise initializer default values'"
793
+
794
+ # ----- Modify the routes file ------------------------------------------------------------------------
795
+
796
+ puts
797
+ say_status 'config', 'Modifying the routes file...', :yellow
798
+ puts '-'*80, ''; sleep 0.25
799
+
800
+ inject_into_file 'config/routes.rb', after: "collection\n end\n" do <<-CODE
801
+
802
+ # disable users from being able to register by uncommenting the lines below
803
+ # get 'accounts/sign_up(.:format)', to: redirect('/')
804
+ # post 'accounts(.:format)', to: redirect('/')
805
+
806
+ # disable users from deleting their own account by uncommenting the line below
807
+ # delete 'accounts(.:format)', to: redirect('/')
808
+
809
+ devise_for :accounts
810
+
811
+ authenticate :account, lambda { |account| account.is?(:admin) } do
812
+ mount Sidekiq::Web => '/sidekiq'
813
+ end
814
+
815
+ CODE
816
+ end
817
+
818
+ git add: '.'
819
+ git commit: "-m 'Add devise to the routes file'"
820
+
821
+ # ----- Add pundit support ----------------------------------------------------------------------------
822
+
823
+ puts
824
+ say_status 'root', 'Adding pundit support...', :yellow
825
+ puts '-'*80, ''; sleep 0.25
826
+
827
+ generate 'pundit:install'
828
+
829
+ git add: '.'
830
+ git commit: "-m 'Add pundit application policy'"
831
+
832
+ inject_into_file 'app/controllers/application_controller.rb', after: "::Base\n" do <<-'CODE'
833
+ include Pundit
834
+
835
+ CODE
836
+ end
837
+
838
+ inject_into_file 'app/controllers/application_controller.rb', after: ":exception\n" do <<-'CODE'
839
+
840
+ rescue_from Pundit::NotAuthorizedError, with: :account_not_authorized
841
+ CODE
842
+ end
843
+
844
+ inject_into_file 'app/controllers/application_controller.rb', after: " #end\n" do <<-'CODE'
845
+
846
+ def account_not_authorized
847
+ redirect_to request.headers['Referer'] || root_path, flash: { error: I18n.t('authorization.error') }
848
+ end
849
+ CODE
850
+ end
851
+
852
+ git add: '.'
853
+ git commit: "-m 'Add pundit logic to the application controller'"