securial 0.6.1 → 0.8.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 +4 -4
- data/README.md +2 -5
- data/app/controllers/concerns/securial/identity.rb +17 -16
- data/app/controllers/securial/accounts_controller.rb +2 -2
- data/app/controllers/securial/passwords_controller.rb +2 -2
- data/app/controllers/securial/role_assignments_controller.rb +5 -7
- data/app/controllers/securial/roles_controller.rb +1 -0
- data/app/controllers/securial/sessions_controller.rb +6 -8
- data/app/controllers/securial/status_controller.rb +1 -1
- data/app/controllers/securial/users_controller.rb +8 -7
- data/app/mailers/securial/securial_mailer.rb +17 -6
- data/app/models/concerns/securial/password_resettable.rb +1 -1
- data/app/models/securial/application_record.rb +1 -1
- data/app/models/securial/role.rb +1 -1
- data/app/models/securial/session.rb +16 -5
- data/app/models/securial/user.rb +4 -6
- data/app/views/securial/securial_mailer/forgot_password.html.erb +20 -0
- data/app/views/securial/securial_mailer/forgot_password.text.erb +14 -0
- data/app/views/securial/securial_mailer/sign_in.html.erb +31 -0
- data/app/views/securial/securial_mailer/sign_in.text.erb +17 -0
- data/app/views/securial/securial_mailer/update_account.html.erb +15 -0
- data/app/views/securial/securial_mailer/update_account.text.erb +11 -0
- data/app/views/securial/securial_mailer/welcome.html.erb +11 -0
- data/app/views/securial/securial_mailer/welcome.text.erb +8 -0
- data/app/views/securial/users/_securial_user.json.jbuilder +9 -3
- data/config/routes.rb +5 -1
- data/db/migrate/{20250515104930_create_securial_roles.rb → 20250603130214_create_securial_roles.rb} +0 -2
- data/db/migrate/{20250517155521_create_securial_users.rb → 20250604110520_create_securial_users.rb} +8 -3
- data/db/migrate/{20250519075407_create_securial_sessions.rb → 20250604184841_create_securial_sessions.rb} +4 -0
- data/lib/generators/securial/install/templates/securial_initializer.erb +2 -2
- data/lib/generators/securial/scaffold/templates/controller.erb +1 -0
- data/lib/generators/securial/scaffold/templates/routes.erb +1 -1
- data/lib/securial/auth/auth_encoder.rb +42 -43
- data/lib/securial/auth/session_creator.rb +15 -12
- data/lib/securial/auth/token_generator.rb +26 -0
- data/lib/securial/auth.rb +12 -0
- data/lib/securial/config/configuration.rb +33 -15
- data/lib/securial/config/validation/logger_validation.rb +29 -0
- data/lib/securial/config/validation/mailer_validation.rb +24 -0
- data/lib/securial/config/validation/password_validation.rb +91 -0
- data/lib/securial/config/validation/response_validation.rb +37 -0
- data/lib/securial/config/validation/roles_validation.rb +32 -0
- data/lib/securial/config/validation/security_validation.rb +56 -0
- data/lib/securial/config/validation/session_validation.rb +87 -0
- data/lib/securial/config/validation.rb +16 -239
- data/lib/securial/config.rb +26 -0
- data/lib/securial/engine.rb +7 -70
- data/lib/securial/engine_initializers.rb +33 -0
- data/lib/securial/error/auth.rb +21 -0
- data/lib/securial/error/base_securial_error.rb +16 -0
- data/lib/securial/error/config.rb +37 -0
- data/lib/securial/error.rb +9 -0
- data/lib/securial/helpers/normalizing_helper.rb +11 -9
- data/lib/securial/helpers/regex_helper.rb +12 -10
- data/lib/securial/helpers/roles_helper.rb +17 -0
- data/lib/securial/helpers.rb +10 -0
- data/lib/securial/logger/broadcaster.rb +60 -0
- data/lib/securial/logger/builder.rb +45 -0
- data/lib/securial/logger/formatter.rb +35 -0
- data/lib/securial/logger.rb +7 -63
- data/lib/securial/middleware/request_tag_logger.rb +39 -0
- data/lib/securial/middleware.rb +13 -0
- data/lib/securial/version.rb +1 -1
- data/lib/securial.rb +2 -20
- metadata +45 -150
- data/app/views/securial/securial_mailer/reset_password.html.erb +0 -5
- data/app/views/securial/securial_mailer/reset_password.text.erb +0 -4
- data/lib/securial/auth/_index.rb +0 -3
- data/lib/securial/auth/errors.rb +0 -15
- data/lib/securial/config/_index.rb +0 -3
- data/lib/securial/config/errors.rb +0 -20
- data/lib/securial/helpers/_index.rb +0 -2
- data/lib/securial/inspectors/_index.rb +0 -1
- data/lib/securial/inspectors/route_inspector.rb +0 -52
- data/lib/securial/key_transformer.rb +0 -32
- data/lib/securial/middleware/_index.rb +0 -3
- data/lib/securial/middleware/request_logger_tag.rb +0 -18
- data/lib/securial/middleware/transform_request_keys.rb +0 -33
- data/lib/securial/middleware/transform_response_keys.rb +0 -45
- data/lib/securial/rack_attack.rb +0 -48
- /data/db/migrate/{20250518122749_create_securial_role_assignments.rb → 20250604123805_create_securial_role_assignments.rb} +0 -0
- /data/lib/generators/securial/install/{views_generastor.rb → views_generator.rb} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5d32016af493f09599093d6a47f3ad2ec6b1be080a82d1ef275374249802808
|
4
|
+
data.tar.gz: 75b64439f12e4b9f581a625967d83b39ebd07215be6c722ce4fbe81c50d3b0c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0da8943af4c5a85090d711e862d0e00185f0af951c9a619796fc9d3de3cfb37ab939fea73c690b6378495e66794b9f5ebe26f010fd44bc95118acc727654114a
|
7
|
+
data.tar.gz: 6785f1890063c01eac1a2b8f3b15aee18eb6cc8429eead79c15c65aadf5854a5496ab8bf5f0ac572f5a47020108dce77d23bbf49bd091062b3f5900a0fb4f002
|
data/README.md
CHANGED
@@ -5,7 +5,8 @@
|
|
5
5
|
[](https://github.com/AlyBadawy/Securial?tab=MIT-1-ov-file#readme)
|
6
6
|
|
7
7
|
[](https://github.com/alybadawy/securial/actions)
|
8
|
-
[
|
9
|
+
](https://coveralls.io/github/AlyBadawy/Securial?branch=main)
|
9
10
|
|
10
11
|
> [!WARNING]
|
11
12
|
>
|
@@ -26,15 +27,12 @@
|
|
26
27
|
- ✅ Clean, JSON-based API responses
|
27
28
|
- ✅ Database-agnostic support
|
28
29
|
|
29
|
-
|
30
30
|
### 🚀 Why Securial?
|
31
31
|
|
32
32
|
Securial was built to offer a clean, modular, and API-first authentication system for Rails developers who want full control without the black-box complexity. Whether you're building for the web, mobile, or both, Securial gives you the flexibility to implement exactly what you need — from simple JWT authentication to more advanced setups involving sessions, API tokens, and role-based access.
|
33
33
|
|
34
34
|
It follows familiar Rails conventions, stays lightweight and database-agnostic, and keeps security at the core. With fully customizable controllers, serializers, and logic, Securial is designed to grow with your project — making it an ideal choice for everything from side projects to production-grade APIs.
|
35
35
|
|
36
|
-
|
37
|
-
|
38
36
|
## 🚀 Installation
|
39
37
|
|
40
38
|
Securial can be installed on an existing Rails application or use the `securial new app_name` command to create a new Securial-ready Rails app.
|
@@ -94,7 +92,6 @@ Explore all modules in the [Wiki](https://github.com/AlyBadawy/Securial/wiki).
|
|
94
92
|
- Run `bundle install`
|
95
93
|
- Start coding right away 🏃♂️
|
96
94
|
|
97
|
-
|
98
95
|
To run the test suite:
|
99
96
|
|
100
97
|
```bash
|
@@ -14,9 +14,10 @@ module Securial
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def authenticate_admin!
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
authenticate_user!
|
18
|
+
return if current_user.blank? || current_user.is_admin?
|
19
|
+
|
20
|
+
render status: :forbidden, json: { error: "You are not authorized to perform this action" }
|
20
21
|
end
|
21
22
|
|
22
23
|
def current_user
|
@@ -28,26 +29,26 @@ module Securial
|
|
28
29
|
def authenticate_user!
|
29
30
|
return if internal_rails_request?
|
30
31
|
|
32
|
+
Current.session = nil
|
31
33
|
auth_header = request.headers["Authorization"]
|
32
34
|
if auth_header.present? && auth_header.start_with?("Bearer ")
|
33
35
|
token = auth_header.split(" ").last
|
34
36
|
begin
|
35
37
|
decoded_token = Securial::Auth::AuthEncoder.decode(token)
|
36
|
-
|
37
|
-
|
38
|
-
|
38
|
+
session = Session.find_by(id: decoded_token["jti"], revoked: false)
|
39
|
+
if session.present? &&
|
40
|
+
session.is_valid_session? &&
|
41
|
+
session.ip_address == request.remote_ip &&
|
42
|
+
session.user_agent == request.user_agent
|
43
|
+
Current.session = session
|
44
|
+
return # Authenticated
|
45
|
+
end
|
46
|
+
rescue Securial::Error::Auth::TokenDecodeError, ActiveRecord::RecordNotFound => e
|
47
|
+
Securial.logger.debug "Authentication failed: #{e.message}"
|
39
48
|
end
|
40
|
-
else
|
41
|
-
render status: :unauthorized, json: { error: "Missing or invalid Authorization header" } and return
|
42
49
|
end
|
43
|
-
|
44
|
-
|
45
|
-
def start_new_session_for(user)
|
46
|
-
Securial::Auth::SessionCreator.create_session(user, request)
|
47
|
-
end
|
48
|
-
|
49
|
-
def create_jwt_for_current_session
|
50
|
-
Securial::Auth::AuthEncoder.encode(Current.session)
|
50
|
+
# If we reach here, authentication failed
|
51
|
+
render status: :unauthorized, json: { error: "You are not signed in" } and return
|
51
52
|
end
|
52
53
|
|
53
54
|
def internal_rails_request?
|
@@ -7,12 +7,12 @@ module Securial
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def show
|
10
|
-
@securial_user = User.find_by(username: params.expect(:username))
|
10
|
+
@securial_user = Securial::User.find_by(username: params.expect(:username))
|
11
11
|
render_user_profile
|
12
12
|
end
|
13
13
|
|
14
14
|
def register
|
15
|
-
@securial_user = User.new(user_params)
|
15
|
+
@securial_user = Securial::User.new(user_params)
|
16
16
|
if @securial_user.save
|
17
17
|
render :show, status: :created, location: @securial_user
|
18
18
|
else
|
@@ -6,7 +6,7 @@ module Securial
|
|
6
6
|
def forgot_password
|
7
7
|
if user = User.find_by(email_address: params.require(:email_address))
|
8
8
|
user.generate_reset_password_token!
|
9
|
-
Securial::SecurialMailer.
|
9
|
+
Securial::SecurialMailer.forgot_password(user).deliver_later
|
10
10
|
end
|
11
11
|
|
12
12
|
render status: :ok, json: { message: "Password reset instructions sent (if user with that email address exists)." }
|
@@ -26,7 +26,7 @@ module Securial
|
|
26
26
|
def set_user_by_password_token
|
27
27
|
@user = User.find_by_reset_password_token!(params[:token]) # rubocop:disable Rails/DynamicFindBy
|
28
28
|
unless @user.reset_password_token_valid?
|
29
|
-
render status: :unprocessable_entity, json: { errors: { token: "is invalid or has expired" } }
|
29
|
+
render status: :unprocessable_entity, json: { errors: { token: "is invalid or has expired" } } and return
|
30
30
|
end
|
31
31
|
rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveRecord::RecordNotFound
|
32
32
|
render status: :unprocessable_entity, json: { errors: { token: "is invalid or has expired" } } and return
|
@@ -3,13 +3,12 @@ module Securial
|
|
3
3
|
def create
|
4
4
|
return unless define_user_and_role
|
5
5
|
|
6
|
-
if @
|
6
|
+
if @securial_user.roles.exists?(@securial_role.id)
|
7
7
|
render json: { error: "Role already assigned to user" }, status: :unprocessable_entity
|
8
8
|
return
|
9
9
|
end
|
10
10
|
@securial_role_assignment = RoleAssignment.new(securial_role_assignment_params)
|
11
11
|
@securial_role_assignment.save
|
12
|
-
@securial_user = @user
|
13
12
|
render :show, status: :created
|
14
13
|
end
|
15
14
|
|
@@ -18,7 +17,6 @@ module Securial
|
|
18
17
|
@role_assignment = RoleAssignment.find_by(securial_role_assignment_params)
|
19
18
|
if @role_assignment
|
20
19
|
@role_assignment.destroy!
|
21
|
-
@securial_user = @user
|
22
20
|
render :show, status: :ok
|
23
21
|
else
|
24
22
|
render json: { error: "Role is not assigned to user" }, status: :unprocessable_entity
|
@@ -28,13 +26,13 @@ module Securial
|
|
28
26
|
private
|
29
27
|
|
30
28
|
def define_user_and_role
|
31
|
-
@
|
32
|
-
@
|
33
|
-
if @
|
29
|
+
@securial_user = User.find_by(id: params.expect(securial_role_assignment: [:user_id]).dig(:user_id))
|
30
|
+
@securial_role = Role.find_by(id: params.expect(securial_role_assignment: [:role_id]).dig(:role_id))
|
31
|
+
if @securial_user.nil?
|
34
32
|
render json: { error: "User not found" }, status: :unprocessable_entity
|
35
33
|
return false
|
36
34
|
end
|
37
|
-
if @
|
35
|
+
if @securial_role.nil?
|
38
36
|
render json: { error: "Role not found" }, status: :unprocessable_entity
|
39
37
|
return false
|
40
38
|
end
|
@@ -31,14 +31,12 @@ module Securial
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def refresh
|
34
|
-
if Current.session = Session.find_by(refresh_token: params[:refresh_token])
|
35
|
-
if Current.session.
|
36
|
-
Current.session.ip_address == request.ip &&
|
37
|
-
Current.session.user_agent == request.user_agent
|
34
|
+
if Current.session = Securial::Session.find_by(refresh_token: params[:refresh_token])
|
35
|
+
if Current.session.is_valid_session_request?(request)
|
38
36
|
Current.session.refresh!
|
39
37
|
render status: :created,
|
40
38
|
json: {
|
41
|
-
access_token:
|
39
|
+
access_token: Securial::Auth::AuthEncoder.encode(Current.session),
|
42
40
|
refresh_token: Current.session.refresh_token,
|
43
41
|
refresh_token_expires_at: Current.session.refresh_token_expires_at,
|
44
42
|
}
|
@@ -64,7 +62,7 @@ module Securial
|
|
64
62
|
|
65
63
|
def set_session
|
66
64
|
id = params[:id]
|
67
|
-
@securial_session = id ? Current.user.sessions.find(params
|
65
|
+
@securial_session = id ? Current.user.sessions.find(params[:id]) : Current.session
|
68
66
|
end
|
69
67
|
|
70
68
|
def render_login_response(user)
|
@@ -75,10 +73,10 @@ module Securial
|
|
75
73
|
instructions: "Please reset your password before logging in.",
|
76
74
|
}
|
77
75
|
else
|
78
|
-
|
76
|
+
Securial::Auth::SessionCreator.create_session!(user, request)
|
79
77
|
render status: :created,
|
80
78
|
json: {
|
81
|
-
access_token:
|
79
|
+
access_token: Securial::Auth::AuthEncoder.encode(Current.session),
|
82
80
|
refresh_token: Current.session.refresh_token,
|
83
81
|
refresh_token_expires_at: Current.session.refresh_token_expires_at,
|
84
82
|
}
|
@@ -28,26 +28,27 @@ module Securial
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def destroy
|
31
|
-
@securial_user.destroy
|
31
|
+
@securial_user.destroy
|
32
|
+
head :no_content
|
32
33
|
end
|
33
34
|
|
34
35
|
private
|
35
36
|
|
36
37
|
def set_securial_user
|
37
|
-
@securial_user = User.find(params
|
38
|
+
@securial_user = User.find(params[:id])
|
38
39
|
end
|
39
40
|
|
40
41
|
def securial_user_params
|
41
42
|
params.expect(securial_user: [
|
42
43
|
:email_address,
|
43
|
-
:
|
44
|
-
:password_confirmation,
|
44
|
+
:username,
|
45
45
|
:first_name,
|
46
46
|
:last_name,
|
47
47
|
:phone,
|
48
|
-
:
|
49
|
-
:
|
50
|
-
|
48
|
+
:bio,
|
49
|
+
:password,
|
50
|
+
:password_confirmation
|
51
|
+
])
|
51
52
|
end
|
52
53
|
end
|
53
54
|
end
|
@@ -1,17 +1,28 @@
|
|
1
1
|
module Securial
|
2
2
|
class SecurialMailer < ApplicationMailer
|
3
|
-
|
3
|
+
def welcome(securial_user)
|
4
|
+
@user = securial_user
|
5
|
+
subject = Securial.configuration.mailer_sign_up_subject
|
6
|
+
mail subject: subject, to: securial_user.email_address
|
7
|
+
end
|
4
8
|
|
5
|
-
def
|
9
|
+
def sign_in(securial_user, securial_session)
|
6
10
|
@user = securial_user
|
7
|
-
|
11
|
+
@session = securial_session
|
12
|
+
subject = Securial.configuration.mailer_sign_in_subject
|
8
13
|
mail subject: subject, to: securial_user.email_address
|
9
14
|
end
|
10
15
|
|
11
|
-
|
16
|
+
def update_account(securial_user)
|
17
|
+
@user = securial_user
|
18
|
+
subject = Securial.configuration.mailer_update_account_subject
|
19
|
+
mail subject: subject, to: securial_user.email_address
|
20
|
+
end
|
12
21
|
|
13
|
-
def
|
14
|
-
|
22
|
+
def forgot_password(securial_user)
|
23
|
+
@user = securial_user
|
24
|
+
subject = Securial.configuration.mailer_forgot_password_subject
|
25
|
+
mail subject: subject, to: securial_user.email_address
|
15
26
|
end
|
16
27
|
end
|
17
28
|
end
|
@@ -25,7 +25,7 @@ module Securial
|
|
25
25
|
|
26
26
|
def generate_reset_password_token!
|
27
27
|
update!(
|
28
|
-
reset_password_token:
|
28
|
+
reset_password_token: Auth::TokenGenerator.generate_password_reset_token,
|
29
29
|
reset_password_token_created_at: Time.current
|
30
30
|
)
|
31
31
|
end
|
data/app/models/securial/role.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Securial
|
2
2
|
class Role < ApplicationRecord
|
3
|
-
normalizes :role_name, with: ->(e) { Securial::NormalizingHelper.normalize_role_name(e) }
|
3
|
+
normalizes :role_name, with: ->(e) { Securial::Helpers::NormalizingHelper.normalize_role_name(e) }
|
4
4
|
|
5
5
|
validates :role_name, presence: true, uniqueness: { case_sensitive: false }
|
6
6
|
|
@@ -11,17 +11,28 @@ module Securial
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def is_valid_session?
|
14
|
-
|
14
|
+
revoked? || expired? ? false : true
|
15
|
+
end
|
16
|
+
|
17
|
+
def is_valid_session_request?(request)
|
18
|
+
is_valid_session? && ip_address == request.ip && user_agent == request.user_agent
|
15
19
|
end
|
16
20
|
|
17
21
|
def refresh!
|
18
|
-
raise Securial::Auth::
|
19
|
-
raise Securial::Auth::
|
22
|
+
raise Securial::Error::Auth::TokenRevokedError if revoked?
|
23
|
+
raise Securial::Error::Auth::TokenExpiredError if expired?
|
24
|
+
|
25
|
+
new_refresh_token = Securial::Auth::TokenGenerator.generate_refresh_token
|
20
26
|
|
21
|
-
|
27
|
+
refresh_token_duration = Securial.configuration.session_refresh_token_expires_in
|
28
|
+
|
29
|
+
update!(refresh_token: new_refresh_token,
|
22
30
|
refresh_count: self.refresh_count + 1,
|
23
31
|
last_refreshed_at: Time.current,
|
24
|
-
refresh_token_expires_at:
|
32
|
+
refresh_token_expires_at: refresh_token_duration.from_now)
|
25
33
|
end
|
34
|
+
|
35
|
+
def revoked?; revoked; end
|
36
|
+
def expired?; refresh_token_expires_at < Time.current; end
|
26
37
|
end
|
27
38
|
end
|
data/app/models/securial/user.rb
CHANGED
@@ -2,7 +2,7 @@ module Securial
|
|
2
2
|
class User < ApplicationRecord
|
3
3
|
include Securial::PasswordResettable
|
4
4
|
|
5
|
-
normalizes :email_address, with: ->(e) { Securial::NormalizingHelper.normalize_email_address(e) }
|
5
|
+
normalizes :email_address, with: ->(e) { Securial::Helpers::NormalizingHelper.normalize_email_address(e) }
|
6
6
|
|
7
7
|
validates :email_address,
|
8
8
|
presence: true,
|
@@ -12,7 +12,7 @@ module Securial
|
|
12
12
|
maximum: 255,
|
13
13
|
},
|
14
14
|
format: {
|
15
|
-
with: Securial::RegexHelper::EMAIL_REGEX,
|
15
|
+
with: Securial::Helpers::RegexHelper::EMAIL_REGEX,
|
16
16
|
message: "must be a valid email address",
|
17
17
|
}
|
18
18
|
|
@@ -21,7 +21,7 @@ module Securial
|
|
21
21
|
uniqueness: { case_sensitive: false },
|
22
22
|
length: { maximum: 20 },
|
23
23
|
format: {
|
24
|
-
with: Securial::RegexHelper::USERNAME_REGEX,
|
24
|
+
with: Securial::Helpers::RegexHelper::USERNAME_REGEX,
|
25
25
|
message: "can only contain letters, numbers, underscores, and periods, but cannot start with a number or contain consecutive underscores or periods",
|
26
26
|
}
|
27
27
|
|
@@ -46,9 +46,7 @@ module Securial
|
|
46
46
|
|
47
47
|
|
48
48
|
def is_admin?
|
49
|
-
|
50
|
-
|
51
|
-
roles.exists?(role_name: titleized_role_name)
|
49
|
+
roles.exists?(role_name: Securial.titleized_admin_role)
|
52
50
|
end
|
53
51
|
end
|
54
52
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<h1>Password Reset Instructions for <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %></h1>
|
2
|
+
<p>
|
3
|
+
Hello <%= @user.first_name || @user.email_address %>,
|
4
|
+
</p>
|
5
|
+
<p>
|
6
|
+
We received a request to reset your password for your account.
|
7
|
+
</p>
|
8
|
+
<p>
|
9
|
+
To reset your password, use the following token:
|
10
|
+
</p>
|
11
|
+
<p style="font-size: 1.2em; font-weight: bold; letter-spacing: 2px; background: #f7f7f7; padding: 8px 16px; display: inline-block;">
|
12
|
+
<%= @user.reset_password_token %>
|
13
|
+
</p>
|
14
|
+
<p>
|
15
|
+
If you did not request a password reset, please ignore this email.
|
16
|
+
</p>
|
17
|
+
<p>
|
18
|
+
Best regards,<br>
|
19
|
+
The <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %> Team
|
20
|
+
</p>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Password Reset Instructions for <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %>
|
2
|
+
|
3
|
+
Hello <%= @user.first_name || @user.email_address %>,
|
4
|
+
|
5
|
+
We received a request to reset your password for your account.
|
6
|
+
|
7
|
+
To reset your password, use the following token:
|
8
|
+
|
9
|
+
<%= @user.reset_password_token %>
|
10
|
+
|
11
|
+
If you did not request a password reset, please ignore this email.
|
12
|
+
|
13
|
+
Best regards,
|
14
|
+
The <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %> Team
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<h1>Sign-in Alert for <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %></h1>
|
2
|
+
<p>
|
3
|
+
Hello <%= @user.first_name || @user.email_address %>,
|
4
|
+
</p>
|
5
|
+
<p>
|
6
|
+
We noticed a sign-in to your account.
|
7
|
+
</p>
|
8
|
+
<table style="border-collapse: collapse;">
|
9
|
+
<tr>
|
10
|
+
<td style="padding: 4px 8px; font-weight: bold;">IP Address:</td>
|
11
|
+
<td style="padding: 4px 8px;"><%= @session.ip_address %></td>
|
12
|
+
</tr>
|
13
|
+
<tr>
|
14
|
+
<td style="padding: 4px 8px; font-weight: bold;">Device/Browser:</td>
|
15
|
+
<td style="padding: 4px 8px;"><%= @session.user_agent %></td>
|
16
|
+
</tr>
|
17
|
+
<% if @session.respond_to?(:created_at) %>
|
18
|
+
<tr>
|
19
|
+
<td style="padding: 4px 8px; font-weight: bold;">Time:</td>
|
20
|
+
<td style="padding: 4px 8px;"><%= @session.created_at.strftime("%B %d, %Y at %H:%M %Z") %></td>
|
21
|
+
</tr>
|
22
|
+
<% end %>
|
23
|
+
</table>
|
24
|
+
<p>
|
25
|
+
If this was you, you can safely ignore this message.<br>
|
26
|
+
If you did not sign in, please reset your password immediately or contact support.
|
27
|
+
</p>
|
28
|
+
<p>
|
29
|
+
Best regards,<br>
|
30
|
+
The <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %> Team
|
31
|
+
</p>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Sign-in Alert for <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %>
|
2
|
+
|
3
|
+
Hello <%= @user.first_name || @user.email_address %>,
|
4
|
+
|
5
|
+
We noticed a sign-in to your account.
|
6
|
+
|
7
|
+
IP Address: <%= @session.ip_address %>
|
8
|
+
Device/Browser: <%= @session.user_agent %>
|
9
|
+
<% if @session.respond_to?(:created_at) %>
|
10
|
+
Time: <%= @session.created_at.strftime("%B %d, %Y at %H:%M %Z") %>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
If this was you, you can safely ignore this message.
|
14
|
+
If you did not sign in, please reset your password immediately or contact support.
|
15
|
+
|
16
|
+
Best regards,
|
17
|
+
The <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %> Team
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<h1>Your Account Has Been Updated on <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %></h1>
|
2
|
+
<p>
|
3
|
+
Hello <%= @user.first_name || @user.email_address %>,
|
4
|
+
</p>
|
5
|
+
<p>
|
6
|
+
This is a confirmation that changes were made to your account.
|
7
|
+
</p>
|
8
|
+
<p>
|
9
|
+
If you made these changes, you do not need to do anything further.<br>
|
10
|
+
If you did not update your account, please review your account security or contact support immediately.
|
11
|
+
</p>
|
12
|
+
<p>
|
13
|
+
Best regards,<br>
|
14
|
+
The <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %> Team
|
15
|
+
</p>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Your Account Has Been Updated on <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %>
|
2
|
+
|
3
|
+
Hello <%= @user.first_name || @user.email_address %>,
|
4
|
+
|
5
|
+
This is a confirmation that changes were made to your account.
|
6
|
+
|
7
|
+
If you made these changes, you do not need to do anything further.
|
8
|
+
If you did not update your account, please review your account security or contact support immediately.
|
9
|
+
|
10
|
+
Best regards,
|
11
|
+
The <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %> Team
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<h1>Welcome to <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %>, <%= @user.first_name || @user.email_address %>!</h1>
|
2
|
+
<p>
|
3
|
+
Thank you for signing up. We're excited to have you on board.
|
4
|
+
</p>
|
5
|
+
<p>
|
6
|
+
If you have any questions or need help getting started, feel free to reply to this email.
|
7
|
+
</p>
|
8
|
+
<p>
|
9
|
+
Best regards,<br>
|
10
|
+
The <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %> Team
|
11
|
+
</p>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
Welcome to <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %>, <%= @user.first_name || @user.email_address %>!
|
2
|
+
|
3
|
+
Thank you for signing up. We're excited to have you on board.
|
4
|
+
|
5
|
+
If you have any questions or need help getting started, feel free to reply to this email.
|
6
|
+
|
7
|
+
Best regards,
|
8
|
+
The <%= Securial.configuration.app_name || Rails.application.class.module_parent_name %> Team
|
@@ -1,15 +1,21 @@
|
|
1
1
|
json.id securial_user.id
|
2
2
|
|
3
|
+
json.email_address securial_user.email_address
|
4
|
+
json.username securial_user.username
|
3
5
|
json.first_name securial_user.first_name
|
4
6
|
json.last_name securial_user.last_name
|
5
7
|
json.phone securial_user.phone
|
6
|
-
json.username securial_user.username
|
7
8
|
json.bio securial_user.bio
|
9
|
+
json.email_verified securial_user.email_verified
|
10
|
+
json.locked securial_user.locked
|
11
|
+
json.locked_at securial_user.locked_at
|
8
12
|
|
9
|
-
json.password_expired securial_user.password_expired?
|
13
|
+
# json.password_expired securial_user.password_expired?
|
14
|
+
# This field is currently commented out because it is not required in the JSON response.
|
15
|
+
# Uncomment this line if the `password_expired` field needs to be included in the future.
|
10
16
|
|
11
17
|
json.roles securial_user.roles, partial: "securial/roles/securial_role", as: :securial_role
|
12
18
|
|
13
19
|
json.partial! "securial/shared/timestamps", record: securial_user
|
14
20
|
|
15
|
-
json.url securial.
|
21
|
+
json.url securial.users_url(securial_user, format: :json)
|
data/config/routes.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
+
Rails.application.routes.default_url_options = { host: "localhost", port: 3000, protocol: "http" }
|
2
|
+
|
1
3
|
Securial::Engine.routes.draw do
|
4
|
+
default_url_options host: "example.com"
|
5
|
+
|
2
6
|
defaults format: :json do
|
3
7
|
get "/status", to: "status#show", as: :status
|
4
8
|
|
5
|
-
scope Securial.
|
9
|
+
scope Securial.protected_namespace do
|
6
10
|
resources :roles
|
7
11
|
resources :users
|
8
12
|
namespace :role_assignments, as: "role_assignments" do
|
data/db/migrate/{20250517155521_create_securial_users.rb → 20250604110520_create_securial_users.rb}
RENAMED
@@ -1,16 +1,21 @@
|
|
1
1
|
class CreateSecurialUsers < ActiveRecord::Migration[8.0]
|
2
2
|
def change
|
3
3
|
create_table :securial_users, id: :string do |t|
|
4
|
-
t.string :email_address
|
4
|
+
t.string :email_address, null: false
|
5
|
+
t.boolean :email_verified, null: false, default: false
|
6
|
+
t.string :email_verification_token
|
7
|
+
t.datetime :email_verification_token_created_at
|
8
|
+
t.string :username, null: false
|
5
9
|
t.string :first_name
|
6
10
|
t.string :last_name
|
7
11
|
t.string :phone
|
8
|
-
t.string :username
|
9
12
|
t.string :bio
|
10
13
|
t.string :password_digest
|
14
|
+
t.datetime :password_changed_at
|
11
15
|
t.string :reset_password_token
|
12
16
|
t.datetime :reset_password_token_created_at
|
13
|
-
t.
|
17
|
+
t.boolean :locked, null: false, default: false
|
18
|
+
t.datetime :locked_at
|
14
19
|
|
15
20
|
t.timestamps
|
16
21
|
end
|
@@ -2,13 +2,17 @@ class CreateSecurialSessions < ActiveRecord::Migration[8.0]
|
|
2
2
|
def change
|
3
3
|
create_table :securial_sessions, id: :string do |t|
|
4
4
|
t.references :user, null: false, type: :string, foreign_key: { to_table: :securial_users }
|
5
|
+
|
5
6
|
t.string :ip_address, null: false
|
6
7
|
t.string :user_agent, null: false
|
8
|
+
|
7
9
|
t.string :refresh_token, null: false
|
8
10
|
t.integer :refresh_count, default: 0
|
9
11
|
t.datetime :last_refreshed_at
|
10
12
|
t.datetime :refresh_token_expires_at
|
13
|
+
|
11
14
|
t.boolean :revoked, default: false, null: false
|
15
|
+
|
12
16
|
t.timestamps
|
13
17
|
end
|
14
18
|
end
|