securial 0.8.1 → 1.0.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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -16
  3. data/app/controllers/concerns/securial/identity.rb +18 -9
  4. data/app/controllers/securial/status_controller.rb +2 -0
  5. data/app/controllers/securial/users_controller.rb +1 -1
  6. data/app/views/securial/status/show.json.jbuilder +1 -1
  7. data/bin/securial +20 -52
  8. data/db/migrate/20250606182648_seed_roles_and_users.rb +69 -0
  9. data/lib/generators/securial/install/templates/securial_initializer.erb +115 -18
  10. data/lib/securial/cli/run.rb +11 -0
  11. data/lib/securial/cli/securial_new.rb +53 -0
  12. data/lib/securial/cli/show_help.rb +26 -0
  13. data/lib/securial/cli/show_version.rb +9 -0
  14. data/lib/securial/config/configuration.rb +3 -53
  15. data/lib/securial/config/signature.rb +107 -0
  16. data/lib/securial/config/validation.rb +58 -14
  17. data/lib/securial/config.rb +2 -0
  18. data/lib/securial/engine.rb +2 -0
  19. data/lib/securial/engine_initializers.rb +21 -2
  20. data/lib/securial/error/config.rb +0 -28
  21. data/lib/securial/helpers/key_transformer.rb +33 -0
  22. data/lib/securial/helpers.rb +1 -0
  23. data/lib/securial/middleware/response_headers.rb +19 -0
  24. data/lib/securial/middleware/transform_request_keys.rb +35 -0
  25. data/lib/securial/middleware/transform_response_keys.rb +47 -0
  26. data/lib/securial/middleware.rb +3 -0
  27. data/lib/securial/security/request_rate_limiter.rb +45 -0
  28. data/lib/securial/security.rb +8 -0
  29. data/lib/securial/version.rb +1 -1
  30. data/lib/tasks/securial_routes.rake +26 -0
  31. metadata +44 -19
  32. data/lib/securial/config/validation/logger_validation.rb +0 -29
  33. data/lib/securial/config/validation/mailer_validation.rb +0 -24
  34. data/lib/securial/config/validation/password_validation.rb +0 -91
  35. data/lib/securial/config/validation/response_validation.rb +0 -37
  36. data/lib/securial/config/validation/roles_validation.rb +0 -32
  37. data/lib/securial/config/validation/security_validation.rb +0 -56
  38. data/lib/securial/config/validation/session_validation.rb +0 -87
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e9f576f089867c769449ef4dff49a41632a053b5b0f45ba8e8c7843532ea0869
4
- data.tar.gz: 06eeee1379bd6c6a0859f3b8ae43cfa177dc1e99a7a20c82f0c50890472d655e
3
+ metadata.gz: 6d6f0817d44bf16fef31a683152d11e38a0671a92f966df31257f8ab9b867c92
4
+ data.tar.gz: ec7aa78f714a20598db55ecf92d69dc17d7dd38a137cce3964d8677ec622a46a
5
5
  SHA512:
