authentication-zero 2.8.4 → 2.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +6 -0
  5. data/authentication-zero-api.md +210 -0
  6. data/lib/authentication_zero/version.rb +1 -1
  7. data/lib/generators/authentication/authentication_generator.rb +52 -19
  8. data/lib/generators/authentication/templates/controllers/api/authentications/events_controller.rb.tt +5 -0
  9. data/lib/generators/authentication/templates/controllers/api/sessions_controller.rb.tt +1 -5
  10. data/lib/generators/authentication/templates/controllers/html/authentications/events_controller.rb.tt +5 -0
  11. data/lib/generators/authentication/templates/controllers/html/registrations_controller.rb.tt +1 -5
  12. data/lib/generators/authentication/templates/controllers/{omniauth_controller.rb.tt → html/sessions/omniauth_controller.rb.tt} +1 -5
  13. data/lib/generators/authentication/templates/controllers/html/sessions_controller.rb.tt +1 -5
  14. data/lib/generators/authentication/templates/erb/authentications/events/index.html.erb +33 -0
  15. data/lib/generators/authentication/templates/erb/identity/password_resets/edit.html.erb.tt +2 -2
  16. data/lib/generators/authentication/templates/erb/passwords/edit.html.erb.tt +3 -3
  17. data/lib/generators/authentication/templates/erb/registrations/new.html.erb.tt +2 -2
  18. data/lib/generators/authentication/templates/erb/sessions/sudos/new.html.erb.tt +1 -1
  19. data/lib/generators/authentication/templates/migrations/create_events_migration.rb.tt +12 -0
  20. data/lib/generators/authentication/templates/migrations/create_sessions_migration.rb.tt +2 -2
  21. data/lib/generators/authentication/templates/migrations/create_table_migration.rb.tt +1 -1
  22. data/lib/generators/authentication/templates/models/current.rb.tt +1 -0
  23. data/lib/generators/authentication/templates/models/event.rb.tt +8 -0
  24. data/lib/generators/authentication/templates/models/model.rb.tt +16 -4
  25. data/lib/generators/authentication/templates/models/session.rb.tt +15 -0
  26. data/lib/generators/authentication/templates/test_unit/controllers/api/identity/email_verifications_controller_test.rb.tt +1 -1
  27. data/lib/generators/authentication/templates/test_unit/controllers/api/identity/emails_controller_test.rb.tt +1 -1
  28. data/lib/generators/authentication/templates/test_unit/controllers/api/passwords_controller_test.rb.tt +1 -1
  29. data/lib/generators/authentication/templates/test_unit/controllers/api/sessions/sudos_controller_test.rb.tt +1 -1
  30. data/lib/generators/authentication/templates/test_unit/controllers/api/sessions_controller_test.rb.tt +3 -3
  31. data/lib/generators/authentication/templates/test_unit/controllers/html/identity/email_verifications_controller_test.rb.tt +1 -1
  32. data/lib/generators/authentication/templates/test_unit/controllers/html/identity/emails_controller_test.rb.tt +1 -1
  33. data/lib/generators/authentication/templates/test_unit/controllers/html/passwords_controller_test.rb.tt +1 -1
  34. data/lib/generators/authentication/templates/test_unit/controllers/html/registrations_controller_test.rb.tt +1 -1
  35. data/lib/generators/authentication/templates/test_unit/controllers/html/sessions/sudos_controller_test.rb.tt +1 -1
  36. data/lib/generators/authentication/templates/test_unit/controllers/html/sessions_controller_test.rb.tt +3 -3
  37. data/lib/generators/authentication/templates/test_unit/system/sessions_test.rb.tt +7 -0
  38. metadata +9 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bfa3da3a6167d405cd7869b9cb90f7c61eee5ef1d03475d079d72362a3bedf04
4
- data.tar.gz: 3775ac3bd3fb334134618c9cbc8b32c25a5a3940246a554592e74415383de72b
3
+ metadata.gz: 1eb8eb320cf8198e9afebf59fd2365b2683e3b52840d8d455876a11b208e243c
4
+ data.tar.gz: 3bc190759683e6e647a597bdb57e09e69243901a3390c401e09eac72ada96726
5
5
  SHA512:
6
- metadata.gz: 4b596006ad41a57e3c3e54d21393ae2ceae9b21b36a11666f0988b2d59bda944c499d599074ced369cedbdb5cde98fc831f97691a3274d3467596904a7f2022c
7
- data.tar.gz: 7096373b36cf3c9dead056e3d0584f4e44eb97b6efa353e0b3a7f9399620eb59aa3c298e5016aef38138508c9e01d4d1c1e99d4918730e6f9d3eda0b8e035a6a
6
+ metadata.gz: 0c2996c6cf6779c22442f7155025bb99a470f26a0dc23218aea96f413fd5ae85a1ffdebd796922b77a09f60a833c4a0bb72bc9e964f5c0695066cf4074c3f4e9
7
+ data.tar.gz: 8cbf3eb8525e08fc10a5c00a88e1a82d928a107c969e7b6f4e65789d0db1d79b3a19369331f914b5baf439230a6b0e246287f6875f1f4ab7d57c4f470e96c0a0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## Authentication Zero 2.9.0 (March 2, 2022) ##
2
+
3
+ * Implement trackable
4
+
1
5
  ## Authentication Zero 2.8.0 (March 2, 2022) ##
