authentication-zero 2.16.11 → 2.16.13
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/Gemfile.lock +1 -1
- data/README.md +4 -2
- data/lib/authentication_zero/version.rb +1 -1
- data/lib/generators/authentication/authentication_generator.rb +26 -3
- data/lib/generators/authentication/templates/controllers/api/sessions_controller.rb.tt +0 -3
- data/lib/generators/authentication/templates/controllers/html/application_controller.rb.tt +6 -0
- data/lib/generators/authentication/templates/controllers/html/invitations_controller.rb.tt +1 -1
- data/lib/generators/authentication/templates/controllers/html/sessions/omniauth_controller.rb.tt +5 -5
- data/lib/generators/authentication/templates/controllers/html/sessions/sudos_controller.rb.tt +14 -0
- data/lib/generators/authentication/templates/erb/sessions/sudos/new.html.erb.tt +28 -0
- data/lib/generators/authentication/templates/models/session.rb.tt +8 -11
- data/lib/generators/authentication/templates/test_unit/controllers/api/sessions_controller_test.rb.tt +0 -2
- data/lib/generators/authentication/templates/test_unit/controllers/html/sessions_controller_test.rb.tt +0 -2
- metadata +4 -5
- data/lib/generators/authentication/templates/erb/session_mailer/signed_in_notification.html.erb.tt +0 -21
- data/lib/generators/authentication/templates/mailers/session_mailer.rb.tt +0 -6
- data/lib/generators/authentication/templates/test_unit/mailers/session_mailer_test.rb.tt +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd4aba188e6a8763ada4f0cc90f370e8980afd0903e3e5f17a5f4bcc5db7c8d6
|
4
|
+
data.tar.gz: 8e16e20f5ed9d2cf1aadd469bdeffae20e1c51ab030bb5731697ae37dd749079
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc0688a6b1fae46b456d7c7ce473f1a4414f13bf9bc29439c6d687a67e9122d58eb76e592ef2796c79f1b0e19816e66e6a92f7278dc9df31c9595382108016ce
|
7
|
+
data.tar.gz: 06d51e04679f0821a4cb296f2e06f588c0614b75ae1f8ca6ce370cbca494d950280b617e8f6346539ddbe8d412838c9fdb9fceb4b6315d47b3471e30b9b7cd19
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## Authentication Zero 2.16.13 ##
|
2
|
+
|
3
|
+
* Enable resend invitation
|
4
|
+
* Refactor first_or_initialize -> find_or_initialize_by
|
5
|
+
|
6
|
+
## Authentication Zero 2.16.12 ##
|
7
|
+
|
8
|
+
* Bring back --sudoable, just for html and you should set before_action yourself
|
9
|
+
* Bring back --ratelimit
|
10
|
+
* Removed signed in email notification
|
11
|
+
|
1
12
|
## Authentication Zero 2.16.11 ##
|
2
13
|
|
3
14
|
* Added sending invitation
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -33,11 +33,12 @@ Since Authentication Zero generates this code into your application instead of b
|
|
33
33
|
- Send invitations (--invitable)
|
34
34
|
- Verify email using a link with token
|
35
35
|
- Verify email using a six random digits code for api (--code-verifiable)
|
36
|
+
- Ask password before sensitive data changes, aka: sudo (--sudoable)
|
36
37
|
- Reset the user password and send reset instructions
|
37
38
|
- Reset the user password only from verified emails
|
38
|
-
- Lock mechanism
|
39
|
+
- Lock mechanism to prevent spamming (--lockable)
|
40
|
+
- Rate limiting for your app, 1000 reqs/minute (--ratelimit)
|
39
41
|
- Send e-mail confirmation when your email has been changed
|
40
|
-
- Send e-mail notification when someone has logged into your account
|
41
42
|
- Manage multiple sessions & devices
|
42
43
|
- Activity log (--trackable)
|
43
44
|
- Log out
|
@@ -53,6 +54,7 @@ Since Authentication Zero generates this code into your application instead of b
|
|
53
54
|
- [log filtering](https://guides.rubyonrails.org/action_controller_overview.html#log-filtering): Parameters 'token' and 'password' are marked [FILTERED] in the log.
|
54
55
|
- [functional tests](https://guides.rubyonrails.org/testing.html#functional-tests-for-your-controllers): In Rails, testing the various actions of a controller is a form of writing functional tests.
|
55
56
|
- [system testing](https://guides.rubyonrails.org/testing.html#system-testing): System tests allow you to test user interactions with your application, running tests in either a real or a headless browser.
|
57
|
+
- **sudoable**: Use `before_action :require_sudo` in controllers with sensitive information, it will ask for your password on the first access or after 30 minutes.
|
56
58
|
|
57
59
|
## Development
|
58
60
|
|
@@ -6,7 +6,9 @@ class AuthenticationGenerator < Rails::Generators::Base
|
|
6
6
|
class_option :api, type: :boolean, desc: "Generates API authentication"
|
7
7
|
class_option :pwned, type: :boolean, desc: "Add pwned password validation"
|
8
8
|
class_option :code_verifiable, type: :boolean, desc: "Add email verification using a code for api"
|
9
|
+
class_option :sudoable, type: :boolean, desc: "Add password request before sensitive data changes"
|
9
10
|
class_option :lockable, type: :boolean, desc: "Add password reset locking"
|
11
|
+
class_option :ratelimit, type: :boolean, desc: "Add request rate limiting"
|
10
12
|
class_option :passwordless, type: :boolean, desc: "Add passwordless sign"
|
11
13
|
class_option :omniauthable, type: :boolean, desc: "Add social login support"
|
12
14
|
class_option :trackable, type: :boolean, desc: "Add activity log support"
|
@@ -18,6 +20,10 @@ class AuthenticationGenerator < Rails::Generators::Base
|
|
18
20
|
def add_gems
|
19
21
|
gem "bcrypt", "~> 3.1.7", comment: "Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]"
|
20
22
|
|
23
|
+
if options.ratelimit?
|
24
|
+
gem "rack-ratelimit", group: :production, comment: "Use Rack::Ratelimit to rate limit requests [https://github.com/jeremy/rack-ratelimit]"
|
25
|
+
end
|
26
|
+
|
21
27
|
if redis?
|
22
28
|
gem "redis", ">= 4.0.1", comment: "Use Redis adapter to run additional authentication features"
|
23
29
|
gem "kredis", comment: "Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis]"
|
@@ -41,6 +47,7 @@ class AuthenticationGenerator < Rails::Generators::Base
|
|
41
47
|
def add_environment_configurations
|
42
48
|
application "config.action_mailer.default_url_options = { host: \"localhost\", port: 3000 }", env: "development"
|
43
49
|
application "config.action_mailer.default_url_options = { host: \"localhost\", port: 3000 }", env: "test"
|
50
|
+
environment ratelimit_block, env: "production" if options.ratelimit?
|
44
51
|
end
|
45
52
|
|
46
53
|
def create_configuration_files
|
@@ -81,6 +88,7 @@ class AuthenticationGenerator < Rails::Generators::Base
|
|
81
88
|
template "controllers/#{format_folder}/invitations_controller.rb", "app/controllers/invitations_controller.rb" if invitable?
|
82
89
|
template "controllers/#{format_folder}/registrations_controller.rb", "app/controllers/registrations_controller.rb"
|
83
90
|
template "controllers/#{format_folder}/home_controller.rb", "app/controllers/home_controller.rb" unless options.api?
|
91
|
+
template "controllers/#{format_folder}/sessions/sudos_controller.rb", "app/controllers/sessions/sudos_controller.rb" if sudoable?
|
84
92
|
template "controllers/#{format_folder}/sessions/omniauth_controller.rb", "app/controllers/sessions/omniauth_controller.rb" if omniauthable?
|
85
93
|
template "controllers/#{format_folder}/sessions/passwordlesses_controller.rb", "app/controllers/sessions/passwordlesses_controller.rb" if passwordless?
|
86
94
|
template "controllers/#{format_folder}/authentications/events_controller.rb", "app/controllers/authentications/events_controller.rb" if options.trackable?
|
@@ -89,10 +97,8 @@ class AuthenticationGenerator < Rails::Generators::Base
|
|
89
97
|
def create_views
|
90
98
|
if options.api?
|
91
99
|
directory "erb/user_mailer", "app/views/user_mailer"
|
92
|
-
directory "erb/session_mailer", "app/views/session_mailer"
|
93
100
|
else
|
94
101
|
directory "erb/user_mailer", "app/views/user_mailer"
|
95
|
-
directory "erb/session_mailer", "app/views/session_mailer"
|
96
102
|
|
97
103
|
directory "erb/home", "app/views/home"
|
98
104
|
|
@@ -105,6 +111,8 @@ class AuthenticationGenerator < Rails::Generators::Base
|
|
105
111
|
template "erb/sessions/index.html.erb", "app/views/sessions/index.html.erb"
|
106
112
|
template "erb/sessions/new.html.erb", "app/views/sessions/new.html.erb"
|
107
113
|
|
114
|
+
directory "erb/sessions/sudos", "app/views/sessions/sudos" if sudoable?
|
115
|
+
|
108
116
|
directory "erb/sessions/passwordlesses", "app/views/sessions/passwordlesses" if passwordless?
|
109
117
|
|
110
118
|
directory "erb/two_factor_authentication", "app/views/two_factor_authentication" if two_factor?
|
@@ -119,6 +127,10 @@ class AuthenticationGenerator < Rails::Generators::Base
|
|
119
127
|
def add_routes
|
120
128
|
route "root 'home#index'" unless options.api?
|
121
129
|
|
130
|
+
if sudoable?
|
131
|
+
route "resource :sudo, only: [:new, :create]", namespace: :sessions
|
132
|
+
end
|
133
|
+
|
122
134
|
if passwordless?
|
123
135
|
route "resource :passwordless, only: [:new, :edit, :create]", namespace: :sessions
|
124
136
|
end
|
@@ -163,6 +175,13 @@ class AuthenticationGenerator < Rails::Generators::Base
|
|
163
175
|
options.api? ? "api" : "html"
|
164
176
|
end
|
165
177
|
|
178
|
+
def ratelimit_block
|
179
|
+
<<~CODE
|
180
|
+
# Rate limit general requests by IP address in a rate of 1000 requests per minute
|
181
|
+
config.middleware.use(Rack::Ratelimit, name: "General", rate: [1000, 1.minute], redis: Redis.new, logger: Rails.logger) { |env| ActionDispatch::Request.new(env).ip }
|
182
|
+
CODE
|
183
|
+
end
|
184
|
+
|
166
185
|
def omniauthable?
|
167
186
|
options.omniauthable? && !options.api?
|
168
187
|
end
|
@@ -179,11 +198,15 @@ class AuthenticationGenerator < Rails::Generators::Base
|
|
179
198
|
options.invitable? && !options.api?
|
180
199
|
end
|
181
200
|
|
201
|
+
def sudoable?
|
202
|
+
options.sudoable? && !options.api?
|
203
|
+
end
|
204
|
+
|
182
205
|
def code_verifiable?
|
183
206
|
options.code_verifiable? && options.api?
|
184
207
|
end
|
185
208
|
|
186
209
|
def redis?
|
187
|
-
options.lockable? || code_verifiable?
|
210
|
+
options.lockable? || options.ratelimit? || sudoable? || code_verifiable?
|
188
211
|
end
|
189
212
|
end
|
@@ -1,9 +1,6 @@
|
|
1
1
|
class SessionsController < ApplicationController
|
2
2
|
skip_before_action :authenticate, only: :create
|
3
3
|
|
4
|
-
<%- if options.lockable? -%>
|
5
|
-
before_action :require_lock, attempts: 20, only: :create
|
6
|
-
<%- end -%>
|
7
4
|
before_action :set_session, only: %i[ show destroy ]
|
8
5
|
|
9
6
|
def index
|
@@ -1,6 +1,12 @@
|
|
1
1
|
class ApplicationController < ActionController::Base
|
2
2
|
before_action :set_current_request_details
|
3
3
|
before_action :authenticate
|
4
|
+
<%- if sudoable? %>
|
5
|
+
def require_sudo
|
6
|
+
return if Current.session.sudo?
|
7
|
+
redirect_to new_sessions_sudo_path(proceed_to_url: request.url)
|
8
|
+
end
|
9
|
+
<%- end -%>
|
4
10
|
<%- if options.lockable? %>
|
5
11
|
def require_lock(wait: 1.hour, attempts: 10)
|
6
12
|
counter = Kredis.counter("require_lock:#{request.remote_ip}:#{controller_path}:#{action_name}", expires_in: wait)
|
data/lib/generators/authentication/templates/controllers/html/sessions/omniauth_controller.rb.tt
CHANGED
@@ -3,7 +3,7 @@ class Sessions::OmniauthController < ApplicationController
|
|
3
3
|
skip_before_action :authenticate
|
4
4
|
|
5
5
|
def create
|
6
|
-
@user = User.
|
6
|
+
@user = User.create_with(user_params).find_or_initialize_by(omniauth_params)
|
7
7
|
|
8
8
|
if @user.save
|
9
9
|
session = @user.sessions.create!
|
@@ -20,14 +20,14 @@ class Sessions::OmniauthController < ApplicationController
|
|
20
20
|
end
|
21
21
|
|
22
22
|
private
|
23
|
-
def omniauth_params
|
24
|
-
{ provider: omniauth.provider, uid: omniauth.uid }
|
25
|
-
end
|
26
|
-
|
27
23
|
def user_params
|
28
24
|
{ email: omniauth.info.email, password: SecureRandom::base58, verified: true }
|
29
25
|
end
|
30
26
|
|
27
|
+
def omniauth_params
|
28
|
+
{ provider: omniauth.provider, uid: omniauth.uid }
|
29
|
+
end
|
30
|
+
|
31
31
|
def omniauth
|
32
32
|
request.env["omniauth.auth"]
|
33
33
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Sessions::SudosController < ApplicationController
|
2
|
+
def new
|
3
|
+
end
|
4
|
+
|
5
|
+
def create
|
6
|
+
session = Current.session
|
7
|
+
|
8
|
+
if session.user.authenticate(params[:password])
|
9
|
+
session.sudo.mark; redirect_to(params[:proceed_to_url])
|
10
|
+
else
|
11
|
+
redirect_to new_sessions_sudo_path(proceed_to_url: params[:proceed_to_url]), alert: "The password you entered is incorrect"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<p style="color: red"><%%= alert %></p>
|
2
|
+
|
3
|
+
<h1>Enter your password to continue</h1>
|
4
|
+
|
5
|
+
<%%= form_with(url: sessions_sudo_path) do |form| %>
|
6
|
+
|
7
|
+
<%%= form.hidden_field :proceed_to_url, value: params[:proceed_to_url] %>
|
8
|
+
|
9
|
+
<div>
|
10
|
+
<%%= form.password_field :password, required: true, autofocus: true, autocomplete: "current-password" %>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<div>
|
14
|
+
<%%= form.submit "Continue" %>
|
15
|
+
</div>
|
16
|
+
<%% end %>
|
17
|
+
|
18
|
+
<br>
|
19
|
+
|
20
|
+
<p>
|
21
|
+
<strong>Why are you asking me to do this?</strong><br>
|
22
|
+
To better protect your account, we'll occasionally ask you to confirm your password before performing sensitive actions.
|
23
|
+
</p>
|
24
|
+
|
25
|
+
<p>
|
26
|
+
<strong>Forgot your password?</strong><br>
|
27
|
+
We'll help you <%%= link_to "reset it", new_identity_password_reset_path %> so you can continue.
|
28
|
+
</p>
|
@@ -1,21 +1,18 @@
|
|
1
1
|
class Session < ApplicationRecord
|
2
2
|
belongs_to :user
|
3
|
+
<%- if sudoable? %>
|
4
|
+
kredis_flag :sudo, expires_in: 30.minutes
|
5
|
+
<%- end -%>
|
3
6
|
|
4
7
|
before_create do
|
5
8
|
self.user_agent = Current.user_agent
|
6
9
|
self.ip_address = Current.ip_address
|
7
10
|
end
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
11
|
+
<%- if sudoable? %>
|
12
|
+
after_create { sudo.mark }
|
13
|
+
<%- end -%>
|
12
14
|
<%- if options.trackable? %>
|
13
|
-
after_create
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
after_destroy do
|
18
|
-
user.events.create! action: "signed_out"
|
19
|
-
end
|
15
|
+
after_create { user.events.create! action: "signed_in" }
|
16
|
+
after_destroy { user.events.create! action: "signed_out" }
|
20
17
|
<%- end -%>
|
21
18
|
end
|
@@ -21,8 +21,6 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
|
|
21
21
|
|
22
22
|
test "should sign in" do
|
23
23
|
post sign_in_url, params: { email: @user.email, password: "Secret1*3*5*" }
|
24
|
-
|
25
|
-
assert_enqueued_email_with SessionMailer, :signed_in_notification, args: { session: @user.sessions.last }
|
26
24
|
assert_response :created
|
27
25
|
end
|
28
26
|
|
@@ -19,8 +19,6 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
|
|
19
19
|
|
20
20
|
test "should sign in" do
|
21
21
|
post sign_in_url, params: { email: @user.email, password: "Secret1*3*5*" }
|
22
|
-
assert_enqueued_email_with SessionMailer, :signed_in_notification, args: { session: @user.sessions.last }
|
23
|
-
|
24
22
|
assert_redirected_to root_url
|
25
23
|
|
26
24
|
get root_url
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: authentication-zero
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.16.
|
4
|
+
version: 2.16.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nixon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04-
|
11
|
+
date: 2023-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -56,6 +56,7 @@ files:
|
|
56
56
|
- lib/generators/authentication/templates/controllers/html/registrations_controller.rb.tt
|
57
57
|
- lib/generators/authentication/templates/controllers/html/sessions/omniauth_controller.rb.tt
|
58
58
|
- lib/generators/authentication/templates/controllers/html/sessions/passwordlesses_controller.rb.tt
|
59
|
+
- lib/generators/authentication/templates/controllers/html/sessions/sudos_controller.rb.tt
|
59
60
|
- lib/generators/authentication/templates/controllers/html/sessions_controller.rb.tt
|
60
61
|
- lib/generators/authentication/templates/controllers/html/two_factor_authentication/challenges_controller.rb.tt
|
61
62
|
- lib/generators/authentication/templates/controllers/html/two_factor_authentication/totps_controller.rb.tt
|
@@ -67,17 +68,16 @@ files:
|
|
67
68
|
- lib/generators/authentication/templates/erb/invitations/new.html.erb.tt
|
68
69
|
- lib/generators/authentication/templates/erb/passwords/edit.html.erb.tt
|
69
70
|
- lib/generators/authentication/templates/erb/registrations/new.html.erb.tt
|
70
|
-
- lib/generators/authentication/templates/erb/session_mailer/signed_in_notification.html.erb.tt
|
71
71
|
- lib/generators/authentication/templates/erb/sessions/index.html.erb.tt
|
72
72
|
- lib/generators/authentication/templates/erb/sessions/new.html.erb.tt
|
73
73
|
- lib/generators/authentication/templates/erb/sessions/passwordlesses/new.html.erb.tt
|
74
|
+
- lib/generators/authentication/templates/erb/sessions/sudos/new.html.erb.tt
|
74
75
|
- lib/generators/authentication/templates/erb/two_factor_authentication/challenges/new.html.erb.tt
|
75
76
|
- lib/generators/authentication/templates/erb/two_factor_authentication/totps/new.html.erb.tt
|
76
77
|
- lib/generators/authentication/templates/erb/user_mailer/email_verification.html.erb.tt
|
77
78
|
- lib/generators/authentication/templates/erb/user_mailer/invitation_instructions.html.erb.tt
|
78
79
|
- lib/generators/authentication/templates/erb/user_mailer/password_reset.html.erb.tt
|
79
80
|
- lib/generators/authentication/templates/erb/user_mailer/passwordless.html.erb.tt
|
80
|
-
- lib/generators/authentication/templates/mailers/session_mailer.rb.tt
|
81
81
|
- lib/generators/authentication/templates/mailers/user_mailer.rb.tt
|
82
82
|
- lib/generators/authentication/templates/migrations/create_email_verification_tokens_migration.rb.tt
|
83
83
|
- lib/generators/authentication/templates/migrations/create_events_migration.rb.tt
|
@@ -105,7 +105,6 @@ files:
|
|
105
105
|
- lib/generators/authentication/templates/test_unit/controllers/html/passwords_controller_test.rb.tt
|
106
106
|
- lib/generators/authentication/templates/test_unit/controllers/html/registrations_controller_test.rb.tt
|
107
107
|
- lib/generators/authentication/templates/test_unit/controllers/html/sessions_controller_test.rb.tt
|
108
|
-
- lib/generators/authentication/templates/test_unit/mailers/session_mailer_test.rb.tt
|
109
108
|
- lib/generators/authentication/templates/test_unit/mailers/user_mailer_test.rb.tt
|
110
109
|
- lib/generators/authentication/templates/test_unit/system/identity/emails_test.rb.tt
|
111
110
|
- lib/generators/authentication/templates/test_unit/system/identity/password_resets_test.rb.tt
|
data/lib/generators/authentication/templates/erb/session_mailer/signed_in_notification.html.erb.tt
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
<p>Hey there,</p>
|
2
|
-
|
3
|
-
<p>A new device just signed in to your account (<%%= @session.user.email %>).</p>
|
4
|
-
|
5
|
-
<p>
|
6
|
-
<strong><%%= @session.user_agent %></strong>
|
7
|
-
<br>
|
8
|
-
<%%= @session.created_at %>
|
9
|
-
<br>
|
10
|
-
IP address: <%%= @session.ip_address %>
|
11
|
-
</p>
|
12
|
-
|
13
|
-
<p><strong>If this was you, carry on.</strong> We could notify you about sign-ins from this device again.</p>
|
14
|
-
|
15
|
-
<p><strong>If you don't recognize this device</strong>, someone else may have accessed your account. You should immediately <%%= link_to "change your password", new_identity_password_reset_url %>.</p>
|
16
|
-
|
17
|
-
<p><strong>Tip:</strong> It's a good idea to periodically review all of the <%%= link_to "devices and sessions", sessions_url %> in your account for suspicious activity.</p>
|
18
|
-
|
19
|
-
<hr>
|
20
|
-
|
21
|
-
<p>Have questions or need help? Just reply to this email and our support team will help you sort it out.</p>
|
@@ -1,13 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class SessionMailerTest < ActionMailer::TestCase
|
4
|
-
setup do
|
5
|
-
@session = users(:lazaro_nixon).sessions.create!
|
6
|
-
end
|
7
|
-
|
8
|
-
test "signed_in_notification" do
|
9
|
-
mail = SessionMailer.with(session: @session).signed_in_notification
|
10
|
-
assert_equal "New sign-in to your account", mail.subject
|
11
|
-
assert_equal [@session.user.email], mail.to
|
12
|
-
end
|
13
|
-
end
|