ronflex 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 109826546b5ad30bac8df1832887023a7c8bda79df3f240b2aac4f9b9af816b5
4
+ data.tar.gz: 2764d13b18441bd16a695848174dc14010f663afcdecc78c1fb7f7fc2422e5f0
5
+ SHA512:
6
+ metadata.gz: 28820603b4ed03da0c23247e42096d069b9571f8623e494a8443ab7515c714c888a08b072931949ad76f45fba3866f6d6527be66fa7614d5b6276cdb690cfd94
7
+ data.tar.gz: 9b65bc8ed111da7adcb65d310ae5184ca3c67d2f02d81365b6035b175449984f6ca18d4ffd082559624fb72e78a47804a9d71df713cab009e497466e3f746b63
data/CHANGELOG.md ADDED
File without changes
data/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # Ronflex Gem
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/ronflex.svg)](http://badge.fury.io/rb/ronflex)
4
+
5
+ `Ronflex` is a Ruby gem that provides a middleware for managing requests and displaying a custom maintenance page during downtime or maintenance. It also offers configuration options to handle user access and authorization rules, making it easy to implement a custom maintenance mode in your application.
6
+
7
+ ## Installation
8
+
9
+ Run:
10
+
11
+ bundle add zarby
12
+
13
+ Or install it yourself as:
14
+
15
+ $ gem install zarby
16
+
17
+ ## Configuration
18
+
19
+ ```ruby
20
+ Ronflex.configure do |config|
21
+ # The list of paths that are always accessible (no restrictions)
22
+ config.excluded_path = ["/health_check", "/status"]
23
+
24
+ # Define a provider for user model (can be a Proc or Lambda)
25
+ config.provider = ->(env) { User.find_by(api_key: env['HTTP_API_KEY']) }
26
+
27
+ # Define rules for which routes users are authorized to access
28
+ config.add_rule :admin do |user, request|
29
+ request.path.start_with?("/admin") && user.admin?
30
+ end
31
+
32
+ # Enable or disable the maintenance mode
33
+ config.enable = true
34
+
35
+ # Optional: Set a custom maintenance page (HTML or ERB template)
36
+ config.maintenance_page = "/path/to/maintenance_page.html" # Or "/path/to/maintenance_page.erb"
37
+ end
38
+ ```
39
+
40
+ ## Enable or disable ronflex
41
+ ```ruby
42
+ # Playing the pokeflute, Ronflex wakes up and no longer blocks the road
43
+ Ronflex.play_pokeflute
44
+
45
+ # Stop the pokeflute, Ronflex falls asleep and blocks the road again
46
+ Ronflex.stop_pokeflute
47
+ ```
48
+
49
+ ## Configuration Options
50
+
51
+ - excluded_path (Array): A list of paths that are always accessible, even when the application is in maintenance mode.
52
+
53
+ Default: ["/health_check"]
54
+
55
+ - provider (Proc/Lambda): A function that returns the user model based on the request environment. This is used to determine which user is making the request.
56
+
57
+ exemple:
58
+ ```ruby
59
+ config.provider = ->(env) { User.find_by(api_key: env['HTTP_API_KEY']) }
60
+ ```
61
+
62
+ - rules (Array): A collection of rules for user access authorization. You can add rules to control access based on the user type and request path.
63
+
64
+ Example:
65
+ ```ruby
66
+ config.add_rule :admin do |user, request|
67
+ user.admin? && request.path.start_with?("/admin")
68
+ end
69
+ ```
70
+
71
+ - enable (Boolean): Enable or disable maintenance mode.
72
+ Default: false
73
+
74
+ - maintenance_page (String): A path to a custom maintenance page (HTML or ERB). If not set, a default maintenance page will be used.
75
+
76
+ Example:
77
+ ```ruby
78
+ config.maintenance_page = "/path/to/maintenance_page.html"
79
+ ```
80
+
81
+ ## Middleware
82
+ Ronflex includes a Rack middleware (Ronflex::Rest) that intercepts incoming requests and checks whether the application should be in maintenance mode.
83
+
84
+ - If the enable flag is set to true and the user is not authorized or the system is under maintenance, the middleware will return a 503 response with the maintenance page.
85
+ - If the request path is in the excluded_path list, it will always be allowed through.
86
+
87
+ Example usage in config/application.rb:
88
+ ```ruby
89
+ # Add Ronflex middleware to the stack
90
+ config.middleware.use Ronflex::Rest
91
+ ```
92
+
93
+ Example of Request Handling with Middleware
94
+ The middleware checks the following conditions before allowing a request to proceed:
95
+
96
+ 1. Always Accessible Paths: If the request path is in excluded_path, it is immediately allowed.
97
+ 2. Maintenance Mode: If enable is true and the user is not authorized, the request will return the maintenance page.
98
+ 3. Authorization: If a user is authenticated and authorized to access the requested route, the request proceeds as normal.
99
+
100
+ ## Custom Maintenance Page
101
+ If you configure a custom maintenance_page path in your Ronflex.configure block, the middleware will load the file and return it as the response when the application is in maintenance mode.
102
+
103
+ - The custom maintenance page can be either a static HTML file or an ERB template. If it's an ERB file, it will be rendered.
104
+ - If the custom page is not found or if no maintenance_page is configured, a default maintenance page will be returned.
105
+
106
+ ```ruby
107
+ Ronflex.configure do |config|
108
+ config.maintenance_page = "/path/to/custom/maintenance_page.html"
109
+ end
110
+ ```
111
+
112
+ ## Customization and Extensibility
113
+
114
+ Adding Custom Authorization Rules
115
+ You can add custom rules to control which users are allowed to access specific parts of your application. For example:
116
+ ```ruby
117
+ Ronflex.configure do |config|
118
+ # Only admin users can access /admin routes
119
+ config.add_rule :admin do |user, request|
120
+ user.admin? && request.path.start_with?("/admin")
121
+ end
122
+ end
123
+ ```
124
+
125
+ ## Handling User Model
126
+ Ronflex uses a provider (a lambda or Proc) to determine which user is making the request based on the request environment. This allows you to integrate with your own user authentication system.
127
+ ```ruby
128
+ Ronflex.configure do |config|
129
+ config.provider = ->(env) { User.find_by(api_key: env['HTTP_API_KEY']) }
130
+ end
131
+ ```
132
+
133
+ ## Example Flow
134
+ 1. Request Access: When a request is made, the middleware checks if the request path is in the list of excluded_path.
135
+ 2. Maintenance Mode: If enable is true and the user is not authorized, the request will return the maintenance page.
136
+ 3. Authorization: If the request is for an authorized route, the request proceeds as normal.
137
+
138
+ ## Example Output of Maintenance Page
139
+ When the application is in maintenance mode, the user will see a page like the following:
140
+ ```html
141
+ <!DOCTYPE html>
142
+ <html>
143
+ <head>
144
+ <title>Maintenance</title>
145
+ </head>
146
+ <body>
147
+ <h1>The site is currently under maintenance</h1>
148
+ <p>Please try again later</p>
149
+ </body>
150
+ </html>
151
+ ```
152
+
153
+ ## Error Handling
154
+ In case of issues loading the custom maintenance page (e.g., if the file does not exist), Ronflex will fallback to a default maintenance page.
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ task default: %i[]
data/gem_info.txt ADDED
@@ -0,0 +1,5 @@
1
+ ## TEST ##
2
+
3
+ ## PUSH GEM ##
4
+
5
+ gem build ronflex.gemspec
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ronflex/rule"
4
+ require "ronflex/errors"
5
+
6
+ # Module Ronflex
7
+ #
8
+ # This module provides configuration to manage access rules based on templates and queries,
9
+ # with options to exclude certain paths or customize logic via a provider.
10
+ #
11
+ # @example Basic setup
12
+ # Ronflex.configure do |config|
13
+ # # add path to exclude all rule
14
+ # config.excluded_path << "/public"
15
+
16
+ # # add a provider to identify the model user
17
+ # config.provider = ->(env) { env[:current_user] }
18
+
19
+ # # add rule for administrator
20
+ # config.add_rule(:admin) do |user, request|
21
+ # # admins can access to all routes, expected "/restricted"
22
+ # !request.path.start_with?("/restricted")
23
+ # end
24
+
25
+ # # add rule for guest
26
+ # config.add_rule(:guest) do |user, request|
27
+ # # guests can only acces public path
28
+ # request.path.start_with?("/public")
29
+ # end
30
+ #
31
+ # # add custom page maintenance
32
+ # config.maintenance_page = "/path/to/your/custom/maintenance/page.html"
33
+ # end
34
+ module Ronflex
35
+ class Configuration
36
+ # List of paths excluded by default.
37
+ # These paths are ignored by the defined rules.
38
+ #
39
+ # @return [Array<String>]
40
+ DEFAULT_EXCLUDED_PATHS = ["/health_check"]
41
+ # Default provider.
42
+ # by default, it is a lambda which returns `nil`.
43
+ #
44
+ # @return [Proc]
45
+ DEFAULT_PROVIDER = -> (env) { nil }
46
+
47
+ # Attribute for desable or enable th feature
48
+ #
49
+ # @return [Boolean]
50
+ attr_accessor :enable
51
+
52
+ # List of paths excluded from rule evaluation.
53
+ #
54
+ # @return [Array<String>]
55
+ attr_accessor :excluded_path
56
+
57
+ # Custom provider to retrieve a model based on the environment.
58
+ #
59
+ # @return [Proc]
60
+ attr_accessor :provider
61
+
62
+ # List of defined access rules.
63
+ #
64
+ # @return [Array<Rule>]
65
+ attr_accessor :rules
66
+
67
+ # Path to a custom maintenance page (either an HTML or ERB file).
68
+ #
69
+ # @return [String]
70
+ attr_accessor :maintenance_page
71
+
72
+ # Initializes a new configuration with default values.
73
+ def initialize
74
+ @excluded_path = DEFAULT_EXCLUDED_PATHS.dup
75
+ @provider = DEFAULT_PROVIDER
76
+ @enable = false
77
+ @maintenance_page = nil
78
+ @rules = []
79
+ end
80
+
81
+ # Adds an access rule for a specific type.
82
+ #
83
+ # A rule is a block of code that determines whether a model is allowed to access a given query.
84
+ #
85
+ # @param type [Symbol, String] The type or role of the model (e.g. `:admin`, `:user`).
86
+ # @yield [model, request] The rule block, which takes a model and a request as parameters.
87
+ # @yieldparam model [Object] The evaluated model.
88
+ # @yieldparam request [Object] The evaluated request.
89
+ # @return [void]
90
+ #
91
+ # @example Add a rule for administrators
92
+ # config.add_rule(:admin) do |model, request|
93
+ # request.path.start_with?("/admin")
94
+ # end
95
+ def add_rule(type, &block)
96
+ raise RonflexArgumentError, "Rule type must be provided" if type.nil?
97
+ raise RonflexArgumentError, "Block must be provided for the rule" unless block_given?
98
+ @rules << Rule.new(type, &block)
99
+ end
100
+
101
+ # Checks if a model is allowed to access a given query.
102
+ #
103
+ # This method iterates through the defined rules and applies those matching the pattern.
104
+ #
105
+ # @param model [Object] The model to check (e.g. a user or a symbolic role).
106
+ # @param request [Object] The request to check, which should respond to methods like `path`.
107
+ # @return [Boolean] `true` if at least one rule authorizes access, `false` otherwise.
108
+ #
109
+ # @example Check an authorization
110
+ # model = :admin
111
+ # request = OpenStruct.new(path: "/admin/dashboard")
112
+ # config.allowed?(model, request) # => true or false
113
+ def allowed?(model, request)
114
+ rules.any? { |rule| rule.matches?(model, request) }
115
+ end
116
+
117
+
118
+ # Checks if a model is valid (present).
119
+ #
120
+ # @param model [Object] The model to check.
121
+ # @return [Boolean] `true` if the model is valid, `false` otherwise.
122
+ #
123
+ # @note This method uses the `present?` method, which is typically available in Rails.
124
+ # If you are not using Rails, you may need to override this method.
125
+ def model_present?(model)
126
+ !model.nil? && !(model.respond_to?(:empty?) && model.empty?)
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ronflex
4
+ class Error < StandardError; end
5
+ class RonflexArgumentError < ArgumentError end
6
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ronflex/rest"
4
+
5
+ module Ronflex
6
+ class Railtie < Rails::Railtie
7
+ initializer "ronflex.configure_rails_initialization" do |app|
8
+ app.middleware.insert_before 0, Ronflex::Rest
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,123 @@
1
+ module Ronflex
2
+ class Rest
3
+ # Initializes the middleware with the given app
4
+ #
5
+ # @param app [Object] The application instance that this middleware wraps
6
+ # It will call the next middleware or application in the stack.
7
+ def initialize(app)
8
+ @app = app
9
+ end
10
+
11
+ # Rack middleware call method that processes the incoming request.
12
+ #
13
+ # It checks if the request should be granted access, and if not, it returns
14
+ # the maintenance page or proceeds with the normal request handling.
15
+ #
16
+ # @param env [Hash] Rack environment hash which contains information about
17
+ # the incoming request, such as headers, parameters, etc.
18
+ #
19
+ # @return [Array] Rack response in the form of [status, headers, body]
20
+ # If access is allowed, the request is passed to the next middleware/application.
21
+ # Otherwise, a 503 status with the maintenance page is returned.
22
+ def call(env)
23
+ request = Rack::Request.new(env)
24
+ model = Ronflex.configuration.provider.call(env)
25
+
26
+ # If the request is always accessible (excluded path), pass the request to the next app
27
+ return @app.call(env) if always_access?(request)
28
+
29
+ # If the system is enabled and the model is present and authorized, proceed with the request
30
+ if Ronflex.configuration.enable
31
+ if model_present?(model) && routes_authorized?(model, request)
32
+ return @app.call(env)
33
+ else
34
+ # If conditions are not met, return maintenance page
35
+ return [503, { "Content-Type" => "text/html" }, [maintenance_page]]
36
+ end
37
+ end
38
+
39
+ # Default pass-through for the request
40
+ @app.call(env)
41
+ end
42
+
43
+ private
44
+
45
+ # Checks if the request path is always accessible, i.e., excluded from any restrictions.
46
+ #
47
+ # @param request [Rack::Request] The current HTTP request object
48
+ #
49
+ # @return [Boolean] Returns `true` if the request path is in the list of excluded paths.
50
+ def always_access?(request)
51
+ Ronflex.configuration.excluded_path.include?(request.path)
52
+ end
53
+
54
+ # Checks if the model (user or entity) is present and valid.
55
+ #
56
+ # @param model [Object] The model (typically a user) retrieved from the provider.
57
+ #
58
+ # @return [Boolean] Returns `true` if the model is present and valid as per the configuration.
59
+ def model_present?(model)
60
+ Ronflex.configuration.model_present?(model)
61
+ end
62
+
63
+ # Checks if the current route is authorized for the given model.
64
+ #
65
+ # @param model [Object] The model (user or entity) for which access needs to be authorized.
66
+ # @param request [Rack::Request] The current HTTP request object containing the route.
67
+ #
68
+ # @return [Boolean] Returns `true` if the model is allowed to access the requested route.
69
+ def routes_authorized?(model, request)
70
+ Ronflex.configuration.allowed?(model, request)
71
+ end
72
+
73
+ # Returns the content of the maintenance page, either custom or default.
74
+ #
75
+ # @return [String] The HTML content to be displayed on the maintenance page.
76
+ def maintenance_page
77
+ if Ronflex.configuration.maintenance_page
78
+ load_custom_maintenance_page(Ronflex.configuration.maintenance_page)
79
+ else
80
+ default_maintenance_page
81
+ end
82
+ end
83
+
84
+ # Loads a custom maintenance page if a path is provided.
85
+ #
86
+ # If the path is an ERB file, it renders the template. Otherwise, it reads and returns the HTML file.
87
+ #
88
+ # @param path [String] The file path to the custom maintenance page (could be HTML or ERB).
89
+ #
90
+ # @return [String] The rendered HTML content of the custom maintenance page.
91
+ def load_custom_maintenance_page(path)
92
+ if path.end_with?(".erb")
93
+ template = ERB.new(File.read(path))
94
+ template.result
95
+ else
96
+ File.read(path)
97
+ end
98
+ rescue Errno::ENOENT
99
+ default_maintenance_page
100
+ end
101
+
102
+ # Provides the default maintenance page content.
103
+ #
104
+ # This is a basic HTML page shown when no custom page is configured or when
105
+ # there is an issue loading the custom maintenance page.
106
+ #
107
+ # @return [String] The default HTML content for the maintenance page.
108
+ def default_maintenance_page
109
+ <<~HTML
110
+ <!DOCTYPE html>
111
+ <html>
112
+ <head>
113
+ <title>Maintenance</title>
114
+ </head>
115
+ <body>
116
+ <h1>The site is currently under maintenance</h1>
117
+ <p>Please try again later</p>
118
+ </body>
119
+ </html>
120
+ HTML
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Module Ronflex
4
+ #
5
+ # This module encapsulates functionality related to rule-based access control.
6
+ # The `Rule` class within this module represents a single rule that determines
7
+ # whether a specific model (e.g., a user or role) is allowed access to a given request.
8
+ module Ronflex
9
+ # Class Rule
10
+ #
11
+ # Represents a rule that associates a specific type (e.g., `:admin`, `:guest`) with
12
+ # a custom condition defined by a block of logic. The rule can then evaluate whether
13
+ # a model matches the type and satisfies the condition for a particular request.
14
+ class Rule
15
+ # @return [Symbol, String] the type of the model this rule applies to (e.g., `:admin` or `:guest`).
16
+ attr_reader :type
17
+
18
+ # @return [Proc] the block of logic that determines if the rule matches a given model and request.
19
+ attr_reader :rule
20
+
21
+ # Initializes a new rule.
22
+ #
23
+ # @param type [Symbol, String] The type of model this rule applies to.
24
+ # Typically a symbol representing a role (e.g., `:admin` or `:guest`).
25
+ # @yield [model, request] The block defining the rule's logic. It is executed to determine
26
+ # if the rule matches for a given model and request.
27
+ # @yieldparam model [Object] The model being evaluated (e.g., a user or role).
28
+ # @yieldparam request [Object] The request being evaluated (e.g., an HTTP request object).
29
+ def initialize(type, &block)
30
+ @type = type
31
+ @rule = block
32
+ end
33
+
34
+ # Checks if the rule matches a given model and request.
35
+ #
36
+ # This method evaluates whether the provided model matches the rule's type
37
+ # and if the rule's block returns `true` for the given model and request.
38
+ #
39
+ # @param model [Object] The model to check (e.g., a user or role).
40
+ # @param request [Object] The request to check (e.g., an HTTP request object).
41
+ # @return [Boolean] `true` if the model matches the rule's type and satisfies the block's logic, `false` otherwise.
42
+ def matches?(model, request)
43
+ model == type && rule.call(model, request)
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ronflex
4
+ VERSION = "0.1.0"
5
+ end
data/lib/ronflex.rb ADDED
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ronflex/version"
4
+ require "ronflex/configuration"
5
+
6
+ module Ronflex
7
+ class << self
8
+ # Method to configure `Snorlax` with a block
9
+ #
10
+ # @yield [config] The block receives an instance of `Snorlax::Configuration`
11
+ # @yieldparam config [Snorlax::Configuration] The configuration object to modify.
12
+ def configure
13
+ @configuration ||= Configuration.new
14
+ yield @configuration if block_given?
15
+ end
16
+
17
+ # Accesses global configuration
18
+ #
19
+ # @return [Snorlax::Configuration] The global instance of the configuration
20
+ def configuration
21
+ @configuration ||= Configuration.new
22
+ end
23
+
24
+ # Simulates playing the Pokéflute, disabling a feature.
25
+ #
26
+ # This method modifies the global configuration to enable a specific feature.
27
+ #
28
+ # @return [void]
29
+ def play_pokeflute
30
+ configuration.enable = false
31
+ end
32
+
33
+ # Simulates stopping the Pokéflute, enabling a feature.
34
+ #
35
+ # This method modifies the global configuration to disable a specific feature.
36
+ #
37
+ # @return [void]
38
+ def stop_pokeflute
39
+ configuration.enable = true
40
+ end
41
+ end
42
+ end
data/sig/ronflex.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Ronflex
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ronflex
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - vianney.sonneville
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-11-29 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Ronflex is a Ruby gem that provides a middleware for managing requests
14
+ and displaying a custom maintenance page during downtime or maintenance. It also
15
+ offers configuration options to handle user access and authorization rules, making
16
+ it easy to implement a custom maintenance mode in your application.
17
+ email:
18
+ - vianneysonneville4@gmail.com
19
+ executables: []
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - CHANGELOG.md
24
+ - README.md
25
+ - Rakefile
26
+ - gem_info.txt
27
+ - lib/ronflex.rb
28
+ - lib/ronflex/configuration.rb
29
+ - lib/ronflex/errors.rb
30
+ - lib/ronflex/railties.rb
31
+ - lib/ronflex/rest.rb
32
+ - lib/ronflex/rule.rb
33
+ - lib/ronflex/version.rb
34
+ - sig/ronflex.rbs
35
+ homepage: https://rubygems.org/gems/ronflex.
36
+ licenses:
37
+ - MIT
38
+ metadata:
39
+ allowed_push_host: https://rubygems.org
40
+ source_code_uri: https://github.com/VianneySonneville/ronflex
41
+ changelog_uri: https://github.com/VianneySonneville/ronflex/blob/main/CHANGELOG.md
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 3.0.0
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubygems_version: 3.5.23
58
+ signing_key:
59
+ specification_version: 4
60
+ summary: Gem that provides a middleware for managing requests and displaying a custom
61
+ maintenance page during downtime or maintenance.
62
+ test_files: []