2
6
 
3
7
  * Organize controllers in identity and sessions namespaces
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- authentication-zero (2.8.4)
4
+ authentication-zero (2.9.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -20,7 +20,9 @@ The purpose of authentication zero is to generate a pre-built authentication sys
20
20
  - Send e-mail confirmation when your email has been changed
21
21
  - Send e-mail notification when someone has logged into your account
22
22
  - Manage multiple sessions & devices
23
+ - Activity log (--trackable)
23
24
  - Log out
25
+ - [API documentation](https://github.com/lazaronixon/authentication-zero/blob/master/authentication-zero-api.md)
24
26
 
25
27
  ## Security and best practices
26
28
 
@@ -73,6 +75,10 @@ Add these lines to your `app/views/home/index.html.erb`:
73
75
  <%= link_to "Devices & Sessions", sessions_path %>
74
76
  </div>
75
77
 
78
+ <div>
79
+ <%# link_to "Activity Log", authentications_events_path %>
80
+ </div>
81
+
76
82
  <br>
77
83
 
78
84
  <%= button_to "Log out", Current.session, method: :delete %>
@@ -0,0 +1,210 @@
1
+ # Authentication Zero API
2
+
3
+ This document describe the api endpoints available in authentication-zero.
4
+
5
+ ## Making a request
6
+
7
+ To make a sign in request for example, append sign_in to the base URL to form something like http://localhost:3000/sign_in, also notice you have to include the Content-Type header and the JSON data: In cURL, it looks like this:
8
+
9
+ ``` shell
10
+ curl -H "Authorization: Bearer $ACCESS_TOKEN" \
11
+ -H 'Content-Type: application/json' \
12
+ -H 'User-Agent: MyApp (yourname@example.com)' \
13
+ -d '{ "email": "lazaronixon@hotmail.com", "password": "secret", "password_confirmation": "secret" }' \
14
+ http://localhost:3000/sign_in
15
+ ```
16
+
17
+ ## API endpoints
18
+
19
+ - [Sign up](#sign-up)
20
+ - [Sign in](#sign-in)
21
+ - [Get your sessions](#get-your-sessions)
22
+ - [Get a session](#get-a-session)
23
+ - [Destroy a session](#destroy-a-session)
24
+ - [Execute sudo](#execute-sudo)
25
+ - [Update your password](#update-your-password)
26
+ - [Update your email](#update-your-email)
27
+ - [Send verification email](#send-verification-email)
28
+ - [Verify email](#verify-email)
29
+ - [Send password reset email](#send-password-reset-email)
30
+ - [Reset password](#reset-password)
31
+
32
+ ## Registrations
33
+
34
+ ### Sign up
35
+
36
+ * `POST /sign_up` creates a user on database.
37
+
38
+ ###### Example JSON Request
39
+
40
+ ``` json
41
+ {
42
+ "email": "lazaronixon@hotmail.com",
43
+ "password": "Secret1*2*3*4*5*6",
44
+ "password_confirmation": "Secret1*2*3*4*5*6"
45
+ }
46
+ ```
47
+
48
+ This endpoint will return `201 Created` with the current JSON representation of the user if the creation was a success.
49
+
50
+ ## Sessions
51
+
52
+ ### Sign in
53
+
54
+ * `POST /sign_in` creates a session on database.
55
+
56
+ ###### Example JSON Request
57
+
58
+ ``` json
59
+ {
60
+ "email": "lazaronixon@hotmail.com",
61
+ "password": "Secret1*2*3*4*5*6"
62
+ }
63
+ ```
64
+
65
+ This endpoint will return `201 Created` with the current JSON representation of the session if the creation was a success, also you will receive a `X-Session-Token` that you will use as your authorization token.
66
+
67
+
68
+ ### Get your sessions
69
+
70
+ * `GET /sessions` will return a list of sessions.
71
+
72
+ ###### Example JSON Response
73
+
74
+ ``` json
75
+ [
76
+ {
77
+ "id": 2,
78
+ "user_id": 1,
79
+ "user_agent": "insomnia/2022.1.0",
80
+ "ip_address": "127.0.0.1",
81
+ "sudo_at": "2022-03-04T17:20:33.632Z",
82
+ "created_at": "2022-03-04T17:20:33.632Z",
83
+ "updated_at": "2022-03-04T17:20:33.632Z"
84
+ },
85
+ {
86
+ "id": 1,
87
+ "user_id": 1,
88
+ "user_agent": "insomnia/2022.1.0",
89
+ "ip_address": "127.0.0.1",
90
+ "sudo_at": "2022-03-04T17:14:03.386Z",
91
+ "created_at": "2022-03-04T17:14:03.386Z",
92
+ "updated_at": "2022-03-04T17:14:03.386Z"
93
+ }
94
+ ]
95
+ ```
96
+
97
+ ### Get a session
98
+
99
+ * `GET /sessions/1` will return the session with an ID of 1.
100
+
101
+ ###### Example JSON Response
102
+
103
+ ``` json
104
+ {
105
+ "id": 1,
106
+ "user_id": 1,
107
+ "user_agent": "insomnia/2022.1.0",
108
+ "ip_address": "127.0.0.1",
109
+ "sudo_at": "2022-03-04T17:14:03.386Z",
110
+ "created_at": "2022-03-04T17:14:03.386Z",
111
+ "updated_at": "2022-03-04T17:14:03.386Z"
112
+ }
113
+ ```
114
+
115
+ ### Destroy a session
116
+
117
+ * `DELETE /sessions/1` will destroy the session with an ID of 1.
118
+
119
+ Returns `204 No Content` if successful.
120
+
121
+
122
+ ### Execute sudo
123
+
124
+ * `POST /sessions/sudo` will grant temporary access to sensitive information.
125
+
126
+ ###### Example JSON Request
127
+
128
+ ``` json
129
+ {
130
+ "password": "Secret1*2*3*4*5*6",
131
+ }
132
+ ```
133
+
134
+ Returns `204 No Content` if successful.
135
+
136
+ ## Password
137
+
138
+ ### Update your password
139
+
140
+ * `PUT /password` allows changing your password.
141
+
142
+ ###### Example JSON Request
143
+
144
+ ``` json
145
+ {
146
+ "current_password": "Secret1*2*3*4*5*6",
147
+ "password": "NewPassword12$34$56$7",
148
+ "password_confirmation": "NewPassword12$34$56$7"
149
+ }
150
+ ```
151
+
152
+ This endpoint will return 200 OK with the current JSON representation of the user if the update was a success.
153
+
154
+ ## Email
155
+
156
+ ### Update your email
157
+
158
+ * `PUT /identity/email` allows changing your email. **(requires sudo)**.
159
+
160
+ ###### Example JSON Request
161
+
162
+ ``` json
163
+ {
164
+ "email": "new_email@hey.com"
165
+ }
166
+ ```
167
+
168
+ This endpoint will return 200 OK with the current JSON representation of the user if the update was a success.
169
+
170
+ ## Email verification
171
+
172
+ ### Send verification email
173
+
174
+ * `POST /identity/email_verification` sends an email verification with the instructions and link to proceed with the verification.
175
+
176
+ Returns `204 No Content` if successful.
177
+
178
+ ### Verify email
179
+
180
+ * `GET /identity/email_verification` verify your email using a temporary token.
181
+
182
+ **Required parameters:** `email` and `token`.
183
+
184
+ Example: `/identity/email_verification?email=lazaronixon@hotmail.com&token=eyJfcmFpbHMiOnsibWVzc2FnZSI6Ik1nPT0iLCJleHAiOm51bGwsInB1ciI6InNlc3Npb24ifX0=--1a277b4a5576c6e371144a22476979a18d3e45fb8515a79e815cd4b95eb5fb6b`
185
+
186
+ Returns `204 No Content` if successful.
187
+
188
+ ## Password reset
189
+
190
+ ### Send password reset email
191
+
192
+ * `POST /identity/password_reset` sends a password reset email with the instructions and link to proceed reset.
193
+
194
+ Returns `204 No Content` if successful.
195
+
196
+ ### Reset password
197
+
198
+ * `PUT /identity/password_reset` allows changing your password through a email token.
199
+
200
+ ##### Example JSON Request
201
+
202
+ ``` json
203
+ {
204
+ "password": "NewPassword12$34$56$7",
205
+ "password_confirmation": "NewPassword12$34$56$7",
206
+ "token": "eyJfcmFpbHMiOnsibWVzc2FnZSI6Ik1nPT0iLCJleHAiOm51bGwsInB1ciI6InNlc3Npb24ifX0=--1a277b4a5576c6e371144a22476979a18d3e45fb8515a79e815cd4b95eb5fb6b",
207
+ }
208
+ ```
209
+
210
+ This endpoint will return 200 OK with the current JSON representation of the user if the update was a success.
@@ -1,3 +1,3 @@
1
1
  module AuthenticationZero
2
- VERSION = "2.8.4"
2
+ VERSION = "2.9.2"
3
3
  end
@@ -8,6 +8,7 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
8
8
  class_option :lockable, type: :boolean, desc: "Add password reset locking"
9
9
  class_option :ratelimit, type: :boolean, desc: "Add request rate limiting"
10
10
  class_option :omniauthable, type: :boolean, desc: "Add social login support"
11
+ class_option :trackable, type: :boolean, desc: "Add activity log support"
11
12
 
12
13
  source_root File.expand_path("templates", __dir__)
13
14
 
@@ -47,6 +48,7 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
47
48
  def create_migrations
48
49
  migration_template "migrations/create_table_migration.rb", "#{db_migrate_path}/create_#{table_name}.rb"
49
50
  migration_template "migrations/create_sessions_migration.rb", "#{db_migrate_path}/create_sessions.rb"
51
+ migration_template "migrations/create_events_migration.rb", "#{db_migrate_path}/create_events.rb" if options.trackable?
50
52
  end
51
53
 
52
54
  def create_models
@@ -54,6 +56,7 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
54
56
  template "models/session.rb", "app/models/session.rb"
55
57
  template "models/current.rb", "app/models/current.rb"
56
58
  template "models/locking.rb", "app/models/locking.rb" if options.lockable?
59
+ template "models/event.rb", "app/models/event.rb" if options.trackable?
57
60
  end
58
61
 
59
62
  def create_fixture_file
@@ -64,39 +67,53 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
64
67
  api_code = <<~CODE
65
68
  include ActionController::HttpAuthentication::Token::ControllerMethods
66
69
 
70
+ before_action :set_current_request_details
67
71
  before_action :authenticate
68
72
 
69
- def authenticate
70
- if session = authenticate_with_http_token { |token, _| Session.find_signed(token) }
71
- Current.session = session
72
- else
73
- request_http_token_authentication
74
- end
75
- end
76
-
77
73
  def require_sudo
78
74
  if Current.session.sudo_at < 30.minutes.ago
79
75
  render json: { error: "Enter your password to continue" }, status: :forbidden
80
76
  end
81
77
  end
78
+
79
+ private
80
+ def authenticate
81
+ if session = authenticate_with_http_token { |token, _| Session.find_signed(token) }
82
+ Current.session = session
83
+ else
84
+ request_http_token_authentication
85
+ end
86
+ end
87
+
88
+ def set_current_request_details
89
+ Current.user_agent = request.user_agent
90
+ Current.ip_address = request.ip
91
+ end
82
92
  CODE
83
93
 
84
94
  html_code = <<~CODE
95
+ before_action :set_current_request_details
85
96
  before_action :authenticate
86
97
 
87
- def authenticate
88
- if session = Session.find_by_id(cookies.signed[:session_token])
89
- Current.session = session
90
- else
91
- redirect_to sign_in_path
92
- end
93
- end
94
-
95
98
  def require_sudo
96
99
  if Current.session.sudo_at < 30.minutes.ago
97
100
  redirect_to new_sessions_sudo_path(proceed_to_url: request.url)
98
101
  end
99
102
  end
103
+
104
+ private
105
+ def authenticate
106
+ if session = Session.find_by_id(cookies.signed[:session_token])
107
+ Current.session = session
108
+ else
109
+ redirect_to sign_in_path
110
+ end
111
+ end
112
+
113
+ def set_current_request_details
114
+ Current.user_agent = request.user_agent
115
+ Current.ip_address = request.ip
116
+ end
100
117
  CODE
101
118
 
102
119
  inject_code = options.api? ? api_code : html_code
@@ -104,8 +121,13 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
104
121
  end
105
122
 
106
123
  def create_controllers
107
- directory "controllers/#{format_folder}", "app/controllers"
108
- template "controllers/omniauth_controller.rb", "app/controllers/sessions/omniauth_controller.rb" if omniauthable?
124
+ directory "controllers/#{format_folder}/identity", "app/controllers/identity"
125
+ template "controllers/#{format_folder}/passwords_controller.rb", "app/controllers/passwords_controller.rb"
126
+ template "controllers/#{format_folder}/registrations_controller.rb", "app/controllers/registrations_controller.rb"
127
+ template "controllers/#{format_folder}/sessions_controller.rb", "app/controllers/sessions_controller.rb"
128
+ template "controllers/#{format_folder}/sessions/sudos_controller.rb", "app/controllers/sessions/sudos_controller.rb"
129
+ template "controllers/#{format_folder}/sessions/omniauth_controller.rb", "app/controllers/sessions/omniauth_controller.rb" if omniauthable?
130
+ template "controllers/#{format_folder}/authentications/events_controller.rb", "app/controllers/authentications/events_controller.rb" if options.trackable?
109
131
  end
110
132
 
111
133
  def create_views
@@ -113,7 +135,14 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
113
135
  directory "erb/identity_mailer", "app/views/identity_mailer"
114
136
  directory "erb/session_mailer", "app/views/session_mailer"
115
137
  else
116
- directory "erb", "app/views"
138
+ directory "erb/identity_mailer", "app/views/identity_mailer"
139
+ directory "erb/session_mailer", "app/views/session_mailer"
140
+
141
+ directory "erb/identity", "app/views/identity"
142
+ directory "erb/passwords", "app/views/passwords"
143
+ directory "erb/registrations", "app/views/registrations"
144
+ directory "erb/sessions", "app/views/sessions"
145
+ directory "erb/authentications/events", "app/views/authentications/events" if options.trackable?
117
146
  end
118
147
  end
119
148
 
@@ -128,6 +157,10 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
128
157
  route "get '/auth/failure', to: 'sessions/omniauth#failure'"
129
158
  end
130
159
 
160
+ if options.trackable?
161
+ route "resources :events, only: :index", namespace: :authentications
162
+ end
163
+
131
164
  route "resource :password_reset, only: [:new, :edit, :create, :update]", namespace: :identity
132
165
  route "resource :email_verification, only: [:edit, :create]", namespace: :identity
133
166
  route "resource :email, only: [:edit, :update]", namespace: :identity
@@ -0,0 +1,5 @@
1
+ class Authentications::EventsController < ApplicationController
2
+ def index
3
+ render json: Current.<%= singular_table_name %>.events.order(created_at: :desc)
4
+ end
5
+ end
@@ -15,7 +15,7 @@ class SessionsController < ApplicationController
15
15
  <%= singular_table_name %> = <%= class_name %>.find_by(email: params[:email])
16
16
 
17
17
  if <%= singular_table_name %> && <%= singular_table_name %>.authenticate(params[:password])
18
- @session = <%= singular_table_name %>.sessions.create!(session_params)
18
+ @session = <%= singular_table_name %>.sessions.create!
19
19
  response.set_header("X-Session-Token", @session.signed_id)
20
20
 
21
21
  render json: @session, status: :created
@@ -32,8 +32,4 @@ class SessionsController < ApplicationController
32
32
  def set_session
33
33
  @session = Current.<%= singular_table_name %>.sessions.find(params[:id])
34
34
  end
35
-
36
- def session_params
37
- { user_agent: request.user_agent, ip_address: request.remote_ip, sudo_at: Time.current }
38
- end
39
35
  end
@@ -0,0 +1,5 @@
1
+ class Authentications::EventsController < ApplicationController
2
+ def index
3
+ @events = Current.<%= singular_table_name %>.events.order(created_at: :desc)
4
+ end
5
+ end
@@ -9,7 +9,7 @@ class RegistrationsController < ApplicationController
9
9
  @<%= singular_table_name %> = <%= class_name %>.new(<%= "#{singular_table_name}_params" %>)
10
10
 
11
11
  if @<%= singular_table_name %>.save
12
- session = @<%= singular_table_name %>.sessions.create!(session_params)
12
+ session = @<%= singular_table_name %>.sessions.create!
13
13
  cookies.signed.permanent[:session_token] = { value: session.id, httponly: true }
14
14
 
15
15
  redirect_to root_path, notice: "Welcome! You have signed up successfully"
@@ -22,8 +22,4 @@ class RegistrationsController < ApplicationController
22
22
  def <%= "#{singular_table_name}_params" %>
23
23
  params.permit(:email, :password, :password_confirmation)
24
24
  end
25
-
26
- def session_params
27
- { user_agent: request.user_agent, ip_address: request.remote_ip, sudo_at: Time.current }
28
- end
29
25
  end
@@ -6,7 +6,7 @@ class Sessions::OmniauthController < ApplicationController
6
6
  @<%= singular_table_name %> = <%= class_name %>.where(omniauth_params).first_or_initialize(<%= "#{singular_table_name}_params" %>)
7
7
 
8
8
  if @<%= singular_table_name %>.save
9
- session = @<%= singular_table_name %>.sessions.create!(session_params)
9
+ session = @<%= singular_table_name %>.sessions.create!
10
10
  cookies.signed.permanent[:session_token] = { value: session.id, httponly: true }
11
11
 
12
12
  redirect_to root_path, notice: "Signed in successfully"
@@ -28,10 +28,6 @@ class Sessions::OmniauthController < ApplicationController
28
28
  { email: omniauth.info.email, password: SecureRandom::base58, verified: true }
29
29
  end
30
30
 
31
- def session_params
32
- { user_agent: request.user_agent, ip_address: request.remote_ip, sudo_at: Time.current }
33
- end
34
-
35
31
  def omniauth
36
32
  request.env["omniauth.auth"]
37
33
  end
@@ -15,7 +15,7 @@ class SessionsController < ApplicationController
15
15
  <%= singular_table_name %> = <%= class_name %>.find_by(email: params[:email])
16
16
 
17
17
  if <%= singular_table_name %> && <%= singular_table_name %>.authenticate(params[:password])
18
- @session = <%= singular_table_name %>.sessions.create!(session_params)
18
+ @session = <%= singular_table_name %>.sessions.create!
19
19
  cookies.signed.permanent[:session_token] = { value: @session.id, httponly: true }
20
20
 
21
21
  redirect_to root_path, notice: "Signed in successfully"
@@ -32,8 +32,4 @@ class SessionsController < ApplicationController
32
32
  def set_session
33
33
  @session = Current.<%= singular_table_name %>.sessions.find(params[:id])
34
34
  end
35
-
36
- def session_params
37
- { user_agent: request.user_agent, ip_address: request.remote_ip, sudo_at: Time.current }
38
- end
39
35
  end
@@ -0,0 +1,33 @@
1
+ <h1>Activity Log</h1>
2
+
3
+ <div id="sessions">
4
+ <% @events.each do |event| %>
5
+ <div id="<%= dom_id event %>">
6
+ <p>
7
+ <strong>User Agent:</strong>
8
+ <%= event.user_agent %>
9
+ </p>
10
+
11
+ <p>
12
+ <strong>Action:</strong>
13
+ <%= event.action %>
14
+ </p>
15
+
16
+ <p>
17
+ <strong>Ip Address:</strong>
18
+ <%= event.ip_address %>
19
+ </p>
20
+
21
+ <p>
22
+ <strong>Created at:</strong>
23
+ <%= event.created_at %>
24
+ </p>
25
+ </div>
26
+ <% end %>
27
+ </div>
28
+
29
+ <br>
30
+
31
+ <div>
32
+ <%= link_to "Back", root_path %>
33
+ </div>
@@ -17,13 +17,13 @@
17
17
 
18
18
  <div>
19
19
  <%%= form.label :password, "New password", style: "display: block" %>
20
- <%%= form.password_field :password, autofocus: true, autocomplete: "new-password" %>
20
+ <%%= form.password_field :password, required: true, autofocus: true, autocomplete: "new-password" %>
21
21
  <div>12 characters minimum.</div>
22
22
  </div>
23
23
 
24
24
  <div>
25
25
  <%%= form.label :password_confirmation, "Confirm new password", style: "display: block" %>
26
- <%%= form.password_field :password_confirmation, autocomplete: "new-password" %>
26
+ <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password" %>
27
27
  </div>
28
28
 
29
29
  <div>
@@ -17,18 +17,18 @@
17
17
 
18
18
  <div>
19
19
  <%%= label_tag :current_password, nil, style: "display: block" %>
20
- <%%= password_field_tag :current_password, nil, autofocus: true, autocomplete: "current-password" %>
20
+ <%%= password_field_tag :current_password, nil, required: true, autofocus: true, autocomplete: "current-password" %>
21
21
  </div>
22
22
 
23
23
  <div>
24
24
  <%%= form.label :password, "New password", style: "display: block" %>
25
- <%%= form.password_field :password, autocomplete: "new-password" %>
25
+ <%%= form.password_field :password, required: true, autocomplete: "new-password" %>
26
26
  <div>12 characters minimum.</div>
27
27
  </div>
28
28
 
29
29
  <div>
30
30
  <%%= form.label :password_confirmation, "Confirm new password", style: "display: block" %>
31
- <%%= form.password_field :password_confirmation, autocomplete: "new-password" %>
31
+ <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password" %>
32
32
  </div>
33
33
 
34
34
  <div>
@@ -20,13 +20,13 @@
20
20
 
21
21
  <div>
22
22
  <%%= form.label :password, style: "display: block" %>
23
- <%%= form.password_field :password, autocomplete: "new-password" %>
23
+ <%%= form.password_field :password, required: true, autocomplete: "new-password" %>
24
24
  <div>12 characters minimum.</div>
25
25
  </div>
26
26
 
27
27
  <div>
28
28
  <%%= form.label :password_confirmation, style: "display: block" %>
29
- <%%= form.password_field :password_confirmation, autocomplete: "new-password" %>
29
+ <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password" %>
30
30
  </div>
31
31
 
32
32
  <div>
@@ -7,7 +7,7 @@
7
7
  <%%= hidden_field_tag :proceed_to_url, params[:proceed_to_url] %>
8
8
 
9
9
  <div>
10
- <%%= password_field_tag :password, nil, autofocus: true, autocomplete: "current-password" %>
10
+ <%%= password_field_tag :password, nil, required: true, autofocus: true, autocomplete: "current-password" %>
11
11
  </div>
12
12
 
13
13
  <div>
@@ -0,0 +1,12 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def change
3
+ create_table :events do |t|
4
+ t.references :<%= singular_table_name %>, null: false, foreign_key: true
5
+ t.string :action, null: false
6
+ t.string :user_agent
7
+ t.string :ip_address
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -3,8 +3,8 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
3
3
  create_table :sessions do |t|
4
4
  t.references :<%= singular_table_name %>, null: false, foreign_key: true
5
5
 
6
- t.string :user_agent, null: false
7
- t.string :ip_address, null: false
6
+ t.string :user_agent
7
+ t.string :ip_address
8
8
 
9
9
  t.datetime :sudo_at, null: false
10
10
 
@@ -6,7 +6,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
6
6
 
7
7
  t.boolean :verified, null: false, default: false
8
8
  <% if omniauthable? %>
9
- t.string :provide
9
+ t.string :provider
10
10
  t.string :uid
11
11
  <% end -%>
12
12
 
@@ -1,5 +1,6 @@
1
1
  class Current < ActiveSupport::CurrentAttributes
2
2
  attribute :session, :<%= singular_table_name %>
3
+ attribute :user_agent, :ip_address
3
4
 
4
5
  def session=(session)
5
6
  super; self.<%= singular_table_name %> = session.<%= singular_table_name %>
@@ -0,0 +1,8 @@
1
+ class Event < ApplicationRecord
2
+ belongs_to :<%= singular_table_name %>
3
+
4
+ before_create do
5
+ self.user_agent = Current.user_agent
6
+ self.ip_address = Current.ip_address
7
+ end
8
+ end
@@ -2,6 +2,9 @@ class <%= class_name %> < ApplicationRecord
2
2
  has_secure_password
3
3
 
4
4
  has_many :sessions, dependent: :destroy
5
+ <% if options.trackable? -%>
6
+ has_many :events, dependent: :destroy
7
+ <% end -%>
5
8
 
6
9
  validates :email, presence: true, uniqueness: true
7
10
  validates_format_of :email, with: /\A[^@\s]+@[^@\s]+\z/
@@ -24,11 +27,20 @@ class <%= class_name %> < ApplicationRecord
24
27
  sessions.where.not(id: Current.session).destroy_all
25
28
  end
26
29
 
27
- after_create_commit do
28
- IdentityMailer.with(<%= singular_table_name %>: self).email_verify_confirmation.deliver_later
30
+ after_save_commit if: :email_previously_changed? do
31
+ IdentityMailer.with(user: self).email_verify_confirmation.deliver_later
32
+ end
33
+ <% if options.trackable? %>
34
+ after_save_commit if: :email_previously_changed? do
35
+ events.create! action: "email_verification_requested"
36
+ end
37
+
38
+ after_update if: :password_digest_previously_changed? do
39
+ events.create! action: "password_changed"
29
40
  end
30
41
 
31
- after_update_commit if: :email_previously_changed? do
32
- IdentityMailer.with(<%= singular_table_name %>: self).email_verify_confirmation.deliver_later
42
+ after_update if: :verified_previously_changed? do
43
+ events.create! action: "email_verified" if verified?
33
44
  end
45
+ <% end -%>
34
46
  end
@@ -1,7 +1,22 @@
1
1
  class Session < ApplicationRecord
2
2
  belongs_to :<%= singular_table_name %>
3
3
 
4
+ before_create do
5
+ self.user_agent = Current.user_agent
6
+ self.ip_address = Current.ip_address
7
+ self.sudo_at = Time.current
8
+ end
9
+
4
10
  after_create_commit do
5
11
  SessionMailer.with(session: self).signed_in_notification.deliver_later
6
12
  end
13
+ <% if options.trackable? %>
14
+ after_create do
15
+ <%= singular_table_name %>.events.create! action: "signed_in"
16
+ end
17
+
18
+ after_destroy do
19
+ <%= singular_table_name %>.events.create! action: "signed_out"
20
+ end
21
+ <% end -%>
7
22
  end
@@ -39,6 +39,6 @@ class Identity::EmailVerificationsControllerTest < ActionDispatch::IntegrationTe
39
39
  end
40
40
 
41
41
  def sign_in_as(<%= singular_table_name %>)
42
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "App iOS" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
42
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
43
43
  end
44
44
  end
@@ -20,6 +20,6 @@ class Identity::EmailsControllerTest < ActionDispatch::IntegrationTest
20
20
  end
21
21
 
22
22
  def sign_in_as(<%= singular_table_name %>)
23
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "App iOS" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
23
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
24
24
  end
25
25
  end
@@ -18,6 +18,6 @@ class PasswordsControllerTest < ActionDispatch::IntegrationTest
18
18
  end
19
19
 
20
20
  def sign_in_as(<%= singular_table_name %>)
21
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "App iOS" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
21
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
22
22
  end
23
23
  end
@@ -19,6 +19,6 @@ class Sessions::SudosControllerTest < ActionDispatch::IntegrationTest
19
19
  end
20
20
 
21
21
  def sign_in_as(<%= singular_table_name %>)
22
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "App iOS" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
22
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
23
23
  end
24
24
  end
@@ -16,14 +16,14 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
16
16
  end
17
17
 
18
18
  test "should sign in" do
19
- post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "App iOS" }
19
+ post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "Secret1*3*5*" }
20
20
 
21
21
  assert_enqueued_email_with SessionMailer, :signed_in_notification, args: { session: @<%= singular_table_name %>.sessions.last }
22
22
  assert_response :created
23
23
  end
24
24
 
25
25
  test "should not sign in with wrong credentials" do
26
- post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "SecretWrong1*3" }, headers: { "User-Agent" => "App iOS" }
26
+ post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "SecretWrong1*3" }
27
27
  assert_response :unauthorized
28
28
  end
29
29
 
@@ -33,6 +33,6 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
33
33
  end
34
34
 
35
35
  def sign_in_as(<%= singular_table_name %>)
36
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "App iOS" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
36
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
37
37
  end
38
38
  end
@@ -39,6 +39,6 @@ class Identity::EmailVerificationsControllerTest < ActionDispatch::IntegrationTe
39
39
  end
40
40
 
41
41
  def sign_in_as(<%= singular_table_name %>)
42
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "Firefox" }); <%= singular_table_name %>
42
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
43
43
  end
44
44
  end
@@ -30,6 +30,6 @@ class Identity::EmailsControllerTest < ActionDispatch::IntegrationTest
30
30
  end
31
31
 
32
32
  def sign_in_as(<%= singular_table_name %>)
33
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "Firefox" }); <%= singular_table_name %>
33
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
34
34
  end
35
35
  end
@@ -23,6 +23,6 @@ class PasswordsControllerTest < ActionDispatch::IntegrationTest
23
23
  end
24
24
 
25
25
  def sign_in_as(<%= singular_table_name %>)
26
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "Firefox" }); <%= singular_table_name %>
26
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
27
27
  end
28
28
  end
@@ -8,7 +8,7 @@ class RegistrationsControllerTest < ActionDispatch::IntegrationTest
8
8
 
9
9
  test "should sign up" do
10
10
  assert_difference("<%= class_name %>.count") do
11
- post sign_up_url, params: { email: "lazaronixon@hey.com", password: "Secret1*3*5*", password_confirmation: "Secret1*3*5*" }, headers: { "User-Agent" => "Firefox" }
11
+ post sign_up_url, params: { email: "lazaronixon@hey.com", password: "Secret1*3*5*", password_confirmation: "Secret1*3*5*" }
12
12
  end
13
13
 
14
14
  assert_redirected_to root_url
@@ -21,6 +21,6 @@ class Sessions::SudosControllerTest < ActionDispatch::IntegrationTest
21
21
  end
22
22
 
23
23
  def sign_in_as(<%= singular_table_name %>)
24
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "Firefox" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
24
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
25
25
  end
26
26
  end
@@ -18,7 +18,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
18
18
  end
19
19
 
20
20
  test "should sign in" do
21
- post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "Firefox" }
21
+ post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "Secret1*3*5*" }
22
22
  assert_enqueued_email_with SessionMailer, :signed_in_notification, args: { session: @<%= singular_table_name %>.sessions.last }
23
23
 
24
24
  assert_redirected_to root_url
@@ -28,7 +28,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
28
28
  end
29
29
 
30
30
  test "should not sign in with wrong credentials" do
31
- post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "SecretWrong1*3" }, headers: { "User-Agent" => "Firefox" }
31
+ post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "SecretWrong1*3" }
32
32
  assert_redirected_to sign_in_url(email_hint: @<%= singular_table_name %>.email)
