securial 1.0.2 → 1.0.3
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 +6 -4
- data/lib/securial/auth/auth_encoder.rb +78 -0
- data/lib/securial/auth/session_creator.rb +49 -0
- data/lib/securial/auth/token_generator.rb +74 -0
- data/lib/securial/config/signature.rb +116 -5
- data/lib/securial/config/validation.rb +91 -0
- data/lib/securial/helpers/key_transformer.rb +106 -0
- data/lib/securial/helpers/normalizing_helper.rb +69 -0
- data/lib/securial/helpers/regex_helper.rb +122 -0
- data/lib/securial/helpers/roles_helper.rb +71 -2
- data/lib/securial/middleware/request_tag_logger.rb +80 -0
- data/lib/securial/middleware/response_headers.rb +51 -3
- data/lib/securial/middleware/transform_request_keys.rb +143 -20
- data/lib/securial/middleware/transform_response_keys.rb +84 -4
- data/lib/securial/version.rb +1 -1
- metadata +20 -15
@@ -1,35 +1,158 @@
|
|
1
|
+
# @title Securial Transform Request Keys Middleware
|
2
|
+
#
|
3
|
+
# Rack middleware for transforming JSON request body keys to Ruby conventions.
|
4
|
+
#
|
5
|
+
# This middleware automatically converts incoming JSON request body keys from
|
6
|
+
# camelCase or PascalCase to snake_case, allowing Rails applications to receive
|
7
|
+
# JavaScript-style APIs while maintaining Ruby naming conventions internally.
|
8
|
+
# It only processes JSON requests and gracefully handles malformed JSON.
|
9
|
+
#
|
10
|
+
# @example Adding middleware to Rails application
|
11
|
+
# # config/application.rb
|
12
|
+
# config.middleware.use Securial::Middleware::TransformRequestKeys
|
13
|
+
#
|
14
|
+
# @example Request transformation
|
15
|
+
# # Incoming JSON request body:
|
16
|
+
# { "userName": "john", "emailAddress": "john@example.com" }
|
17
|
+
#
|
18
|
+
# # Transformed for Rails application:
|
19
|
+
# { "user_name": "john", "email_address": "john@example.com" }
|
20
|
+
#
|
21
|
+
require "json"
|
22
|
+
require "stringio"
|
23
|
+
|
1
24
|
module Securial
|
2
25
|
module Middleware
|
3
|
-
#
|
4
|
-
#
|
26
|
+
# Rack middleware that transforms JSON request body keys to snake_case.
|
27
|
+
#
|
28
|
+
# This middleware enables Rails applications to accept camelCase JSON
|
29
|
+
# from frontend applications while automatically converting keys to
|
30
|
+
# Ruby's snake_case convention before they reach the application.
|
5
31
|
#
|
6
|
-
# It reads the request body if the content type is JSON and transforms
|
7
|
-
# the keys to underscore format. If the body is not valid JSON, it does nothing.
|
8
32
|
class TransformRequestKeys
|
33
|
+
# Initializes the middleware with the Rack application.
|
34
|
+
#
|
35
|
+
# @param [#call] app The Rack application to wrap
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# middleware = TransformRequestKeys.new(app)
|
39
|
+
#
|
9
40
|
def initialize(app)
|
10
41
|
@app = app
|
11
42
|
end
|
12
43
|
|
44
|
+
# Processes the request and transforms JSON body keys if applicable.
|
45
|
+
#
|
46
|
+
# Intercepts JSON requests, parses the body, transforms all keys to
|
47
|
+
# snake_case using the KeyTransformer helper, and replaces the request
|
48
|
+
# body with the transformed JSON. Non-JSON requests pass through unchanged.
|
49
|
+
#
|
50
|
+
# @param [Hash] env The Rack environment hash
|
51
|
+
# @return [Array] The Rack response array [status, headers, body]
|
52
|
+
#
|
53
|
+
# @example JSON transformation
|
54
|
+
# # Original request body: { "firstName": "John", "lastName": "Doe" }
|
55
|
+
# # Transformed body: { "first_name": "John", "last_name": "Doe" }
|
56
|
+
#
|
57
|
+
# @example Non-JSON requests
|
58
|
+
# # Form data, XML, and other content types pass through unchanged
|
59
|
+
#
|
60
|
+
# @example Malformed JSON handling
|
61
|
+
# # Invalid JSON is left unchanged and passed to the application
|
62
|
+
# # The application can handle the JSON parsing error appropriately
|
63
|
+
#
|
13
64
|
def call(env)
|
14
|
-
if
|
15
|
-
|
16
|
-
if (req.body&.size || 0) > 0
|
17
|
-
raw = req.body.read
|
18
|
-
req.body.rewind
|
19
|
-
begin
|
20
|
-
parsed = JSON.parse(raw)
|
21
|
-
transformed = Securial::Helpers::KeyTransformer.deep_transform_keys(parsed) do |key|
|
22
|
-
Securial::Helpers::KeyTransformer.underscore(key)
|
23
|
-
end
|
24
|
-
env["rack.input"] = StringIO.new(JSON.dump(transformed))
|
25
|
-
env["rack.input"].rewind
|
26
|
-
rescue JSON::ParserError
|
27
|
-
# no-op
|
28
|
-
end
|
29
|
-
end
|
65
|
+
if json_request?(env)
|
66
|
+
transform_json_body(env)
|
30
67
|
end
|
68
|
+
|
31
69
|
@app.call(env)
|
32
70
|
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Checks if the request contains JSON content.
|
75
|
+
#
|
76
|
+
# @param [Hash] env The Rack environment hash
|
77
|
+
# @return [Boolean] true if the request has JSON content type
|
78
|
+
# @api private
|
79
|
+
#
|
80
|
+
def json_request?(env)
|
81
|
+
env["CONTENT_TYPE"]&.include?("application/json")
|
82
|
+
end
|
83
|
+
|
84
|
+
# Transforms JSON request body keys to snake_case.
|
85
|
+
#
|
86
|
+
# Reads the request body, parses it as JSON, transforms all keys
|
87
|
+
# to snake_case, and replaces the original body with the transformed
|
88
|
+
# JSON. Gracefully handles empty bodies and malformed JSON.
|
89
|
+
#
|
90
|
+
# @param [Hash] env The Rack environment hash
|
91
|
+
# @return [void]
|
92
|
+
# @api private
|
93
|
+
#
|
94
|
+
def transform_json_body(env)
|
95
|
+
req = Rack::Request.new(env)
|
96
|
+
|
97
|
+
return unless request_has_body?(req)
|
98
|
+
|
99
|
+
raw_body = read_request_body(req)
|
100
|
+
|
101
|
+
begin
|
102
|
+
parsed_json = JSON.parse(raw_body)
|
103
|
+
transformed_json = transform_keys_to_snake_case(parsed_json)
|
104
|
+
replace_request_body(env, transformed_json)
|
105
|
+
rescue JSON::ParserError
|
106
|
+
# Malformed JSON - leave unchanged for application to handle
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Checks if the request has a body to process.
|
111
|
+
#
|
112
|
+
# @param [Rack::Request] req The Rack request object
|
113
|
+
# @return [Boolean] true if the request has a non-empty body
|
114
|
+
# @api private
|
115
|
+
#
|
116
|
+
def request_has_body?(req)
|
117
|
+
(req.body&.size || 0) > 0
|
118
|
+
end
|
119
|
+
|
120
|
+
# Reads and rewinds the request body.
|
121
|
+
#
|
122
|
+
# @param [Rack::Request] req The Rack request object
|
123
|
+
# @return [String] The raw request body content
|
124
|
+
# @api private
|
125
|
+
#
|
126
|
+
def read_request_body(req)
|
127
|
+
raw = req.body.read
|
128
|
+
req.body.rewind
|
129
|
+
raw
|
130
|
+
end
|
131
|
+
|
132
|
+
# Transforms all keys in the JSON structure to snake_case.
|
133
|
+
#
|
134
|
+
# @param [Object] json_data The parsed JSON data structure
|
135
|
+
# @return [Object] The data structure with transformed keys
|
136
|
+
# @api private
|
137
|
+
#
|
138
|
+
def transform_keys_to_snake_case(json_data)
|
139
|
+
Securial::Helpers::KeyTransformer.deep_transform_keys(json_data) do |key|
|
140
|
+
Securial::Helpers::KeyTransformer.underscore(key)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Replaces the request body with transformed JSON.
|
145
|
+
#
|
146
|
+
# @param [Hash] env The Rack environment hash
|
147
|
+
# @param [Object] transformed_data The transformed JSON data
|
148
|
+
# @return [void]
|
149
|
+
# @api private
|
150
|
+
#
|
151
|
+
def replace_request_body(env, transformed_data)
|
152
|
+
new_body = JSON.dump(transformed_data)
|
153
|
+
env["rack.input"] = StringIO.new(new_body)
|
154
|
+
env["rack.input"].rewind
|
155
|
+
end
|
33
156
|
end
|
34
157
|
end
|
35
158
|
end
|
@@ -1,15 +1,76 @@
|
|
1
|
+
# @title Securial Transform Response Keys Middleware
|
2
|
+
#
|
3
|
+
# Rack middleware for transforming JSON response keys to client-preferred formats.
|
4
|
+
#
|
5
|
+
# This middleware automatically converts outgoing JSON response keys from Ruby's
|
6
|
+
# snake_case convention to the configured format (camelCase, PascalCase, etc.) to
|
7
|
+
# match client application expectations. It only processes JSON responses and
|
8
|
+
# preserves the original response structure and content.
|
9
|
+
#
|
10
|
+
# @example Adding middleware to Rails application
|
11
|
+
# # config/application.rb
|
12
|
+
# config.middleware.use Securial::Middleware::TransformResponseKeys
|
13
|
+
#
|
14
|
+
# @example Response transformation with lowerCamelCase
|
15
|
+
# # Original Rails response:
|
16
|
+
# { "user_name": "john", "email_address": "john@example.com" }
|
17
|
+
#
|
18
|
+
# # Transformed for client:
|
19
|
+
# { "userName": "john", "emailAddress": "john@example.com" }
|
20
|
+
#
|
21
|
+
# @example Response transformation with UpperCamelCase
|
22
|
+
# # Configuration: config.response_keys_format = :UpperCamelCase
|
23
|
+
# # Original: { "user_profile": { "first_name": "John" } }
|
24
|
+
# # Transformed: { "UserProfile": { "FirstName": "John" } }
|
25
|
+
#
|
26
|
+
require "json"
|
27
|
+
|
1
28
|
module Securial
|
2
29
|
module Middleware
|
3
|
-
#
|
4
|
-
#
|
30
|
+
# Rack middleware that transforms JSON response keys to configured format.
|
31
|
+
#
|
32
|
+
# This middleware enables Rails applications to output responses in
|
33
|
+
# the naming convention expected by client applications (JavaScript,
|
34
|
+
# mobile apps, etc.) while maintaining Ruby's snake_case internally.
|
5
35
|
#
|
6
|
-
# It reads the response body if the content type is JSON and transforms
|
7
|
-
# the keys to the specified format (default is lowerCamelCase).
|
8
36
|
class TransformResponseKeys
|
37
|
+
# Initializes the middleware with the Rack application and optional format.
|
38
|
+
#
|
39
|
+
# The format parameter is deprecated in favor of the global configuration
|
40
|
+
# setting `Securial.configuration.response_keys_format`.
|
41
|
+
#
|
42
|
+
# @param [#call] app The Rack application to wrap
|
43
|
+
# @param [Symbol] format Deprecated: The key format to use (defaults to :lowerCamelCase)
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# middleware = TransformResponseKeys.new(app)
|
47
|
+
#
|
48
|
+
# @deprecated Use `Securial.configuration.response_keys_format` instead of format parameter
|
49
|
+
#
|
9
50
|
def initialize(app, format: :lowerCamelCase)
|
10
51
|
@app = app
|
11
52
|
end
|
12
53
|
|
54
|
+
# Processes the response and transforms JSON keys if applicable.
|
55
|
+
#
|
56
|
+
# Intercepts JSON responses, parses the body, transforms all keys to
|
57
|
+
# the configured format using the KeyTransformer helper, and replaces
|
58
|
+
# the response body with the transformed JSON. Non-JSON responses
|
59
|
+
# pass through unchanged.
|
60
|
+
#
|
61
|
+
# @param [Hash] env The Rack environment hash
|
62
|
+
# @return [Array] The Rack response array [status, headers, body] with transformed keys
|
63
|
+
#
|
64
|
+
# @example JSON transformation
|
65
|
+
# # Original response: { "first_name": "John", "last_name": "Doe" }
|
66
|
+
# # Transformed response: { "firstName": "John", "lastName": "Doe" }
|
67
|
+
#
|
68
|
+
# @example Non-JSON responses
|
69
|
+
# # HTML, XML, and other content types pass through unchanged
|
70
|
+
#
|
71
|
+
# @example Empty or malformed JSON handling
|
72
|
+
# # Empty responses and invalid JSON are left unchanged
|
73
|
+
#
|
13
74
|
def call(env)
|
14
75
|
status, headers, response = @app.call(env)
|
15
76
|
|
@@ -33,10 +94,29 @@ module Securial
|
|
33
94
|
|
34
95
|
private
|
35
96
|
|
97
|
+
# Checks if the response contains JSON content.
|
98
|
+
#
|
99
|
+
# @param [Hash] headers The response headers hash
|
100
|
+
# @return [Boolean] true if the response has JSON content type
|
101
|
+
# @api private
|
102
|
+
#
|
36
103
|
def json_response?(headers)
|
37
104
|
headers["Content-Type"]&.include?("application/json")
|
38
105
|
end
|
39
106
|
|
107
|
+
# Extracts the complete response body from the response object.
|
108
|
+
#
|
109
|
+
# Iterates through the response body parts and concatenates them
|
110
|
+
# into a single string for JSON parsing and transformation.
|
111
|
+
#
|
112
|
+
# @param [#each] response The response body object (responds to #each)
|
113
|
+
# @return [String] The complete response body as a string
|
114
|
+
# @api private
|
115
|
+
#
|
116
|
+
# @example
|
117
|
+
# body = extract_body(response)
|
118
|
+
# # => '{"user_name":"john","email":"john@example.com"}'
|
119
|
+
#
|
40
120
|
def extract_body(response)
|
41
121
|
response_body = ""
|
42
122
|
response.each { |part| response_body << part }
|
data/lib/securial/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: securial
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aly Badawy
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-06-
|
10
|
+
date: 2025-06-27 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rails
|
@@ -206,22 +206,27 @@ homepage: https://github.com/AlyBadawy/Securial/wiki
|
|
206
206
|
licenses:
|
207
207
|
- MIT
|
208
208
|
metadata:
|
209
|
-
release_date: '2025-06-25'
|
210
|
-
allowed_push_host: https://rubygems.org
|
211
209
|
homepage_uri: https://github.com/AlyBadawy/Securial/wiki
|
210
|
+
release_date: '2025-06-27'
|
211
|
+
allowed_push_host: https://rubygems.org
|
212
212
|
source_code_uri: https://github.com/AlyBadawy/Securial
|
213
|
+
documentation_uri: https://alybadawy.github.io/Securial/_index.html
|
213
214
|
changelog_uri: https://github.com/AlyBadawy/Securial/blob/main/CHANGELOG.md
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
-
|
223
|
-
|
224
|
-
\
|
215
|
+
wiki_uri: https://github.com/AlyBadawy/Securial/wiki
|
216
|
+
post_install_message: "\e[36m\n ▗▄▄▖▗▄▄▄▖ ▗▄▄▖▗▖ ▗▖▗▄▄▖ ▗▄▄▄▖ ▗▄▖ ▗▖\n▐▌ ▐▌ ▐▌
|
217
|
+
\ ▐▌ ▐▌▐▌ ▐▌ █ ▐▌ ▐▌▐▌\n ▝▀▚▖▐▛▀▀▘▐▌ ▐▌ ▐▌▐▛▀▚▖ █ ▐▛▀▜▌▐▌\n▗▄▄▞▘▐▙▄▄▖▝▚▄▄▖▝▚▄▞▘▐▌
|
218
|
+
▐▌▗▄█▄▖▐▌ ▐▌▐▙▄▄▖\n\e[0m\n\n\e[32mThank you for installing Securial!\e[0m\n\n\e[37mSecurial
|
219
|
+
is a mountable Rails engine that provides robust, extensible\nauthentication and
|
220
|
+
access control for Rails applications. It supports JWT,\nAPI tokens, session-based
|
221
|
+
auth, and is designed for easy integration with\nmodern web and mobile apps.\e[0m\n\n\e[33mUsage:\e[0m\n
|
222
|
+
\ \e[36msecurial new APP_NAME [rails_options...]\e[0m # Create a new Rails app
|
223
|
+
with Securial pre-installed\n \e[36msecurial -v, --version\e[0m #
|
224
|
+
Show the Securial gem version\n \e[36msecurial -h, --help\e[0m #
|
225
|
+
Show this help message\n\n\e[33mExample:\e[0m\n \e[32msecurial new myapp --api
|
226
|
+
--database=postgresql -T\e[0m\n\n\e[33mMore Info:\e[0m\n \e[37mreview the [changelog]
|
227
|
+
and [WIKI] for more info on the latest\n changes and how to use this gem/engine:\e[0m\n
|
228
|
+
\ \e[34m[Changelog]: https://github.com/AlyBadawy/Securial/blob/main/CHANGELOG.md\e[0m\n
|
229
|
+
\ \e[34m[WIKI]: https://github.com/AlyBadawy/Securial/wiki\e[0m\n\e[90m---\e[0m\n"
|
225
230
|
rdoc_options: []
|
226
231
|
require_paths:
|
227
232
|
- lib
|