6
- metadata.gz: afe5443ee10dda1175f26d148c3588fde6fa81b607f85f2969487d38c82ffda6e31be4280409ecd2a6d6a2844fe511e6829a8d9cdece1acb40415b9c99182618
7
- data.tar.gz: c2b18a4562e0995bbcf160f096461e90853f32bd8134e4d5d9e9e53d411a4e40e4eda462744f3d2d6b244918aa60887749ef1383d44ff3de3d4800ef366e88c2
6
+ metadata.gz: 9435fcba0cb665df4992f193f0a8c0012f3dc676619a20b8824cdd085803337290c2453bf10494ef348d46259d0f1f9da48224abb1e70783c0f67e5e33deff2c
7
+ data.tar.gz: 658cca7386b481757396384957d203f35abfb2f72ea56e7c538ea3f355014068e4921f7b4ba03967caa9563b5d3d4d32a1d4bf0257a0676e413bb2a1a394fbfd
data/README.md CHANGED
@@ -8,26 +8,21 @@
8
8
  [![Coveralls](https://img.shields.io/coverallsCoverage/github/AlyBadawy/Securial?branch=main&logo=coveralls&logoColor=%233F5767&labelColor=ddeedd)
9
9
  ](https://coveralls.io/github/AlyBadawy/Securial?branch=main)
10
10
 
11
- > [!WARNING]
12
- >
13
- > **Securial is currently in active development (major version zero).**
14
- >
15
- > While the gem is functional and versioned, it is not yet considered stable. Until v1.0.0 is released, any updates may introduce breaking changes as the API and features continue to evolve. If you plan to use Securial in production, please do so with caution and pin a specific version.
16
- >
17
- > You can track the roadmap and remaining tasks for the v1.0.0 release in [this GitHub issue](https://github.com/AlyBadawy/Securial/issues/64).
18
-
19
11
  ---
20
12
 
13
+ ### 🛡️ What is Securial?
14
+
21
15
  **Securial** is a mountable Rails engine that provides robust, extensible authentication and access control for Rails applications. It supports:
22
16
 
23
- - JWT-based authentication
24
- - API tokens
25
- - Session-based auth
26
- - Simple integration with web and mobile apps
27
- - Clean, JSON-based API responses
28
- - Database-agnostic support
17
+ - 🟦 JWT-based authentication
18
+ - 🟦 API tokens
19
+ - 🟦 Session-based auth
20
+ - 🟦 Simple integration with web and mobile apps
21
+ - 🟦 Clean, JSON-based API responses
22
+ - 🟦 Simple user management with roles
23
+ - 🟦 Database-agnostic support
29
24
 
30
- ### 🚀 Why Securial?
25
+ ### 👀 Why Securial?
31
26
 
32
27
  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
28
 
@@ -48,6 +43,8 @@ To add Securial to an existing Rails app:
48
43
  ```ruby
49
44
  Rails.application.routes.draw do
50
45
  mount Securial::Engine => "/securial"
46
+
47
+ # The rest of your routes
51
48
  end
52
49
  ```
53
50
  - Run the migrations by running the command: `rails db:migrate`
@@ -120,4 +117,5 @@ The gem is available as open source under the terms of the [MIT license](https:/
120
117
 
121
118
  ---
122
119
 
123
- ![image](https://github.com/user-attachments/assets/d7cb9645-c28e-4cca-9c1b-5085a91c11d4)
120
+ \
121
+ ![logo-e9f16c9b 1](https://github.com/AlyBadawy/AlyBadawy/assets/1198568/471e5332-f8d0-4b78-a333-7e207780ecc1)
@@ -3,6 +3,7 @@ module Securial
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
+ before_action :identify_user
6
7
  before_action :authenticate_user!
7
8
  helper_method :current_user if respond_to?(:helper_method)
8
9
  end
@@ -13,20 +14,24 @@ module Securial
13
14
  end
14
15
  end
15
16
 
16
- def authenticate_admin!
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" }
21
- end
22
17
 
23
18
  def current_user
24
19
  Current.session&.user
25
20
  end
26
21
 
22
+ def authenticate_admin!
23
+ if current_user
24
+ return if current_user.is_admin?
25
+
26
+ render status: :forbidden, json: { error: "You are not authorized to perform this action" }
27
+ else
28
+ authenticate_user!
29
+ end
30
+ end
31
+
27
32
  private
28
33
 
29
- def authenticate_user!
34
+ def identify_user
30
35
  return if internal_rails_request?
31
36
 
32
37
  Current.session = nil
@@ -41,13 +46,17 @@ module Securial
41
46
  session.ip_address == request.remote_ip &&
42
47
  session.user_agent == request.user_agent
43
48
  Current.session = session
44
- return # Authenticated
45
49
  end
46
50
  rescue Securial::Error::Auth::TokenDecodeError, ActiveRecord::RecordNotFound => e
47
51
  Securial.logger.debug "Authentication failed: #{e.message}"
48
52
  end
49
53
  end
50
- # If we reach here, authentication failed
54
+ end
55
+
56
+ def authenticate_user!
57
+ return if internal_rails_request?
58
+ return if Current.session&.user
59
+
51
60
  render status: :unauthorized, json: { error: "You are not signed in" } and return
52
61
  end
53
62
 
@@ -3,7 +3,9 @@ module Securial
3
3
  skip_authentication!
4
4
 
5
5
  def show
6
+ @current_user = current_user
6
7
  Securial.logger.info("Status check initiated")
8
+ render :show, status: :ok
7
9
  end
8
10
  end
9
11
  end
@@ -47,7 +47,7 @@ module Securial
47
47
  :phone,
48
48
  :bio,
49
49
  :password,
50
- :password_confirmation
50
+ :password_confirmation,
51
51
  ])
52
52
  end
53
53
  end
@@ -1,3 +1,3 @@
1
1
  json.status "ok"
2
2
  json.timestamp Time.current
3
- json.version Securial::VERSION
3
+ json.version Securial::VERSION if @current_user&.is_admin?
data/bin/securial CHANGED
@@ -1,58 +1,26 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "fileutils"
4
-
5
- def run(command, chdir: nil)
6
- puts "→ #{command}"
7
- if chdir
8
- Dir.chdir(chdir) do
9
- system(command) || abort("❌ Command failed: #{command}")
10
- end
4
+ require_relative "../lib/securial/cli/show_version"
5
+ require_relative "../lib/securial/cli/show_help"
6
+ require_relative "../lib/securial/cli/run"
7
+ require_relative "../lib/securial/cli/securial_new"
8
+
9
+ case ARGV[0]
10
+ when "-v", "--version"
11
+ show_version
12
+ when "-h", "--help"
13
+ show_help
14
+ when "new"
15
+ if ARGV[1]
16
+ app_name = ARGV[1]
17
+ rails_options = ARGV[2..] || []
18
+ securial_new(app_name, rails_options)
11
19
  else
12
- system(command) || abort("❌ Command failed: #{command}")
13
- end
14
- end
15
-
16
-
17
- def securial_new(app_name, rails_options)
18
- puts "🛠 Creating new Rails app: #{app_name}"
19
-
20
- # Step 1: Forward options to `rails new`
21
- rails_command = ["rails", "new", app_name, *rails_options].join(" ")
22
- run(rails_command)
23
-
24
- # Step 2: Append securial gem to the Gemfile
25
- gemfile_path = File.join(app_name, "Gemfile")
26
- File.open(gemfile_path, "a") do |f|
27
- f.puts "\ngem 'securial'"
20
+ show_help
21
+ exit(1)
28
22
  end
29
-
30
- # Step 3: Install gems
31
- run("bundle install", chdir: app_name)
32
-
33
- # Step 4: Install securial
34
- run("bin/rails generate securial:install", chdir: app_name)
35
- run("bin/rails db:migrate", chdir: app_name)
36
-
37
- # Step 5: Mount the engine
38
- routes_path = File.join(app_name, "config/routes.rb")
39
- routes = File.read(routes_path)
40
- updated = routes.sub("Rails.application.routes.draw do") do |match|
41
- "#{match}\n mount Securial::Engine => '/securial'"
42
- end
43
- File.write(routes_path, updated)
44
-
45
- puts "\n✅ Done! Your app is ready at: ./#{app_name}"
46
- puts "➡️ Next steps:"
47
- puts " cd #{app_name}"
48
- puts " rails server"
49
- end
50
-
51
- if ARGV[0] == "new" && ARGV[1]
52
- app_name = ARGV[1]
53
- rails_options = ARGV[2..] || []
54
- securial_new(app_name, rails_options)
55
23
  else
56
- puts "Usage: securial new app_name [rails_options...]"
57
- puts "Example: securial new myapp --api --database=postgresql"
58
- end
24
+ show_help
25
+ exit(1)
26
+ end
@@ -0,0 +1,69 @@
1
+ class SeedRolesAndUsers < ActiveRecord::Migration[8.0]
2
+ def up
3
+ default_email = "user@example.com"
4
+ default_password = "Password123!"
5
+
6
+ default_admin_role = Securial.configuration.admin_role
7
+
8
+ # Create a default user (no roles assigned)
9
+ Securial::User.find_or_create_by!(email_address: default_email) do |user|
10
+ user.password = default_password
11
+ user.password_confirmation = default_password
12
+ user.username = "default_user"
13
+ user.first_name = "Default"
14
+ user.last_name = "User"
15
+ end
16
+ Securial.logger.debug "Created user: #{default_email} with password: #{default_password}"
17
+
18
+ # Default roles
19
+ roles = %w[admin moderator support locked]
20
+ roles << default_admin_role
21
+ roles.uniq!
22
+
23
+ roles.each do |role_name|
24
+ # Create roles if they do not exist
25
+ Securial::Role.find_or_create_by!(role_name: role_name)
26
+ Securial.logger.debug "Created role: #{role_name}"
27
+
28
+ # Create a user for each role
29
+ user = Securial::User.find_or_create_by!(email_address: "#{role_name}@example.com") do |u|
30
+ u.password = "Password123!"
31
+ u.password_confirmation = "Password123!"
32
+ u.username = role_name
33
+ u.first_name = role_name.capitalize
34
+ u.last_name = "User"
35
+ end
36
+
37
+ Securial.logger.debug "Created user: #{user.email_address} with password: #{default_password}"
38
+
39
+ # Assign the role to the user
40
+ user.roles = Securial::Role.where(role_name: role_name)
41
+ Securial.logger.debug "Assigned role: #{role_name} to user: #{user.email_address}"
42
+ user.save!
43
+ end
44
+ end
45
+
46
+ def down
47
+ default_admin_role = Securial.configuration.admin_role
48
+ roles = %w[admin moderator support locked]
49
+ roles << default_admin_role
50
+ roles.uniq!
51
+
52
+ # Remove role-specific users
53
+ roles.each do |role_name|
54
+ user = Securial::User.find_by(email_address: "#{role_name}@example.com")
55
+ user.destroy if user
56
+ Securial.logger.debug "Removed user: #{role_name}@example.com"
57
+ end
58
+
59
+ # Remove the default user with no roles
60
+ user = Securial::User.find_by(email_address: "user@example.com")
61
+ user.destroy if user
62
+ Securial.logger.debug "Removed user: user@example.com"
63
+
64
+
65
+ # Remove the roles themselves
66
+ Securial::Role.where(role_name: roles).destroy_all
67
+ Securial.logger.debug "Removed roles: #{roles.join(', ')}"
68
+ end
69
+ end
@@ -1,6 +1,11 @@
1
1
  # This file was generated by the Securial engine.
2
2
 
3
3
  Securial.configure do |config|
4
+ ##### General Configuration
5
+ ## The name of your application (used in notifications, etc.)
6
+ # Default: "Securial"
7
+ config.app_name = "Securial"
8
+
4
9
  ##### Logging Configuration
5
10
  ## Enable or disable logging to file
6
11
  # Set to true to log to a file, false to disable file logging
@@ -28,9 +33,9 @@ Securial.configure do |config|
28
33
 
29
34
  ##### Session Configuration
30
35
  ## Set the session expiration duration
31
- # This is the time after which a session will be considered expired.
32
- # After this time, the session will be invalidated and the user
33
- # will need to log in again to refresh the session.
36
+ # This is the time after which an access session will be considered expired.
37
+ # After this time, the user must use the refresh token to obtain a new session.
38
+ # The user only needs to log in again if the refresh token has also expired.
34
39
  # The expiration time is set in seconds, minutes, or hours.
35
40
  # The default is 3 minutes.
36
41
  config.session_expiration_duration = 3.minutes
@@ -39,6 +44,17 @@ Securial.configure do |config|
39
44
  # This secret is used to sign the session tokens and ensure
40
45
  # that they cannot be tampered with. It is important to keep this
41
46
  # secret secure and not share it with anyone.
47
+ # It should be a long, random string to ensure security.
48
+ # You can generate a secure secret using a tool like `SecureRandom.hex(64)`
49
+ # or `SecureRandom.base64(64)`.
50
+ # It is recommended to change this secret periodically for security reasons.
51
+ # If you change this secret, all existing sessions will be invalidated,
52
+ # and users will need to log in again.
53
+ # Make sure to keep this secret safe and do not expose it in your codebase.
54
+ # You can use environment variables to store this secret securely.
55
+ # For example, you can set an environment variable `SECURIAL_SESSION_SECRET`
56
+ # and then use it in your configuration like this:
57
+ # config.session_secret = ENV['SECURIAL_SESSION_SECRET']
42
58
  # The default is "secret".
43
59
  config.session_secret = "secret"
44
60
 
@@ -48,6 +64,20 @@ Securial.configure do |config|
48
64
  # Other options include :hs256, :hs384, and :hs512
49
65
  config.session_algorithm = :hs256
50
66
 
67
+ ## Set the session refresh token expiration duration
68
+ # This is the duration after which a refresh token will expire.
69
+ # Once the refresh token expires, the user must log in again.
70
+ # The refresh token is used to obtain a new access token without
71
+ # requiring the user to log in again. It is typically used to keep
72
+ # the user logged in for a longer period without requiring frequent logins.
73
+ # The expiration time is set in seconds, minutes, or hours.
74
+ # The refresh token expiration duration should be longer than the access session
75
+ # expiration duration to allow the user to obtain a new access token
76
+ # without having to log in again. This allows for a better user experience
77
+ # by reducing the frequency of logins while still maintaining security.
78
+ # The default is 1 week.
79
+ config.session_refresh_token_expires_in = 1.week
80
+
51
81
  ##### Securial Mailer Configuration
52
82
  ## Set the mailer sender address
53
83
  # This is the email address that will be used as the sender
@@ -56,12 +86,36 @@ Securial.configure do |config|
56
86
  # notifications.
57
87
  config.mailer_sender = "no-reply@example.com"
58
88
 
59
- ##### Password configuration
89
+ ## Enable or disable sending sign up emails
90
+ # Set to true to send a welcome email on sign up, false to disable.
91
+ config.mailer_sign_up_enabled = true
92
+
93
+ ## Set the subject for sign up emails
94
+ # The default is "SECURIAL: Welcome to Our Service".
95
+ config.mailer_sign_up_subject = "SECURIAL: Welcome to Our Service"
96
+
97
+ ## Enable or disable sending sign in emails
98
+ # Set to true to send a notification on sign in, false to disable.
99
+ config.mailer_sign_in_enabled = true
100
+
101
+ ## Set the subject for sign in emails
102
+ # The default is "SECURIAL: Sign In Notification".
103
+ config.mailer_sign_in_subject = "SECURIAL: Sign In Notification"
104
+
105
+ ## Enable or disable sending update account emails
106
+ # Set to true to send a notification when an account is updated, false to disable.
107
+ config.mailer_update_account_enabled = true
108
+
109
+ ## Set the subject for update account emails
110
+ # The default is "SECURIAL: Account Update Notification".
111
+ config.mailer_update_account_subject = "SECURIAL: Account Update Notification"
112
+
60
113
  ## Set the password reset email subject
61
114
  # This is the subject line that will be used for the password reset
62
- # email. The default is "Password Reset Instructions".
63
- config.mailer_forgot_password_subject = "Password Reset Instructions"
115
+ # email. The default is "SECURIAL: Password Reset Instructions".
116
+ config.mailer_forgot_password_subject = "SECURIAL: Password Reset Instructions"
64
117
 
118
+ ##### Password configuration
65
119
  ## Set the minimum password length
66
120
  # This is the minimum length that a password must have
67
121
  # in order to be considered valid. The default is 8 characters.
@@ -73,30 +127,71 @@ Securial.configure do |config|
73
127
  config.password_max_length = 128
74
128
 
75
129
  ## Set the password complexity requirements
76
- # This is a regular expression that defines the complexity
77
- # requirements for a password. The default is a regex that
78
- # requires at least one uppercase letter, one lowercase letter,
79
- # one digit, and one special character.
130
+ # This is a regular expression (RegEx) that defines the complexity
131
+ # requirements for a password.
132
+ # The password must match this regex in order to be considered valid.
133
+ # The regex can include requirements such as:
134
+ # - At least one uppercase letter
135
+ # - At least one lowercase letter
136
+ # - At least one digit
137
+ # - At least one special character (e.g. !@#$%^&*)
138
+ # - No whitespace characters
139
+ # - No common passwords (e.g. "password", "123456", etc.)
140
+ # You can customize this regex to fit your application's security requirements.
141
+ # The default is a regex that requires at least one uppercase letter,
142
+ # one lowercase letter, one digit, and one special character.
80
143
  config.password_complexity = Securial::Helpers::RegexHelper::PASSWORD_REGEX
81
144
 
145
+ ## Set whether passwords expire
146
+ # Set to true if passwords should expire after a period, false to disable expiration.
147
+ # If password expiration is enabled, users will be required to change
148
+ # their password after a certain period of time. This is useful for
149
+ # maintaining security and ensuring that users do not use the same password
150
+ # for an extended period of time. It is recommended to enable password expiration
151
+ # to enhance security, especially for applications that handle sensitive data.
152
+ # The default is true.
153
+ config.password_expires = true
154
+
82
155
  ## Set the password expiration duration
83
- # This is the time after which a password will be considered expired.
156
+ # This is the time after which a password will be considered expired,
157
+ # but only if password expiration is enabled (`password_expires = true`).
84
158
  # After this time, the user will need to change their password
85
159
  # in order to log in again. The expiration time is set in seconds,
86
160
  # minutes, or hours. The default is 90 days.
87
161
  config.password_expires_in = 90.days
88
-
162
+
89
163
  ## Set the password reset token expiration duration
90
164
  # This is the time after which a password reset token will be considered expired.
91
165
  # After this time, the token will no longer be valid and the user
92
166
  # will need to request a new password reset token. The expiration time
93
- # is set in seconds, minutes, or hours. The default is 2 hours.
167
+ # is set in seconds, minutes, or hours.
168
+ # This is useful for ensuring that password reset tokens are not valid indefinitely,
169
+ # which helps to prevent unauthorized access to user accounts.
170
+ # It is recommended to set a reasonable expiration time for password reset tokens
171
+ # to balance security and user experience. A shorter expiration time
172
+ # can enhance security by reducing the window of opportunity for an attacker
173
+ # to use a stolen or intercepted token, while a longer expiration time
174
+ # can improve user experience by allowing users to reset their passwords
175
+ # without having to request a new token frequently.
176
+ # The expiration time is set in seconds, minutes, or hours.
177
+ # The default is 2 hours.
94
178
  config.reset_password_token_expires_in = 2.hours
95
179
 
96
180
  ## Set the password reset token secret
97
181
  # This secret is used to sign the password reset tokens and ensure
98
182
  # that they cannot be tampered with. It is important to keep this
99
183
  # secret secure and not share it with anyone.
184
+ # It should be a long, random string to ensure security.
185
+ # You can generate a secure secret using a tool like `SecureRandom.hex(64)`
186
+ # or `SecureRandom.base64(64)`.
187
+ # It is recommended to change this secret periodically for security reasons.
188
+ # If you change this secret, all existing sessions will be invalidated,
189
+ # and users will need to log in again.
190
+ # Make sure to keep this secret safe and do not expose it in your codebase.
191
+ # You can use environment variables to store this secret securely.
192
+ # For example, you can set an environment variable `SECURIAL_RESET_PASSWORD_SECRET`
193
+ # and then use it in your configuration like this:
194
+ # config.reset_password_token_secret = ENV['SECURIAL_RESET_PASSWORD_SECRET']
100
195
  config.reset_password_token_secret = "reset_secret"
101
196
 
102
197
  ##### Timestamp Configuration
@@ -108,14 +203,14 @@ Securial.configure do |config|
108
203
  # for regular users while still providing timestamps for admin users.
109
204
  # :all - the created_at and updated_at timestamps will be included
110
205
  # for all users. This is useful for debugging and development purposes.
111
- config.timestamps_in_response = Rails.env.production? ? :admins_only : :all
206
+ config.timestamps_in_response = :all
112
207
 
113
208
  ##### Response Configuration
114
209
  ## Set the format of the JSON keys in the responses.
115
210
  # The options are:
116
211
  # :snake_case - the keys will be in snake_case format.
117
212
  # :lowerCamelCase - the keys will be in lowerCamelCase format.
118
- # :upperCamelCase - the keys will be in UpperCamelCase format.
213
+ # :UpperCamelCase - the keys will be in UpperCamelCase format.
119
214
  config.response_keys_format = :snake_case
120
215
 
121
216
  ##### Security Configuration
@@ -125,14 +220,17 @@ Securial.configure do |config|
125
220
  # :default - the default security headers will be included.
126
221
  # :strict - the strict security headers will be included.
127
222
  config.security_headers = :strict
223
+
128
224
  ## set whether to enable request rate limiting
129
225
  # This is useful for preventing abuse and denial of service attacks.
130
226
  config.rate_limiting_enabled = true
227
+
131
228
  ## Set the rate limit for requests
132
229
  # This is the maximum number of requests that a user can make
133
230
  # in a given time period. The default is 60 requests per minute.
134
231
  # This is only applied if `rate_limiting_enabled` is set to true.
135
232
  config.rate_limit_requests_per_minute = 60
233
+
136
234
  ## Set the rate limit response status code
137
235
  # This is the status code that will be returned when a user exceeds
138
236
  # the rate limit. The status code should be a 4xx or 5xx code
@@ -140,11 +238,10 @@ Securial.configure do |config|
140
238
  # or 503 Service Unavailable. The default is 429 Too Many Requests.
141
239
  # This is only applied if `rate_limiting_enabled` is set to true.
142
240
  config.rate_limit_response_status = 429
241
+
143
242
  ## Set the rate limit response message
144
243
  # This is the message that will be returned when a user exceeds
145
244
  # the rate limit. The default is "Too many requests, please try again later."
146
245
  # This is only applied if `rate_limiting_enabled` is set to true.
147
246
  config.rate_limit_response_message = "Too many requests, please try again later."
148
-
149
-
150
- end
247
+ end
@@ -0,0 +1,11 @@
1
+ # rubocop:disable Rails/Output
2
+ def run(command, chdir: nil)
3
+ puts "→ #{command}"
4
+ if chdir
5
+ Dir.chdir(chdir) do
6
+ system(command) || abort("❌ Command failed: #{command}")
7
+ end
8
+ else
9
+ system(command) || abort("❌ Command failed: #{command}")
10
+ end
11
+ end
@@ -0,0 +1,53 @@
1
+ # rubocop:disable Rails/Output
2
+ require_relative "run"
3
+
4
+ def securial_new(app_name, rails_options)
5
+ puts "🏗️ Creating new Rails app: #{app_name}"
6
+ create_rails_app(app_name, rails_options)
7
+ add_securial_gem(app_name)
8
+ install_gems(app_name)
9
+ install_securial(app_name)
10
+ mount_securial_engine(app_name)
11
+ print_final_instructions(app_name)
12
+ end
13
+
14
+ def create_rails_app(app_name, rails_options)
15
+ rails_command = ["rails", "new", app_name, *rails_options].join(" ")
16
+ run(rails_command)
17
+ end
18
+
19
+ def add_securial_gem(app_name)
20
+ puts "📦 Adding Securial gem to Gemfile"
21
+ gemfile_path = File.join(app_name, "Gemfile")
22
+ File.open(gemfile_path, "a") { |f| f.puts "\ngem 'securial'" }
23
+ end
24
+
25
+ def install_gems(app_name)
26
+ run("bundle install", chdir: app_name)
27
+ end
28
+
29
+ def install_securial(app_name)
30
+ puts "🔧 Installing Securial"
31
+ run("bin/rails generate securial:install", chdir: app_name)
32
+ run("bin/rails db:migrate", chdir: app_name)
33
+ end
34
+
35
+ def mount_securial_engine(app_name)
36
+ puts "🔗 Mounting Securial engine in routes"
37
+ routes_path = File.join(app_name, "config/routes.rb")
38
+ routes = File.read(routes_path)
39
+ updated = routes.sub("Rails.application.routes.draw do") do |match|
40
+ "#{match}\n mount Securial::Engine => '/securial'"
41
+ end
42
+ File.write(routes_path, updated)
43
+ end
44
+
45
+ def print_final_instructions(app_name)
46
+ puts "🎉 Securial has been successfully installed in your Rails app!"
47
+ puts "✅ Your app is ready at: ./#{app_name}"
48
+ puts ""
49
+ puts "➡️ Next steps:"
50
+ puts " cd #{app_name}"
51
+ puts "⚙️ Optional: Configure Securial in config/initializers/securial.rb"
52
+ puts " rails server"
53
+ end
@@ -0,0 +1,26 @@
1
+ # rubocop:disable Rails/Output
2
+ def show_help
3
+ puts <<~HELP
4
+ Securial CLI
5
+
6
+ Securial is a mountable Rails engine that provides robust, extensible
7
+ authentication and access control for Rails applications. It supports JWT,
8
+ API tokens, session-based auth, and is designed for easy integration with
9
+ modern web and mobile apps.
10
+
11
+ Usage:
12
+ securial new APP_NAME [rails_options...] # Create a new Rails app with Securial pre-installed
13
+ securial -v, --version # Show the Securial gem version
14
+ securial -h, --help # Show this help message
15
+
16
+ Example:
17
+ securial new myapp --api --database=postgresql -T
18
+
19
+ More Info:
20
+ review the [Changelog] and [WIKI] for more info on the latest
21
+ changes and how to use this gem/engine:
22
+ [Changelog]: https://github.com/AlyBadawy/Securial/blob/main/CHANGELOG.md
23
+ [WIKI]: https://github.com/AlyBadawy/Securial/wiki
24
+
25
+ HELP
26
+ end
@@ -0,0 +1,9 @@
1
+ # rubocop:disable Rails/Output
2
+ def show_version
3
+ begin
4
+ require "securial/version"
5
+ puts "Securial v#{Securial::VERSION}"
6
+ rescue LoadError
7
+ puts "Securial version information not available."
8
+ end
9
+ end