active_authentication 0.1.0
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 +7 -0
- data/README.md +120 -0
- data/Rakefile +8 -0
- data/app/controllers/active_authentication/confirmations_controller.rb +29 -0
- data/app/controllers/active_authentication/passwords_controller.rb +37 -0
- data/app/controllers/active_authentication/registrations_controller.rb +42 -0
- data/app/controllers/active_authentication/sessions_controller.rb +36 -0
- data/app/controllers/active_authentication/unlocks_controller.rb +29 -0
- data/app/mailers/active_authentication/mailer.rb +21 -0
- data/app/views/active_authentication/confirmations/new.html.erb +14 -0
- data/app/views/active_authentication/mailer/email_confirmation_instructions.html.erb +5 -0
- data/app/views/active_authentication/mailer/password_reset_instructions.html.erb +5 -0
- data/app/views/active_authentication/mailer/unlock_instructions.html.erb +5 -0
- data/app/views/active_authentication/passwords/edit.html.erb +28 -0
- data/app/views/active_authentication/passwords/new.html.erb +14 -0
- data/app/views/active_authentication/registrations/edit.html.erb +42 -0
- data/app/views/active_authentication/registrations/new.html.erb +35 -0
- data/app/views/active_authentication/sessions/new.html.erb +19 -0
- data/app/views/active_authentication/shared/_links.html.erb +19 -0
- data/app/views/active_authentication/unlocks/new.html.erb +14 -0
- data/config/i18n-tasks.yml +159 -0
- data/config/locales/en.yml +90 -0
- data/config/locales/es.yml +90 -0
- data/lib/active_authentication/controller/lockable.rb +31 -0
- data/lib/active_authentication/controller/trackable.rb +17 -0
- data/lib/active_authentication/controller.rb +48 -0
- data/lib/active_authentication/current.rb +5 -0
- data/lib/active_authentication/engine.rb +16 -0
- data/lib/active_authentication/model/authenticatable.rb +16 -0
- data/lib/active_authentication/model/confirmable.rb +54 -0
- data/lib/active_authentication/model/lockable.rb +42 -0
- data/lib/active_authentication/model/recoverable.rb +16 -0
- data/lib/active_authentication/model/registerable.rb +7 -0
- data/lib/active_authentication/model/trackable.rb +23 -0
- data/lib/active_authentication/model.rb +21 -0
- data/lib/active_authentication/routes.rb +34 -0
- data/lib/active_authentication/test/helpers.rb +13 -0
- data/lib/active_authentication/version.rb +3 -0
- data/lib/active_authentication.rb +42 -0
- data/lib/generators/active_authentication/install/install_generator.rb +53 -0
- data/lib/generators/active_authentication/install/templates/initializer.rb +14 -0
- data/lib/generators/active_authentication/install/templates/migration.rb +26 -0
- data/lib/generators/active_authentication/views/views_generator.rb +17 -0
- metadata +116 -0
@@ -0,0 +1,159 @@
|
|
1
|
+
# i18n-tasks finds and manages missing and unused translations: https://github.com/glebm/i18n-tasks
|
2
|
+
|
3
|
+
# The "main" locale.
|
4
|
+
base_locale: en
|
5
|
+
## All available locales are inferred from the data by default. Alternatively, specify them explicitly:
|
6
|
+
# locales: [es, fr]
|
7
|
+
## Reporting locale, default: en. Available: en, ru.
|
8
|
+
# internal_locale: en
|
9
|
+
|
10
|
+
# Read and write translations.
|
11
|
+
data:
|
12
|
+
## Translations are read from the file system. Supported format: YAML, JSON.
|
13
|
+
## Provide a custom adapter:
|
14
|
+
# adapter: I18n::Tasks::Data::FileSystem
|
15
|
+
|
16
|
+
# Locale files or `Find.find` patterns where translations are read from:
|
17
|
+
read:
|
18
|
+
## Default:
|
19
|
+
# - config/locales/%{locale}.yml
|
20
|
+
## More files:
|
21
|
+
# - config/locales/**/*.%{locale}.yml
|
22
|
+
|
23
|
+
# Locale files to write new keys to, based on a list of key pattern => file rules. Matched from top to bottom:
|
24
|
+
# `i18n-tasks normalize -p` will force move the keys according to these rules
|
25
|
+
write:
|
26
|
+
## For example, write devise and simple form keys to their respective files:
|
27
|
+
# - ['{devise, simple_form}.*', 'config/locales/\1.%{locale}.yml']
|
28
|
+
## Catch-all default:
|
29
|
+
# - config/locales/%{locale}.yml
|
30
|
+
|
31
|
+
# External locale data (e.g. gems).
|
32
|
+
# This data is not considered unused and is never written to.
|
33
|
+
external:
|
34
|
+
## Example (replace %#= with %=):
|
35
|
+
# - "<%#= %x[bundle info vagrant --path].chomp %>/templates/locales/%{locale}.yml"
|
36
|
+
|
37
|
+
## Specify the router (see Readme for details). Valid values: conservative_router, pattern_router, or a custom class.
|
38
|
+
# router: conservative_router
|
39
|
+
|
40
|
+
yaml:
|
41
|
+
write:
|
42
|
+
# do not wrap lines at 80 characters
|
43
|
+
line_width: -1
|
44
|
+
|
45
|
+
## Pretty-print JSON:
|
46
|
+
# json:
|
47
|
+
# write:
|
48
|
+
# indent: ' '
|
49
|
+
# space: ' '
|
50
|
+
# object_nl: "\n"
|
51
|
+
# array_nl: "\n"
|
52
|
+
|
53
|
+
# Find translate calls
|
54
|
+
search:
|
55
|
+
## Paths or `Find.find` patterns to search in:
|
56
|
+
# paths:
|
57
|
+
# - app/
|
58
|
+
|
59
|
+
## Root directories for relative keys resolution.
|
60
|
+
# relative_roots:
|
61
|
+
# - app/controllers
|
62
|
+
# - app/helpers
|
63
|
+
# - app/mailers
|
64
|
+
# - app/presenters
|
65
|
+
# - app/views
|
66
|
+
|
67
|
+
## Directories where method names which should not be part of a relative key resolution.
|
68
|
+
# By default, if a relative translation is used inside a method, the name of the method will be considered part of the resolved key.
|
69
|
+
# Directories listed here will not consider the name of the method part of the resolved key
|
70
|
+
#
|
71
|
+
# relative_exclude_method_name_paths:
|
72
|
+
# -
|
73
|
+
|
74
|
+
## Files or `File.fnmatch` patterns to exclude from search. Some files are always excluded regardless of this setting:
|
75
|
+
## *.jpg *.jpeg *.png *.gif *.svg *.ico *.eot *.otf *.ttf *.woff *.woff2 *.pdf *.css *.sass *.scss *.less
|
76
|
+
## *.yml *.json *.zip *.tar.gz *.swf *.flv *.mp3 *.wav *.flac *.webm *.mp4 *.ogg *.opus *.webp *.map *.xlsx
|
77
|
+
exclude:
|
78
|
+
- app/assets/images
|
79
|
+
- app/assets/fonts
|
80
|
+
- app/assets/videos
|
81
|
+
- app/assets/builds
|
82
|
+
|
83
|
+
## Alternatively, the only files or `File.fnmatch patterns` to search in `paths`:
|
84
|
+
## If specified, this settings takes priority over `exclude`, but `exclude` still applies.
|
85
|
+
# only: ["*.rb", "*.html.slim"]
|
86
|
+
|
87
|
+
## If `strict` is `false`, guess usages such as t("categories.#{category}.title"). The default is `true`.
|
88
|
+
# strict: true
|
89
|
+
|
90
|
+
## Allows adding ast_matchers for finding translations using the AST-scanners
|
91
|
+
## The available matchers are:
|
92
|
+
## - RailsModelMatcher
|
93
|
+
## Matches ActiveRecord translations like
|
94
|
+
## User.human_attribute_name(:email) and User.model_name.human
|
95
|
+
##
|
96
|
+
## To implement your own, please see `I18n::Tasks::Scanners::AstMatchers::BaseMatcher`.
|
97
|
+
# <%# I18n::Tasks.add_ast_matcher('I18n::Tasks::Scanners::AstMatchers::RailsModelMatcher') %>
|
98
|
+
|
99
|
+
## Multiple scanners can be used. Their results are merged.
|
100
|
+
## The options specified above are passed down to each scanner. Per-scanner options can be specified as well.
|
101
|
+
## See this example of a custom scanner: https://github.com/glebm/i18n-tasks/wiki/A-custom-scanner-example
|
102
|
+
|
103
|
+
## Translation Services
|
104
|
+
# translation:
|
105
|
+
# # Google Translate
|
106
|
+
# # Get an API key and set billing info at https://code.google.com/apis/console to use Google Translate
|
107
|
+
# google_translate_api_key: "AbC-dEf5"
|
108
|
+
# # DeepL Pro Translate
|
109
|
+
# # Get an API key and subscription at https://www.deepl.com/pro to use DeepL Pro
|
110
|
+
# deepl_api_key: "48E92789-57A3-466A-9959-1A1A1A1A1A1A"
|
111
|
+
# # deepl_host: "https://api.deepl.com"
|
112
|
+
# # deepl_version: "v2"
|
113
|
+
# # add additional options to the DeepL.translate call: https://www.deepl.com/docs-api/translate-text/translate-text/
|
114
|
+
# deepl_options:
|
115
|
+
# formality: prefer_less
|
116
|
+
## Do not consider these keys missing:
|
117
|
+
# ignore_missing:
|
118
|
+
# - 'errors.messages.{accepted,blank,invalid,too_short,too_long}'
|
119
|
+
# - '{devise,simple_form}.*'
|
120
|
+
|
121
|
+
## Consider these keys used:
|
122
|
+
# ignore_unused:
|
123
|
+
# - 'activerecord.attributes.*'
|
124
|
+
# - '{devise,kaminari,will_paginate}.*'
|
125
|
+
# - 'simple_form.{yes,no}'
|
126
|
+
# - 'simple_form.{placeholders,hints,labels}.*'
|
127
|
+
# - 'simple_form.{error_notification,required}.:'
|
128
|
+
ignore_unused:
|
129
|
+
- activerecord.attributes.*
|
130
|
+
- active_authentication.failure.*
|
131
|
+
- active_authentication.mailer.*.subject
|
132
|
+
|
133
|
+
## Exclude these keys from the `i18n-tasks eq-base' report:
|
134
|
+
# ignore_eq_base:
|
135
|
+
# all:
|
136
|
+
# - common.ok
|
137
|
+
# fr,es:
|
138
|
+
# - common.brand
|
139
|
+
|
140
|
+
## Exclude these keys from the `i18n-tasks check-consistent-interpolations` report:
|
141
|
+
# ignore_inconsistent_interpolations:
|
142
|
+
# - 'activerecord.attributes.*'
|
143
|
+
|
144
|
+
## Ignore these keys completely:
|
145
|
+
# ignore:
|
146
|
+
# - kaminari.*
|
147
|
+
|
148
|
+
## Sometimes, it isn't possible for i18n-tasks to match the key correctly,
|
149
|
+
## e.g. in case of a relative key defined in a helper method.
|
150
|
+
## In these cases you can use the built-in PatternMapper to map patterns to keys, e.g.:
|
151
|
+
#
|
152
|
+
# <%# I18n::Tasks.add_scanner 'I18n::Tasks::Scanners::PatternMapper',
|
153
|
+
# only: %w(*.html.haml *.html.slim),
|
154
|
+
# patterns: [['= title\b', '.page_title']] %>
|
155
|
+
#
|
156
|
+
# The PatternMapper can also match key literals via a special %{key} interpolation, e.g.:
|
157
|
+
#
|
158
|
+
# <%# I18n::Tasks.add_scanner 'I18n::Tasks::Scanners::PatternMapper',
|
159
|
+
# patterns: [['\bSpree\.t[( ]\s*%{key}', 'spree.%{key}']] %>
|
@@ -0,0 +1,90 @@
|
|
1
|
+
---
|
2
|
+
en:
|
3
|
+
active_authentication:
|
4
|
+
confirmations:
|
5
|
+
create:
|
6
|
+
success: Email confirmation instructions will be sent to your email.
|
7
|
+
new:
|
8
|
+
email_confirmation_instructions: Send email confirmation instructions
|
9
|
+
set_user:
|
10
|
+
invalid_token: Email confirmation token is invalid.
|
11
|
+
show:
|
12
|
+
success: Your email has been successfully confirmed.
|
13
|
+
failure:
|
14
|
+
already_signed_in: You are already signed in.
|
15
|
+
form_errors: "%{errors} prohibited this user from being saved:"
|
16
|
+
locked: Your account has been locked after %{count} failed attempts. Unlock instructions will be sent to your email.
|
17
|
+
unauthenticated: You need to sign in or sign up before continuing.
|
18
|
+
mailer:
|
19
|
+
email_confirmation_instructions:
|
20
|
+
confirm_email: Confirm my account
|
21
|
+
confirm_email_below: 'You can confirm your account email by clicking the link below:'
|
22
|
+
hello: Hello, %{email}!
|
23
|
+
subject: Confirmation instructions
|
24
|
+
password_reset_instructions:
|
25
|
+
hello: Hello, %{email}!
|
26
|
+
reset_password: Reset your password
|
27
|
+
reset_password_below: 'You can reset your password by clicking the link below:'
|
28
|
+
subject: Password reset instructions
|
29
|
+
unlock_instructions:
|
30
|
+
hello: Hello, %{email}!
|
31
|
+
subject: Unlock instructions
|
32
|
+
unlock: Unlock your account
|
33
|
+
unlock_below: 'You can unlock your account by clicking the link below:'
|
34
|
+
passwords:
|
35
|
+
create:
|
36
|
+
success: Password reset instructions will be sent to your email.
|
37
|
+
edit:
|
38
|
+
set_new_password: Set new password
|
39
|
+
new:
|
40
|
+
reset_password_instructions: Send reset password instructions
|
41
|
+
set_user:
|
42
|
+
invalid_token: Password reset token is invalid.
|
43
|
+
update:
|
44
|
+
success: Your password has been successfully updated.
|
45
|
+
registrations:
|
46
|
+
create:
|
47
|
+
success: You have signed up successfully.
|
48
|
+
destroy:
|
49
|
+
success: Your account has been successfully cancelled.
|
50
|
+
edit:
|
51
|
+
cancel_account: Cancel account
|
52
|
+
confirm: Are you sure?
|
53
|
+
danger_zone: Danger zone
|
54
|
+
edit_profile: Edit profile
|
55
|
+
pending_confirmation: 'You need to confirm your new email address: %{unconfirmed_email}'
|
56
|
+
save: Save
|
57
|
+
new:
|
58
|
+
sign_up: Sign up
|
59
|
+
update:
|
60
|
+
success: Your account has been successfully updated.
|
61
|
+
sessions:
|
62
|
+
create:
|
63
|
+
invalid_email_or_password: Invalid email or password.
|
64
|
+
success: Signed in successfully.
|
65
|
+
destroy:
|
66
|
+
success: Signed out successfully.
|
67
|
+
new:
|
68
|
+
sign_in: Sign in
|
69
|
+
shared:
|
70
|
+
links:
|
71
|
+
reset_password: Reset password
|
72
|
+
send_email_confirmation_instructions: Didn't receive confirmation instructions?
|
73
|
+
send_unlock_instructions: Didn't receive unlock instructions?
|
74
|
+
sign_in: Sign in
|
75
|
+
sign_up: Sign up
|
76
|
+
unlocks:
|
77
|
+
create:
|
78
|
+
success: Unlock instructions will be sent to your email.
|
79
|
+
new:
|
80
|
+
unlock_instructions: Send unlock instructions
|
81
|
+
set_user:
|
82
|
+
invalid_token: Unlock token is invalid.
|
83
|
+
show:
|
84
|
+
success: Your account has been successfully unlocked.
|
85
|
+
activerecord:
|
86
|
+
attributes:
|
87
|
+
user:
|
88
|
+
email: Email
|
89
|
+
password: Password
|
90
|
+
password_confirmation: Password confirmation
|
@@ -0,0 +1,90 @@
|
|
1
|
+
---
|
2
|
+
es:
|
3
|
+
active_authentication:
|
4
|
+
confirmations:
|
5
|
+
create:
|
6
|
+
success: Te enviaremos un email con las instrucciones para confirmar tu cuenta.
|
7
|
+
new:
|
8
|
+
email_confirmation_instructions: Enviar instrucciones de confirmación
|
9
|
+
set_user:
|
10
|
+
invalid_token: El token es inválido.
|
11
|
+
show:
|
12
|
+
success: Tu cuenta fue confirmada exitosamente.
|
13
|
+
failure:
|
14
|
+
already_signed_in: Ya iniciaste sesión.
|
15
|
+
form_errors: "%{errors} no permitieron guardar este elemento:"
|
16
|
+
locked: Tu cuenta fue bloqueada después de %{count} intentos fallidos. Te enviaremos un email con las instrucciones de desbloqueo.
|
17
|
+
unauthenticated: Tenés que iniciar sesión antes de continuar.
|
18
|
+
mailer:
|
19
|
+
email_confirmation_instructions:
|
20
|
+
confirm_email: Confirmar mi cuenta
|
21
|
+
confirm_email_below: 'Podés confirmar tu cuenta haciendo click en el siguiente link:'
|
22
|
+
hello: Hola, %{email}!
|
23
|
+
subject: Instrucciones de confirmación
|
24
|
+
password_reset_instructions:
|
25
|
+
hello: Hola, %{email}!
|
26
|
+
reset_password: Recuperar contraseña
|
27
|
+
reset_password_below: 'Podés recuperar tu contraseña haciendo click en el siguiente link:'
|
28
|
+
subject: Instrucciones de recuperación de contraseña
|
29
|
+
unlock_instructions:
|
30
|
+
hello: Hola, %{email}!
|
31
|
+
subject: Instrucciones de desbloqueo
|
32
|
+
unlock: Desbloquear cuenta
|
33
|
+
unlock_below: 'Podés desbloquear tu contraseña haciendo click en el siguiente link:'
|
34
|
+
passwords:
|
35
|
+
create:
|
36
|
+
success: Te enviaremos un email con las instrucciones de para recuperar tu contraseña.
|
37
|
+
edit:
|
38
|
+
set_new_password: Cambiar contraseña
|
39
|
+
new:
|
40
|
+
reset_password_instructions: Enviar instrucciones para recuperar contraseña
|
41
|
+
set_user:
|
42
|
+
invalid_token: El token es inválido.
|
43
|
+
update:
|
44
|
+
success: Tu contraseña fue actualizada exitosamente.
|
45
|
+
registrations:
|
46
|
+
create:
|
47
|
+
success: Te registraste exitosamente.
|
48
|
+
destroy:
|
49
|
+
success: Tu cuenta fue cancelada exitosamente.
|
50
|
+
edit:
|
51
|
+
cancel_account: Cancelar cuenta
|
52
|
+
confirm: "¿Estás seguro?"
|
53
|
+
danger_zone: Zona de peligro
|
54
|
+
edit_profile: Editar perfil
|
55
|
+
pending_confirmation: 'Tenés que confirmar tu nuevo email: %{unconfirmed_email}'
|
56
|
+
save: Guardar
|
57
|
+
new:
|
58
|
+
sign_up: Crear una cuenta
|
59
|
+
update:
|
60
|
+
success: Tu cuenta fue actualizada exitosamente.
|
61
|
+
sessions:
|
62
|
+
create:
|
63
|
+
invalid_email_or_password: Email o contraseña inválidos.
|
64
|
+
success: Inicio de sesión existoso.
|
65
|
+
destroy:
|
66
|
+
success: Cierre de sesión exitoso.
|
67
|
+
new:
|
68
|
+
sign_in: Iniciar sesión
|
69
|
+
shared:
|
70
|
+
links:
|
71
|
+
reset_password: Recuperar contraseña
|
72
|
+
send_email_confirmation_instructions: "¿No recibiste las instrucciones de confirmación?"
|
73
|
+
send_unlock_instructions: "¿No recibiste las instrucciones de desbloqueo?"
|
74
|
+
sign_in: Iniciar sesión
|
75
|
+
sign_up: Crear una cuenta
|
76
|
+
unlocks:
|
77
|
+
create:
|
78
|
+
success: Te enviaremos un email con las instrucciones de desbloqueo.
|
79
|
+
new:
|
80
|
+
unlock_instructions: Enviar instrucciones de desbloqueo
|
81
|
+
set_user:
|
82
|
+
invalid_token: El token es inválido.
|
83
|
+
show:
|
84
|
+
success: Tu cuenta fue desbloqueada exitosamente.
|
85
|
+
activerecord:
|
86
|
+
attributes:
|
87
|
+
user:
|
88
|
+
email: Email
|
89
|
+
password: Contraseña
|
90
|
+
password_confirmation: Repetir contraseña
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ActiveAuthentication
|
2
|
+
module Controller
|
3
|
+
module Lockable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
set_callback :failed_sign_in, :before, :increment_failed_attempts
|
8
|
+
set_callback :failed_sign_in, :after, :set_alert
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def increment_failed_attempts
|
13
|
+
user = User.find_by email: params[:email]
|
14
|
+
user&.increment_failed_attempts
|
15
|
+
end
|
16
|
+
|
17
|
+
def scope
|
18
|
+
User.unlocked
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_alert
|
22
|
+
user = User.find_by email: params[:email]
|
23
|
+
|
24
|
+
if user.locked?
|
25
|
+
flash[:alert] = t "active_authentication.failure.locked", count: user.failed_attempts
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ActiveAuthentication
|
2
|
+
module Controller
|
3
|
+
module Trackable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
set_callback :successful_sign_in, :after, :track
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def track
|
12
|
+
current_user.track request
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module ActiveAuthentication
|
2
|
+
module Controller
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
include ActiveSupport::Callbacks
|
5
|
+
|
6
|
+
included do
|
7
|
+
helper_method :current_user
|
8
|
+
helper_method :user_signed_in?
|
9
|
+
end
|
10
|
+
|
11
|
+
def authenticate_user!
|
12
|
+
redirect_to new_session_path, alert: t("active_authentication.failure.unauthenticated") unless user_signed_in?
|
13
|
+
end
|
14
|
+
|
15
|
+
def current_user
|
16
|
+
Current.user ||= user_from_session
|
17
|
+
end
|
18
|
+
|
19
|
+
def require_no_authentication
|
20
|
+
redirect_to root_path, alert: t("active_authentication.failure.already_signed_in") if user_signed_in?
|
21
|
+
end
|
22
|
+
|
23
|
+
def sign_in(user)
|
24
|
+
reset_session
|
25
|
+
Current.user = user
|
26
|
+
session[:user_id] = user.id
|
27
|
+
end
|
28
|
+
|
29
|
+
def sign_out
|
30
|
+
reset_session
|
31
|
+
Current.user = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def user_signed_in?
|
35
|
+
current_user.present?
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def scope
|
41
|
+
User
|
42
|
+
end
|
43
|
+
|
44
|
+
def user_from_session
|
45
|
+
User.find_by id: session[:user_id]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "active_authentication/controller"
|
2
|
+
require "active_authentication/model"
|
3
|
+
|
4
|
+
module ActiveAuthentication
|
5
|
+
class Engine < ::Rails::Engine
|
6
|
+
initializer :active_authentication_controller do
|
7
|
+
ActiveSupport.on_load :action_controller_base do
|
8
|
+
include ActiveAuthentication::Controller
|
9
|
+
end
|
10
|
+
|
11
|
+
ActiveSupport.on_load :active_record do
|
12
|
+
include ActiveAuthentication::Model
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ActiveAuthentication
|
2
|
+
module Model
|
3
|
+
module Authenticatable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
has_secure_password
|
8
|
+
|
9
|
+
normalizes :email, with: -> { _1.strip.downcase }
|
10
|
+
|
11
|
+
validates :email, format: {with: URI::MailTo::EMAIL_REGEXP}, presence: true, uniqueness: true
|
12
|
+
validates :password, length: {minimum: ActiveAuthentication.min_password_length}, allow_blank: true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module ActiveAuthentication
|
2
|
+
module Model
|
3
|
+
module Confirmable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
generates_token_for :email_confirmation, expires_in: ActiveAuthentication.email_confirmation_token_expires_in
|
8
|
+
|
9
|
+
normalizes :unconfirmed_email, with: -> { _1.strip.downcase }
|
10
|
+
|
11
|
+
validates :unconfirmed_email, format: {with: URI::MailTo::EMAIL_REGEXP}, allow_blank: true
|
12
|
+
|
13
|
+
after_initialize do
|
14
|
+
@set_unconfirmed_email = true
|
15
|
+
@send_email_confirmation_instructions = true
|
16
|
+
end
|
17
|
+
|
18
|
+
before_update :set_unconfirmed_email, if: :set_unconfirmed_email?
|
19
|
+
after_save :send_email_confirmation_instructions, if: :send_email_confirmation_instructions?
|
20
|
+
end
|
21
|
+
|
22
|
+
def confirm
|
23
|
+
@send_email_confirmation_instructions = false
|
24
|
+
@set_unconfirmed_email = false
|
25
|
+
|
26
|
+
update email: unconfirmed_email, unconfirmed_email: nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def send_email_confirmation_instructions
|
30
|
+
token = generate_token_for :email_confirmation
|
31
|
+
ActiveAuthentication::Mailer.with(token: token, user: self).email_confirmation_instructions.deliver
|
32
|
+
end
|
33
|
+
|
34
|
+
def send_email_confirmation_instructions?
|
35
|
+
@send_email_confirmation_instructions && unconfirmed_email.present?
|
36
|
+
end
|
37
|
+
|
38
|
+
def unconfirmed?
|
39
|
+
unconfirmed_email.present?
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def set_unconfirmed_email
|
45
|
+
self.unconfirmed_email = email
|
46
|
+
self.email = email_was
|
47
|
+
end
|
48
|
+
|
49
|
+
def set_unconfirmed_email?
|
50
|
+
@set_unconfirmed_email && email_changed?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module ActiveAuthentication
|
2
|
+
module Model
|
3
|
+
module Lockable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
ActiveAuthentication::SessionsController.send :include, ActiveAuthentication::Controller::Lockable
|
8
|
+
|
9
|
+
generates_token_for :unlock, expires_in: ActiveAuthentication.unlock_token_expires_in
|
10
|
+
|
11
|
+
scope :unlocked, -> { where locked_at: nil }
|
12
|
+
end
|
13
|
+
|
14
|
+
def increment_failed_attempts
|
15
|
+
increment :failed_attempts
|
16
|
+
|
17
|
+
lock if failed_attempts == ActiveAuthentication.max_failed_attempts
|
18
|
+
|
19
|
+
save
|
20
|
+
end
|
21
|
+
|
22
|
+
def locked?
|
23
|
+
locked_at.present?
|
24
|
+
end
|
25
|
+
|
26
|
+
def lock
|
27
|
+
update locked_at: Time.now
|
28
|
+
|
29
|
+
send_unlock_instructions
|
30
|
+
end
|
31
|
+
|
32
|
+
def send_unlock_instructions
|
33
|
+
token = generate_token_for :unlock
|
34
|
+
ActiveAuthentication::Mailer.with(token: token, user: self).unlock_instructions.deliver
|
35
|
+
end
|
36
|
+
|
37
|
+
def unlock
|
38
|
+
update failed_attempts: 0, locked_at: nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ActiveAuthentication
|
2
|
+
module Model
|
3
|
+
module Recoverable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
generates_token_for :password_reset, expires_in: ActiveAuthentication.password_reset_token_expires_in
|
8
|
+
end
|
9
|
+
|
10
|
+
def send_password_reset_instructions
|
11
|
+
token = generate_token_for :password_reset
|
12
|
+
ActiveAuthentication::Mailer.with(token: token, user: self).password_reset_instructions.deliver
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActiveAuthentication
|
2
|
+
module Model
|
3
|
+
module Trackable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
ActiveAuthentication::SessionsController.send :include, ActiveAuthentication::Controller::Trackable
|
8
|
+
end
|
9
|
+
|
10
|
+
def track(request)
|
11
|
+
self.sign_in_count += 1
|
12
|
+
|
13
|
+
self.last_sign_in_at = current_sign_in_at
|
14
|
+
self.current_sign_in_at = Time.now
|
15
|
+
|
16
|
+
self.last_sign_in_ip = current_sign_in_ip
|
17
|
+
self.current_sign_in_ip = request.remote_ip
|
18
|
+
|
19
|
+
save
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActiveAuthentication
|
2
|
+
module Model
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
CONCERNS = %i[authenticatable confirmable lockable recoverable registerable trackable]
|
6
|
+
|
7
|
+
class_methods do
|
8
|
+
def authenticates_with(*concerns)
|
9
|
+
include Authenticatable
|
10
|
+
concerns.each do |concern|
|
11
|
+
include const_get(concern.to_s.classify)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
alias_method :authenticates, :authenticates_with
|
15
|
+
|
16
|
+
CONCERNS.each do |concern|
|
17
|
+
define_method(:"#{concern}?") { User.included_modules.include? const_get(concern.to_s.classify) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ActionDispatch::Routing
|
2
|
+
class Mapper
|
3
|
+
def active_authentication
|
4
|
+
scope module: :active_authentication do
|
5
|
+
ActiveAuthentication::Model::CONCERNS.each do |concern|
|
6
|
+
send(concern) if User.send(:"#{concern}?") && respond_to?(concern, true)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def authenticatable
|
14
|
+
resource :session, only: [:new, :create, :destroy]
|
15
|
+
end
|
16
|
+
|
17
|
+
def confirmable
|
18
|
+
resources :confirmations, param: :token, only: [:new, :create, :show]
|
19
|
+
end
|
20
|
+
|
21
|
+
def lockable
|
22
|
+
resources :unlocks, param: :token, only: [:new, :create, :show]
|
23
|
+
end
|
24
|
+
|
25
|
+
def registerable
|
26
|
+
resources :registrations, only: [:new, :create]
|
27
|
+
resource :profile, only: [:edit, :update, :destroy], path: :profile, controller: :registrations
|
28
|
+
end
|
29
|
+
|
30
|
+
def recoverable
|
31
|
+
resources :passwords, param: :token, only: [:new, :create, :edit, :update]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|