33
33
  assert_equal "That email or password is incorrect", flash[:alert]
34
34
 
@@ -47,6 +47,6 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
47
47
  end
48
48
 
49
49
  def sign_in_as(<%= singular_table_name %>)
50
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }, headers: { "User-Agent" => "Firefox" }); <%= singular_table_name %>
50
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
51
51
  end
52
52
  end
@@ -21,6 +21,13 @@ class SessionsTest < ApplicationSystemTestCase
21
21
  assert_text "Signed in successfully"
22
22
  end
23
23
 
24
+ test "signing out" do
25
+ sign_in_as @<%= singular_table_name %>
26
+
27
+ click_on "Log out"
28
+ assert_text "That session has been logged out"
29
+ end
30
+
24
31
  def sign_in_as(<%= singular_table_name %>)
25
32
  visit sign_in_url
26
33
  fill_in :email, with: <%= singular_table_name %>.email
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.8.4
4
+ version: 2.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nixon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-04 00:00:00.000000000 Z
11
+ date: 2022-03-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -26,6 +26,7 @@ files:
26
26
  - LICENSE.txt
27
27
  - README.md
28
28
  - Rakefile
29
+ - authentication-zero-api.md
29
30
  - authentication-zero.gemspec
30
31
  - lib/authentication-zero.rb
31
32
  - lib/authentication_zero.rb
@@ -34,6 +35,7 @@ files:
34
35
  - lib/generators/authentication/authentication_generator.rb
35
36
  - lib/generators/authentication/templates/config/initializers/omniauth.rb
36
37
  - lib/generators/authentication/templates/config/redis/shared.yml
38
+ - lib/generators/authentication/templates/controllers/api/authentications/events_controller.rb.tt
37
39
  - lib/generators/authentication/templates/controllers/api/identity/email_verifications_controller.rb.tt
38
40
  - lib/generators/authentication/templates/controllers/api/identity/emails_controller.rb.tt
39
41
  - lib/generators/authentication/templates/controllers/api/identity/password_resets_controller.rb.tt
@@ -41,14 +43,16 @@ files:
41
43
  - lib/generators/authentication/templates/controllers/api/registrations_controller.rb.tt
42
44
  - lib/generators/authentication/templates/controllers/api/sessions/sudos_controller.rb.tt
43
45
  - lib/generators/authentication/templates/controllers/api/sessions_controller.rb.tt
46
+ - lib/generators/authentication/templates/controllers/html/authentications/events_controller.rb.tt
44
47
  - lib/generators/authentication/templates/controllers/html/identity/email_verifications_controller.rb.tt
45
48
  - lib/generators/authentication/templates/controllers/html/identity/emails_controller.rb.tt
46
49
  - lib/generators/authentication/templates/controllers/html/identity/password_resets_controller.rb.tt
47
50
  - lib/generators/authentication/templates/controllers/html/passwords_controller.rb.tt
48
51
  - lib/generators/authentication/templates/controllers/html/registrations_controller.rb.tt
52
+ - lib/generators/authentication/templates/controllers/html/sessions/omniauth_controller.rb.tt
49
53
  - lib/generators/authentication/templates/controllers/html/sessions/sudos_controller.rb.tt
50
54
  - lib/generators/authentication/templates/controllers/html/sessions_controller.rb.tt
51
- - lib/generators/authentication/templates/controllers/omniauth_controller.rb.tt
55
+ - lib/generators/authentication/templates/erb/authentications/events/index.html.erb
52
56
  - lib/generators/authentication/templates/erb/identity/emails/edit.html.erb.tt
53
57
  - lib/generators/authentication/templates/erb/identity/password_resets/edit.html.erb.tt
54
58
  - lib/generators/authentication/templates/erb/identity/password_resets/new.html.erb.tt
@@ -65,9 +69,11 @@ files:
65
69
  - lib/generators/authentication/templates/erb/sessions/sudos/new.html.erb.tt
66
70
  - lib/generators/authentication/templates/mailers/identity_mailer.rb.tt
67
71
  - lib/generators/authentication/templates/mailers/session_mailer.rb.tt
72
+ - lib/generators/authentication/templates/migrations/create_events_migration.rb.tt
68
73
  - lib/generators/authentication/templates/migrations/create_sessions_migration.rb.tt
69
74
  - lib/generators/authentication/templates/migrations/create_table_migration.rb.tt
70
75
  - lib/generators/authentication/templates/models/current.rb.tt
76
+ - lib/generators/authentication/templates/models/event.rb.tt
71
77
  - lib/generators/authentication/templates/models/locking.rb.tt
72
78
  - lib/generators/authentication/templates/models/model.rb.tt
73
79
  - lib/generators/authentication/templates/models/session.rb.tt