ru.Bee 2.7.19 → 3.0.1
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/lib/app/models/user.rb +2 -1
- data/lib/db/create_users.rb +1 -0
- data/lib/db/test.db +0 -0
- data/lib/inits/system.rb +1 -0
- data/lib/rubee/autoload.rb +37 -9
- data/lib/rubee/cli/db.rb +32 -2
- data/lib/rubee/cli/project.rb +1 -0
- data/lib/rubee/cli/server.rb +2 -2
- data/lib/rubee/cli/version.rb +1 -1
- data/lib/rubee/controllers/base_controller.rb +2 -0
- data/lib/rubee/controllers/extensions/auth_tokenable.rb +8 -0
- data/lib/rubee/controllers/extensions/authorizable.rb +43 -0
- data/lib/rubee/internal_middlewares/cookie_samesite_middleware.rb +36 -0
- data/lib/rubee/patches/io_ready.rb +11 -0
- data/lib/rubee/support/time.rb +66 -0
- data/lib/rubee.rb +2 -2
- data/lib/tests/async/thread_async_test.rb +2 -0
- data/lib/tests/configuration_test.rb +7 -0
- data/lib/tests/controllers/auth_tokenable_test.rb +3 -5
- data/lib/tests/controllers/authorizable_test.rb +85 -0
- data/lib/tests/example_models/user.rb +4 -1
- data/lib/tests/models/user_model_test.rb +8 -1
- data/lib/tests/support/time_extension_test.rb +210 -0
- data/readme.md +98 -2
- metadata +23 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b28e888cbb426de4174be8a59d2692b00fc3377992c9f5ec60c88f7192f92242
|
|
4
|
+
data.tar.gz: 35bef19fdb83b5fad826831fa6b70b79b93636673c09730e3eed5c2ec1cc982a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 68d08ae55a2058593c4d465bce2eb3d112bbfdc1f9772e8116e50ba0b0da062dc37cca9dd173fb35f479b7d9a18a39213492abdd83d050702d32463cef1a2e42
|
|
7
|
+
data.tar.gz: 11e6221754f7f854833a4eeebea16e0970a5ff5cddd1cda02b8208e10f4f3f686c7ffd549eaeaa22a357a714b5e465a514f7e2afffb58f7943f9727bb9563e67
|
data/lib/app/models/user.rb
CHANGED
|
@@ -2,5 +2,6 @@
|
|
|
2
2
|
# If you remove or modify it, make sure all changes are inlined
|
|
3
3
|
# with AuthTokenMiddleware and AuthTokenable modules
|
|
4
4
|
class User < Rubee::SequelObject
|
|
5
|
-
attr_accessor :id, :email, :password, :created, :updated
|
|
5
|
+
attr_accessor :id, :email, :role, :password, :created, :updated
|
|
6
|
+
ROLES = { admin: 1, user: 0 }.freeze
|
|
6
7
|
end
|
data/lib/db/create_users.rb
CHANGED
data/lib/db/test.db
CHANGED
|
Binary file
|
data/lib/inits/system.rb
CHANGED
data/lib/rubee/autoload.rb
CHANGED
|
@@ -3,20 +3,37 @@ module Rubee
|
|
|
3
3
|
BLACKLIST = ['rubee.rb', 'test_helper.rb', 'puma.rb']
|
|
4
4
|
class << self
|
|
5
5
|
def call(black_list = [], **options)
|
|
6
|
+
load_envs!
|
|
7
|
+
|
|
6
8
|
load_whitelisted(options[:white_list_dirs]) && return if options[:white_list_dirs]
|
|
7
|
-
#
|
|
9
|
+
# Autoload all rbs
|
|
8
10
|
root_directory = File.join(Rubee::ROOT_PATH, '/lib')
|
|
11
|
+
|
|
12
|
+
load_middlewares(root_directory, black_list)
|
|
13
|
+
|
|
9
14
|
priority_order_require(root_directory, black_list)
|
|
10
15
|
|
|
11
16
|
load_inits(root_directory, black_list)
|
|
12
|
-
#
|
|
17
|
+
# Ensure sequel object is connected
|
|
13
18
|
Rubee::SequelObject.reconnect!
|
|
14
|
-
Dir.glob(File.join(Rubee::APP_ROOT, '**', '*.rb'))
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
Dir.glob(File.join(Rubee::APP_ROOT, '**', '*.rb'))
|
|
20
|
+
.sort_by { |file| file.include?('/models/') ? 0 : 1 }
|
|
21
|
+
.each do |file|
|
|
22
|
+
base_name = File.basename(file)
|
|
23
|
+
unless base_name.end_with?('_test.rb') || (black_list + BLACKLIST).include?(base_name)
|
|
24
|
+
require_relative file
|
|
25
|
+
end
|
|
19
26
|
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def load_middlewares(root_directory, black_list)
|
|
30
|
+
# rubee middlewares
|
|
31
|
+
Dir[File.join(Rubee::ROOT_PATH, '/lib', '/rubee/internal_middlewares/**', '*.rb')].each do |file|
|
|
32
|
+
require_relative file unless black_list.include?("#{file}.rb")
|
|
33
|
+
end
|
|
34
|
+
# project middlewares
|
|
35
|
+
Dir[File.join(root_directory, 'middlewares/**', '*.rb')].each do |file|
|
|
36
|
+
require_relative file unless black_list.include?("#{file}.rb")
|
|
20
37
|
end
|
|
21
38
|
end
|
|
22
39
|
|
|
@@ -45,6 +62,17 @@ module Rubee
|
|
|
45
62
|
end
|
|
46
63
|
end
|
|
47
64
|
|
|
65
|
+
def load_envs!(prefix = ENV['RACK_ENV'], root_directory = File.join(Rubee::APP_ROOT, Rubee::LIB))
|
|
66
|
+
env_file_name = "#{root_directory}/.#{prefix}.env"
|
|
67
|
+
File.foreach(env_file_name) do |line|
|
|
68
|
+
line = line.strip
|
|
69
|
+
next if line.empty? || line.start_with?('#')
|
|
70
|
+
key, value = line.split('=', 2)
|
|
71
|
+
ENV[key] = value
|
|
72
|
+
end
|
|
73
|
+
rescue => _
|
|
74
|
+
end
|
|
75
|
+
|
|
48
76
|
def priority_order_require(root_directory, black_list)
|
|
49
77
|
# rubee pub sub
|
|
50
78
|
Dir[File.join(root_directory, 'rubee/pubsub/**', '*.rb')].each do |file|
|
|
@@ -59,10 +87,10 @@ module Rubee
|
|
|
59
87
|
require_relative file unless black_list.include?("#{file}.rb")
|
|
60
88
|
end
|
|
61
89
|
load_support(root_directory, black_list)
|
|
90
|
+
|
|
62
91
|
# app config and routes
|
|
63
92
|
unless black_list.include?('base_configuration.rb')
|
|
64
|
-
require_relative File.join(Rubee::APP_ROOT, Rubee::LIB,
|
|
65
|
-
'config/base_configuration')
|
|
93
|
+
require_relative File.join(Rubee::APP_ROOT, Rubee::LIB, 'config/base_configuration')
|
|
66
94
|
end
|
|
67
95
|
require_relative File.join(Rubee::APP_ROOT, Rubee::LIB, 'config/routes') unless black_list.include?('routes.rb')
|
|
68
96
|
# rubee extensions
|
data/lib/rubee/cli/db.rb
CHANGED
|
@@ -38,9 +38,39 @@ module Rubee
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def drop_tables(_argv)
|
|
41
|
-
|
|
41
|
+
adapter = Rubee::SequelObject::DB.adapter_scheme.to_s
|
|
42
|
+
tables = Rubee::SequelObject::DB.tables
|
|
43
|
+
|
|
44
|
+
case adapter
|
|
45
|
+
when /postgres/
|
|
46
|
+
tables.each { |table| Rubee::SequelObject::DB.drop_table(table, cascade: true) }
|
|
47
|
+
when /mysql/
|
|
48
|
+
Rubee::SequelObject::DB.run("SET FOREIGN_KEY_CHECKS = 0")
|
|
49
|
+
tables.each { |table| Rubee::SequelObject::DB.drop_table(table) }
|
|
50
|
+
Rubee::SequelObject::DB.run("SET FOREIGN_KEY_CHECKS = 1")
|
|
51
|
+
when /sqlite/
|
|
52
|
+
Rubee::SequelObject::DB.run("PRAGMA foreign_keys = OFF")
|
|
53
|
+
tables.each { |table| Rubee::SequelObject::DB.drop_table(table) }
|
|
54
|
+
Rubee::SequelObject::DB.run("PRAGMA foreign_keys = ON")
|
|
55
|
+
else
|
|
56
|
+
# Fallback — try topological drop order by retrying failures
|
|
57
|
+
remaining = tables.dup
|
|
58
|
+
max_attempts = tables.size
|
|
59
|
+
attempts = 0
|
|
60
|
+
while remaining.any? && attempts < max_attempts
|
|
61
|
+
failed = []
|
|
62
|
+
remaining.each do |table|
|
|
63
|
+
Rubee::SequelObject::DB.drop_table(table)
|
|
64
|
+
rescue StandardError
|
|
65
|
+
failed << table
|
|
66
|
+
end
|
|
67
|
+
remaining = failed
|
|
68
|
+
attempts += 1
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
42
72
|
color_puts("These tables have been dropped for the #{ENV['RACK_ENV']} env:", color: :cyan)
|
|
43
|
-
color_puts(
|
|
73
|
+
color_puts(tables, color: :gray)
|
|
44
74
|
end
|
|
45
75
|
|
|
46
76
|
def truncate_tables(_argv)
|
data/lib/rubee/cli/project.rb
CHANGED
data/lib/rubee/cli/server.rb
CHANGED
|
@@ -46,8 +46,8 @@ LOGO
|
|
|
46
46
|
|
|
47
47
|
print_logo
|
|
48
48
|
color_puts("Starting takeoff of ruBee server on port #{port} in dev mode...", color: :yellow)
|
|
49
|
-
|
|
50
|
-
command = "rerun -- #{jit_prefix(jit)}rackup --port #{port} #{rackup_file}"
|
|
49
|
+
patch_path = File.join(__dir__, '../patches/io_ready.rb')
|
|
50
|
+
command = "ruby -r '#{patch_path}' #{`which rerun`.strip} -- #{jit_prefix(jit)}rackup --port #{port} #{rackup_file}"
|
|
51
51
|
color_puts(command, color: :gray)
|
|
52
52
|
exec(command)
|
|
53
53
|
end
|
data/lib/rubee/cli/version.rb
CHANGED
|
@@ -12,6 +12,14 @@ module Rubee
|
|
|
12
12
|
base.extend(ClassMethods)
|
|
13
13
|
|
|
14
14
|
base.attach('Rubee::AuthTokenMiddleware')
|
|
15
|
+
base.extend(SubclassHook)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module SubclassHook
|
|
19
|
+
def inherited(subclass)
|
|
20
|
+
super
|
|
21
|
+
subclass.attach('Rubee::AuthTokenMiddleware')
|
|
22
|
+
end
|
|
15
23
|
end
|
|
16
24
|
|
|
17
25
|
module InstanceMethods
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Rubee
|
|
2
|
+
module Authorizable
|
|
3
|
+
def self.included(base)
|
|
4
|
+
base.extend ClassMethods
|
|
5
|
+
base.send :include, InstanceMethods
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
module InstanceMethods
|
|
9
|
+
def authorized?(role, model: nil, role_field: :role)
|
|
10
|
+
return false unless authentificated?
|
|
11
|
+
user = authentificated_user(user_model: model)
|
|
12
|
+
return false unless user
|
|
13
|
+
return false unless model.const_get(:ROLES).keys.map(&:to_sym).include?(role.to_sym)
|
|
14
|
+
|
|
15
|
+
model.const_get(:ROLES).key(user.send(role_field)).to_sym == role.to_sym
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module ClassMethods
|
|
20
|
+
def authorize(model: nil, response_hash: nil, role_field: :role, **roles)
|
|
21
|
+
@__authorizations ||= {}
|
|
22
|
+
stringified_model_name = model.to_s
|
|
23
|
+
@__authorizations[stringified_model_name] ||= {}
|
|
24
|
+
@__authorizations[stringified_model_name].merge! roles
|
|
25
|
+
model_klass = Object.const_get stringified_model_name.capitalize
|
|
26
|
+
|
|
27
|
+
@__authorizations[stringified_model_name].each do |role, methods|
|
|
28
|
+
methods = methods.is_a?(Array) ? methods : [methods]
|
|
29
|
+
methods.each do |method|
|
|
30
|
+
around method, ->(controller, &original_action) do
|
|
31
|
+
if controller.send(:authorized?, role, model: model_klass, role_field:)
|
|
32
|
+
original_action.call
|
|
33
|
+
else
|
|
34
|
+
response_object_hash = response_hash || { object: { error: 'Unauthorized' }, type: :json, status: 403 }
|
|
35
|
+
controller.response_with(**response_object_hash)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Rubee
|
|
2
|
+
class CookieSamesiteMiddleware
|
|
3
|
+
def initialize(app)
|
|
4
|
+
@app = app
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def call(env)
|
|
8
|
+
status, headers, body = @app.call(env)
|
|
9
|
+
|
|
10
|
+
cookie_header = headers["Set-Cookie"] || headers["set-cookie"]
|
|
11
|
+
header_key = headers["Set-Cookie"] ? "Set-Cookie" : "set-cookie"
|
|
12
|
+
|
|
13
|
+
if cookie_header
|
|
14
|
+
headers[header_key] = patch_cookies(cookie_header)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
[status, headers, body]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def patch_cookies(cookies)
|
|
23
|
+
is_dev = ENV['RACK_ENV'] == 'development' || ENV['RACK_ENV'].nil?
|
|
24
|
+
|
|
25
|
+
cookies.split("\n").map do |cookie|
|
|
26
|
+
cookie = cookie.gsub(/;\s*secure/i, "") if is_dev
|
|
27
|
+
|
|
28
|
+
unless cookie.downcase.include?("samesite")
|
|
29
|
+
cookie += "; SameSite=Lax"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
cookie
|
|
33
|
+
end.join("\n")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
class Time
|
|
2
|
+
def days_seconds
|
|
3
|
+
hour * 3600 + min * 60 + sec
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def beginning_of_day
|
|
7
|
+
Time.new(year, month, day, 0, 0, 0)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def end_of_day
|
|
11
|
+
Time.new(year, month, day, 23, 59, 59)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def at(hr, mn, sc)
|
|
15
|
+
Time.new(year, month, day, hr, mn, sc)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def all_day
|
|
19
|
+
beginning_of_day..end_of_day
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def add_days(days)
|
|
23
|
+
self + days * 86_400
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def subtract_days(days)
|
|
27
|
+
self - days * 86_400
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def closest_future_working_day
|
|
31
|
+
target_day = self
|
|
32
|
+
target_day = target_day.add_days(1) while target_day.wday == 6 || target_day.wday == 0
|
|
33
|
+
target_day
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def with_current_time
|
|
37
|
+
at(Time.now.hour, Time.now.min, Time.now.sec)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class << self
|
|
41
|
+
def today
|
|
42
|
+
Time.now
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def tomorrow
|
|
46
|
+
today.add_days(1)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def yesterday
|
|
50
|
+
today.subtract_days(1)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def beginning_of_today
|
|
54
|
+
new(Time.now.year, Time.now.month, Time.now.day, 0, 0, 0)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def end_of_today
|
|
58
|
+
new(Time.now.year, Time.now.month, Time.now.day, 23, 59, 59)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def start_of_today
|
|
62
|
+
new(Time.now.year, Time.now.month, Time.now.day, 0, 0, 0)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
data/lib/rubee.rb
CHANGED
|
@@ -20,7 +20,7 @@ module Rubee
|
|
|
20
20
|
RUBEE_SUPPORT = { "Rubee::Support::Hash" => Hash, "Rubee::Support::String" => String }
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
VERSION = '
|
|
23
|
+
VERSION = '3.0.1'
|
|
24
24
|
|
|
25
25
|
require_relative 'rubee/router'
|
|
26
26
|
require_relative 'rubee/logger'
|
|
@@ -66,7 +66,7 @@ module Rubee
|
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def middlewares
|
|
69
|
-
Rubee::Configuration.middlewares
|
|
69
|
+
([Rubee::CookieSamesiteMiddleware] + Rubee::Configuration.middlewares).uniq
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
private
|
|
@@ -20,4 +20,11 @@ describe 'Configuration' do
|
|
|
20
20
|
_("apples".plural?).must_equal(true)
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
|
+
|
|
24
|
+
describe 'when ENV["RACK_ENV"] eq test' do
|
|
25
|
+
it 'laods env var to ENV' do
|
|
26
|
+
_(ENV['RACK_ENV']).must_equal('test')
|
|
27
|
+
_(ENV['SUPER_PASSWORD']).must_equal('test_password')
|
|
28
|
+
end
|
|
29
|
+
end
|
|
23
30
|
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require_relative '../test_helper'
|
|
2
2
|
|
|
3
3
|
class TestController < Rubee::BaseController
|
|
4
|
-
include(Rubee::AuthTokenable)
|
|
5
4
|
auth_methods(:show)
|
|
6
5
|
def show
|
|
7
6
|
response_with(type: :json, object: { ok: :ok })
|
|
@@ -27,7 +26,6 @@ class TestController < Rubee::BaseController
|
|
|
27
26
|
end
|
|
28
27
|
|
|
29
28
|
class TesttwoController < Rubee::BaseController
|
|
30
|
-
include(Rubee::AuthTokenable)
|
|
31
29
|
auth_methods(:show)
|
|
32
30
|
def show
|
|
33
31
|
response_with(type: :json, object: { ok: :ok })
|
|
@@ -71,13 +69,13 @@ class AuthTokenableTest < Minitest::Test
|
|
|
71
69
|
Client.create(name: '9o@example.com', digest_password: '123456')
|
|
72
70
|
end
|
|
73
71
|
|
|
74
|
-
def
|
|
72
|
+
def test_test_controller_auth_tokenable
|
|
75
73
|
get('/test/show')
|
|
76
74
|
|
|
77
75
|
assert_equal(last_response.status, 401)
|
|
78
76
|
end
|
|
79
77
|
|
|
80
|
-
def
|
|
78
|
+
def test_test_controller_tokenable_authenticated
|
|
81
79
|
post('/test/login', { email: '9oU8S@example.com', password: '123456' })
|
|
82
80
|
rack_mock_session.cookie_jar["jwt"] = last_response.cookies["jwt"].value.last
|
|
83
81
|
get('/test/show')
|
|
@@ -91,7 +89,7 @@ class AuthTokenableTest < Minitest::Test
|
|
|
91
89
|
assert_equal(last_response.status, 401)
|
|
92
90
|
end
|
|
93
91
|
|
|
94
|
-
def
|
|
92
|
+
def test_test_controller_authenticated_custom_model
|
|
95
93
|
post('/testtwo/login', { name: '9o@example.com', digest_password: '123456' })
|
|
96
94
|
rack_mock_session.cookie_jar["jwt"] = last_response.cookies["jwt"].value.last
|
|
97
95
|
get('/testtwo/show')
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require_relative '../test_helper'
|
|
2
|
+
|
|
3
|
+
class AuthzController < Rubee::BaseController
|
|
4
|
+
auth_methods(:admin_only, :user_only)
|
|
5
|
+
authorize(admin: [:admin_only], model: :user, role_field: :role)
|
|
6
|
+
|
|
7
|
+
# POST /authz/login
|
|
8
|
+
def login
|
|
9
|
+
if authentificate!
|
|
10
|
+
response_with(type: :json, object: { ok: :ok }, headers: @token_header)
|
|
11
|
+
else
|
|
12
|
+
response_with(type: :json, object: { error: 'user unauthenticated' }, status: :unauthenticated)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# GET /authz/admin_only
|
|
17
|
+
def admin_only
|
|
18
|
+
response_with(type: :json, object: { ok: :admin_access })
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# GET /authz/user_only
|
|
22
|
+
def user_only
|
|
23
|
+
response_with(type: :json, object: { ok: :user_access })
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class AuthorizableTest < Minitest::Test
|
|
28
|
+
include Rack::Test::Methods
|
|
29
|
+
|
|
30
|
+
def app
|
|
31
|
+
Rubee::Application.instance
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def setup
|
|
35
|
+
User.destroy_all
|
|
36
|
+
Rubee::Autoload.call
|
|
37
|
+
Rubee::Router.draw do |route|
|
|
38
|
+
route.post('/authz/login', to: 'authz#login')
|
|
39
|
+
route.get('/authz/admin_only', to: 'authz#admin_only')
|
|
40
|
+
route.get('/authz/user_only', to: 'authz#user_only')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
User.create(email: 'admin@example.com', password: '123456', role: 1)
|
|
44
|
+
User.create(email: 'user@example.com', password: '123456', role: 0)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def login_as(email)
|
|
48
|
+
post('/authz/login', { email:, password: '123456' })
|
|
49
|
+
rack_mock_session.cookie_jar["jwt"] = last_response.cookies["jwt"].value.last
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_admin_only_unauthenticated
|
|
53
|
+
get('/authz/admin_only')
|
|
54
|
+
|
|
55
|
+
assert_equal(403, last_response.status)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_admin_only_as_admin
|
|
59
|
+
login_as('admin@example.com')
|
|
60
|
+
|
|
61
|
+
get('/authz/admin_only')
|
|
62
|
+
assert_equal(200, last_response.status)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def test_admin_only_as_user_forbidden
|
|
66
|
+
login_as('user@example.com')
|
|
67
|
+
|
|
68
|
+
get('/authz/admin_only')
|
|
69
|
+
assert_equal(403, last_response.status)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def test_user_only_as_user
|
|
73
|
+
login_as('user@example.com')
|
|
74
|
+
|
|
75
|
+
get('/authz/user_only')
|
|
76
|
+
assert_equal(200, last_response.status)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def test_user_only_as_admin_forbidden
|
|
80
|
+
login_as('admin@example.com')
|
|
81
|
+
|
|
82
|
+
get('/authz/user_only')
|
|
83
|
+
assert_equal(200, last_response.status)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
class User < Rubee::SequelObject
|
|
2
|
-
attr_accessor :id, :email, :password, :created, :updated
|
|
2
|
+
attr_accessor :id, :email, :password, :role, :created, :updated
|
|
3
3
|
|
|
4
4
|
owns_many :accounts, cascade: true
|
|
5
5
|
owns_one :address, cascade: true
|
|
6
|
+
|
|
7
|
+
# Mandatory hash const required for authorizable
|
|
8
|
+
ROLES = { admin: 1, user: 0 }.freeze
|
|
6
9
|
end
|
|
@@ -169,6 +169,7 @@ describe 'User model' do
|
|
|
169
169
|
|
|
170
170
|
describe 'when there are related recrods' do
|
|
171
171
|
it 'does not delete the record' do
|
|
172
|
+
skip 'Account is not defined' unless respond_to?(:Account)
|
|
172
173
|
user = User.new(email: 'ok-test@test.com', password: '123')
|
|
173
174
|
user.save
|
|
174
175
|
Account.new(user_id: user.id, addres: 'test').save
|
|
@@ -184,6 +185,7 @@ describe 'User model' do
|
|
|
184
185
|
|
|
185
186
|
describe 'when there are related recrods but passed cascade=true' do
|
|
186
187
|
it 'deletes the record' do
|
|
188
|
+
skip 'Account is not defined' unless respond_to?(:Account)
|
|
187
189
|
user = User.new(email: 'ok-test@test.com', password: '123')
|
|
188
190
|
user.save
|
|
189
191
|
Account.new(user_id: user.id, addres: 'test').save
|
|
@@ -209,6 +211,8 @@ describe 'User model' do
|
|
|
209
211
|
|
|
210
212
|
describe 'when there is no record' do
|
|
211
213
|
it 'returns nil' do
|
|
214
|
+
User.destroy_all
|
|
215
|
+
|
|
212
216
|
assert_nil User.find(1)
|
|
213
217
|
end
|
|
214
218
|
end
|
|
@@ -290,6 +294,7 @@ describe 'User model' do
|
|
|
290
294
|
|
|
291
295
|
describe 'when there are records' do
|
|
292
296
|
it 'returns ordered records' do
|
|
297
|
+
User.destroy_all(cascade: true)
|
|
293
298
|
user = User.new(email: 'abc@test.com', password: '123')
|
|
294
299
|
user2 = User.new(email: 'defg@test.com', password: '123')
|
|
295
300
|
user.save
|
|
@@ -306,6 +311,7 @@ describe 'User model' do
|
|
|
306
311
|
|
|
307
312
|
describe 'when there are associated account records' do
|
|
308
313
|
it 'returns all records' do
|
|
314
|
+
skip 'Account is not defined' unless respond_to?(:Account)
|
|
309
315
|
user = User.new(email: 'ok-test@test.com', password: '123')
|
|
310
316
|
user.save
|
|
311
317
|
account = Account.new(user_id: user.id, addres: 'test')
|
|
@@ -321,7 +327,7 @@ describe 'User model' do
|
|
|
321
327
|
end
|
|
322
328
|
describe 'when there is one associated account' do
|
|
323
329
|
it 'cannot add more than one address' do
|
|
324
|
-
skip
|
|
330
|
+
skip 'Address is not defined' unless respond_to?(:Address)
|
|
325
331
|
user = User.new(email: 'bleh@example.com', password: '123')
|
|
326
332
|
user.save
|
|
327
333
|
|
|
@@ -335,6 +341,7 @@ zip: '555555')
|
|
|
335
341
|
end
|
|
336
342
|
|
|
337
343
|
it 'returns the single address' do
|
|
344
|
+
skip 'Address is not defined' unless respond_to?(:Address)
|
|
338
345
|
user = User.new(email: 'bleh@example.com', password: '123')
|
|
339
346
|
user.save
|
|
340
347
|
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
require_relative '../test_helper'
|
|
2
|
+
|
|
3
|
+
class TimeExtensionsTest < Minitest::Test
|
|
4
|
+
def setup
|
|
5
|
+
@time = Time.new(2026, 6, 15, 14, 30, 45) # Sunday
|
|
6
|
+
@weekday = Time.new(2026, 6, 16, 9, 0, 0) # Monday
|
|
7
|
+
@saturday = Time.new(2026, 6, 20, 9, 0, 0) # Saturday
|
|
8
|
+
@sunday = Time.new(2026, 6, 21, 9, 0, 0) # Sunday
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# ---------- days_seconds ----------
|
|
12
|
+
|
|
13
|
+
def test_days_seconds
|
|
14
|
+
t = Time.new(2026, 6, 15, 1, 2, 3)
|
|
15
|
+
assert_equal 1 * 3600 + 2 * 60 + 3, t.days_seconds
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_days_seconds_midnight
|
|
19
|
+
t = Time.new(2026, 6, 15, 0, 0, 0)
|
|
20
|
+
assert_equal 0, t.days_seconds
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_days_seconds_end_of_day
|
|
24
|
+
t = Time.new(2026, 6, 15, 23, 59, 59)
|
|
25
|
+
assert_equal 23 * 3600 + 59 * 60 + 59, t.days_seconds
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# ---------- beginning_of_day ----------
|
|
29
|
+
|
|
30
|
+
def test_beginning_of_day
|
|
31
|
+
result = @time.beginning_of_day
|
|
32
|
+
assert_equal Time.new(2026, 6, 15, 0, 0, 0), result
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_beginning_of_day_preserves_date
|
|
36
|
+
assert_equal @time.year, @time.beginning_of_day.year
|
|
37
|
+
assert_equal @time.month, @time.beginning_of_day.month
|
|
38
|
+
assert_equal @time.day, @time.beginning_of_day.day
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# ---------- end_of_day ----------
|
|
42
|
+
|
|
43
|
+
def test_end_of_day
|
|
44
|
+
result = @time.end_of_day
|
|
45
|
+
assert_equal Time.new(2026, 6, 15, 23, 59, 59), result
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_end_of_day_preserves_date
|
|
49
|
+
assert_equal @time.year, @time.end_of_day.year
|
|
50
|
+
assert_equal @time.month, @time.end_of_day.month
|
|
51
|
+
assert_equal @time.day, @time.end_of_day.day
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# ---------- at ----------
|
|
55
|
+
|
|
56
|
+
def test_at
|
|
57
|
+
result = @time.at(10, 20, 30)
|
|
58
|
+
assert_equal Time.new(2026, 6, 15, 10, 20, 30), result
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_at_preserves_date
|
|
62
|
+
result = @time.at(0, 0, 0)
|
|
63
|
+
assert_equal @time.year, result.year
|
|
64
|
+
assert_equal @time.month, result.month
|
|
65
|
+
assert_equal @time.day, result.day
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# ---------- all_day ----------
|
|
69
|
+
|
|
70
|
+
def test_all_day_returns_range
|
|
71
|
+
assert_kind_of Range, @time.all_day
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_all_day_begin
|
|
75
|
+
assert_equal @time.beginning_of_day, @time.all_day.first
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def test_all_day_end
|
|
79
|
+
assert_equal @time.end_of_day, @time.all_day.last
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def test_all_day_includes_noon
|
|
83
|
+
noon = Time.new(2026, 6, 15, 12, 0, 0)
|
|
84
|
+
assert @time.all_day.include?(noon)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# ---------- add_days ----------
|
|
88
|
+
|
|
89
|
+
def test_add_days
|
|
90
|
+
result = @time.add_days(3)
|
|
91
|
+
assert_equal Time.new(2026, 6, 18, 14, 30, 45), result
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def test_add_days_zero
|
|
95
|
+
assert_equal @time, @time.add_days(0)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def test_add_days_crosses_month
|
|
99
|
+
result = Time.new(2026, 6, 30, 0, 0, 0).add_days(1)
|
|
100
|
+
assert_equal 7, result.month
|
|
101
|
+
assert_equal 1, result.day
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# ---------- subtract_days ----------
|
|
105
|
+
|
|
106
|
+
def test_subtract_days
|
|
107
|
+
result = @time.subtract_days(5)
|
|
108
|
+
assert_equal Time.new(2026, 6, 10, 14, 30, 45), result
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def test_subtract_days_zero
|
|
112
|
+
assert_equal @time, @time.subtract_days(0)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def test_subtract_days_crosses_month
|
|
116
|
+
result = Time.new(2026, 6, 1, 0, 0, 0).subtract_days(1)
|
|
117
|
+
assert_equal 5, result.month
|
|
118
|
+
assert_equal 31, result.day
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def test_add_and_subtract_roundtrip
|
|
122
|
+
assert_equal @time, @time.add_days(7).subtract_days(7)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# ---------- closest_future_working_day ----------
|
|
126
|
+
|
|
127
|
+
def test_closest_future_working_day_from_weekday
|
|
128
|
+
# Monday — already a working day, should not advance
|
|
129
|
+
assert_equal @weekday, @weekday.closest_future_working_day
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def test_closest_future_working_day_from_saturday
|
|
133
|
+
result = @saturday.closest_future_working_day
|
|
134
|
+
assert_equal 1, result.wday # Monday
|
|
135
|
+
assert_equal 22, result.day
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def test_closest_future_working_day_from_sunday
|
|
139
|
+
result = @sunday.closest_future_working_day
|
|
140
|
+
assert_equal 1, result.wday # Monday
|
|
141
|
+
assert_equal 22, result.day
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def test_closest_future_working_day_not_weekend
|
|
145
|
+
result = @saturday.closest_future_working_day
|
|
146
|
+
refute [0, 6].include?(result.wday)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# ---------- with_current_time ----------
|
|
150
|
+
|
|
151
|
+
def test_with_current_time_preserves_date
|
|
152
|
+
result = @time.with_current_time
|
|
153
|
+
assert_equal @time.year, result.year
|
|
154
|
+
assert_equal @time.month, result.month
|
|
155
|
+
assert_equal @time.day, result.day
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def test_with_current_time_uses_current_hours
|
|
159
|
+
result = @time.with_current_time
|
|
160
|
+
now = Time.now
|
|
161
|
+
assert_in_delta now.hour, result.hour, 1
|
|
162
|
+
assert_in_delta now.min, result.min, 1
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# ---------- class methods ----------
|
|
166
|
+
|
|
167
|
+
def test_today_is_time
|
|
168
|
+
assert_kind_of Time, Time.today
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def test_today_is_roughly_now
|
|
172
|
+
assert_in_delta Time.now.to_i, Time.today.to_i, 2
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def test_tomorrow_is_one_day_ahead
|
|
176
|
+
assert_in_delta Time.today.to_i + 86_400, Time.tomorrow.to_i, 2
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def test_yesterday_is_one_day_behind
|
|
180
|
+
assert_in_delta Time.today.to_i - 86_400, Time.yesterday.to_i, 2
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def test_beginning_of_today
|
|
184
|
+
t = Time.beginning_of_today
|
|
185
|
+
assert_equal 0, t.hour
|
|
186
|
+
assert_equal 0, t.min
|
|
187
|
+
assert_equal 0, t.sec
|
|
188
|
+
assert_equal Time.now.day, t.day
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def test_end_of_today
|
|
192
|
+
t = Time.end_of_today
|
|
193
|
+
assert_equal 23, t.hour
|
|
194
|
+
assert_equal 59, t.min
|
|
195
|
+
assert_equal 59, t.sec
|
|
196
|
+
assert_equal Time.now.day, t.day
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def test_start_of_today_equals_beginning_of_today
|
|
200
|
+
assert_equal Time.beginning_of_today, Time.start_of_today
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def test_beginning_and_end_of_today_same_date
|
|
204
|
+
b = Time.beginning_of_today
|
|
205
|
+
e = Time.end_of_today
|
|
206
|
+
assert_equal b.year, e.year
|
|
207
|
+
assert_equal b.month, e.month
|
|
208
|
+
assert_equal b.day, e.day
|
|
209
|
+
end
|
|
210
|
+
end
|
data/readme.md
CHANGED
|
@@ -25,7 +25,7 @@ Starting from ru.Bee 2.0.0, ru.Bee supports WebSocket, which allows you to build
|
|
|
25
25
|
|
|
26
26
|
## Production ready
|
|
27
27
|
|
|
28
|
-
Take a look at the ru.Bee demo site with full documentation: https://rubee.dedyn.io/
|
|
28
|
+
Take a look at the ru.Bee demo site with full documentation: https://rubee.dedyn.io/ (site is currently under maintenance)
|
|
29
29
|
Want to explore how it was built? https://github.com/nucleom42/rubee-site
|
|
30
30
|
|
|
31
31
|
## Stress tested
|
|
@@ -83,12 +83,15 @@ The comparison is based on generic and subjective information available on the i
|
|
|
83
83
|
- [Object hooks](#object-hooks)
|
|
84
84
|
- [Validations](#validations)
|
|
85
85
|
- [JWT based authentication](#jwt-based-authentication)
|
|
86
|
+
- [Authorizable](#authorizable)
|
|
86
87
|
- [OAuth authentication](#oauth-authentication)
|
|
87
88
|
- [ru.Bee commands](#rubee-commands)
|
|
88
89
|
- [Generate commands](#generate-commands)
|
|
89
90
|
- [Migration commands](#migration-commands)
|
|
90
91
|
- [ru.Bee console](#rubee-console)
|
|
91
92
|
- [Rubee::Support](#rubee-support)
|
|
93
|
+
- [Time Extensions](#time-extensions)
|
|
94
|
+
- [Environment Configuration](#environment-configuration)
|
|
92
95
|
- [Testing](#testing)
|
|
93
96
|
- [Background jobs](#background-jobs)
|
|
94
97
|
- [Sidekiq engine](#sidekiq-engine)
|
|
@@ -97,7 +100,7 @@ The comparison is based on generic and subjective information available on the i
|
|
|
97
100
|
- [Logger](#logger)
|
|
98
101
|
- [WebSocket](#websocket)
|
|
99
102
|
- [Bee assistant](#bee-assistant)
|
|
100
|
-
- [Middleware integration](#middleware-integration)
|
|
103
|
+
- [Middleware integration](#middleware-integration))
|
|
101
104
|
|
|
102
105
|
|
|
103
106
|
You can read the full docs on the demo site: [rubee.dedyn.io](https://rubee.dedyn.io/)
|
|
@@ -126,6 +129,12 @@ ORM-agnostic – Models are native ORM objects, but you can use them as blueprin
|
|
|
126
129
|
<br>
|
|
127
130
|
Authenticatable – Easily add JWT authentication to any controller action.
|
|
128
131
|
<br>
|
|
132
|
+
Authorizable – Declarative role-based authorization for controller actions. (New in 3.0.0)
|
|
133
|
+
<br>
|
|
134
|
+
Time Extensions – Comprehensive helper methods for date and time manipulation. (New in 3.0.0)
|
|
135
|
+
<br>
|
|
136
|
+
Environment Configuration – Load environment variables from .env files per environment. (New in 3.0.0)
|
|
137
|
+
<br>
|
|
129
138
|
Hooks – Add logic before, after, or around any controller action.
|
|
130
139
|
<br>
|
|
131
140
|
Testable – Run all or selected tests using fast, beloved Minitest.
|
|
@@ -1088,6 +1097,58 @@ Rubee::Configuration.development?
|
|
|
1088
1097
|
|
|
1089
1098
|
[Back to content](#content)
|
|
1090
1099
|
|
|
1100
|
+
## Time Extensions
|
|
1101
|
+
|
|
1102
|
+
The Time class includes a comprehensive set of helper methods for common date and time manipulations. (New in 3.0.0)
|
|
1103
|
+
|
|
1104
|
+
### Instance Methods
|
|
1105
|
+
|
|
1106
|
+
| Method | Description | Example |
|
|
1107
|
+
| :--- | :--- | :--- |
|
|
1108
|
+
| `days_seconds` | Returns the total number of seconds elapsed since midnight. | `Time.now.days_seconds` |
|
|
1109
|
+
| `beginning_of_day` | Returns a new `Time` object set to the start of the day (00:00:00). | `Time.now.beginning_of_day` |
|
|
1110
|
+
| `end_of_day` | Returns a new `Time` object set to the end of the day (23:59:59). | `Time.now.end_of_day` |
|
|
1111
|
+
| `at(hour, min, sec)` | Returns a new `Time` object for the same date but with the specified time. | `Time.now.at(10, 30, 0)` |
|
|
1112
|
+
| `all_day` | Returns a `Range` from the beginning to the end of the day. | `Time.now.all_day` |
|
|
1113
|
+
| `add_days(n)` | Returns a new `Time` object `n` days in the future. | `Time.now.add_days(5)` |
|
|
1114
|
+
| `subtract_days(n)` | Returns a new `Time` object `n` days in the past. | `Time.now.subtract_days(5)` |
|
|
1115
|
+
| `closest_future_working_day` | Returns the next working day (Monday–Friday). | `Time.now.closest_future_working_day` |
|
|
1116
|
+
| `with_current_time` | Returns a new `Time` object with the same date but the current time of day. | `Time.now.with_current_time` |
|
|
1117
|
+
|
|
1118
|
+
### Class Methods
|
|
1119
|
+
|
|
1120
|
+
| Method | Description |
|
|
1121
|
+
| :--- | :--- |
|
|
1122
|
+
| `Time.today` | Returns the current `Time` object for today. |
|
|
1123
|
+
| `Time.tomorrow` | Returns a `Time` object for tomorrow. |
|
|
1124
|
+
| `Time.yesterday` | Returns a `Time` object for yesterday. |
|
|
1125
|
+
| `Time.beginning_of_today` | Returns a `Time` object for the start of today (00:00:00). |
|
|
1126
|
+
| `Time.end_of_today` | Returns a `Time` object for the end of today (23:59:59). |
|
|
1127
|
+
| `Time.start_of_today` | Alias for `Time.beginning_of_today`. |
|
|
1128
|
+
|
|
1129
|
+
[Back to content](#content)
|
|
1130
|
+
|
|
1131
|
+
## Environment Configuration
|
|
1132
|
+
|
|
1133
|
+
Starting from version 3.0.0, ru.Bee supports loading environment-specific variables from `.env` files, providing a more secure and organized way to manage configuration.
|
|
1134
|
+
|
|
1135
|
+
The system automatically loads files based on the `RACK_ENV` environment variable:
|
|
1136
|
+
- `.development.env` - loaded when `RACK_ENV=development`
|
|
1137
|
+
- `.test.env` - loaded when `RACK_ENV=test`
|
|
1138
|
+
- `.production.env` - loaded when `RACK_ENV=production`
|
|
1139
|
+
|
|
1140
|
+
### Example `.env` file
|
|
1141
|
+
|
|
1142
|
+
```bash
|
|
1143
|
+
# .development.env
|
|
1144
|
+
DATABASE_URL=postgres://localhost:5432/myapp_development
|
|
1145
|
+
SECRET_KEY_BASE=your_secret_key_here
|
|
1146
|
+
API_KEY=abc123
|
|
1147
|
+
```
|
|
1148
|
+
Place your .env files in the root of your project. The variables will be automatically loaded when the application starts and will be available via ENV
|
|
1149
|
+
|
|
1150
|
+
[Back to content](#content)
|
|
1151
|
+
|
|
1091
1152
|
## JWT based authentication
|
|
1092
1153
|
|
|
1093
1154
|
Include the `AuthTokenable` module in your controller and authenticate any action you need.
|
|
@@ -1146,6 +1207,41 @@ end
|
|
|
1146
1207
|
|
|
1147
1208
|
[Back to content](#content)
|
|
1148
1209
|
|
|
1210
|
+
## Authorizable
|
|
1211
|
+
|
|
1212
|
+
Starting from version 3.0.0, ru.Bee includes a declarative authorization system for controller actions. The `Authorizable` module is now part of `Rubee::BaseController` and allows you to restrict access based on user roles.
|
|
1213
|
+
|
|
1214
|
+
### Basic Usage
|
|
1215
|
+
|
|
1216
|
+
```ruby
|
|
1217
|
+
class AdminController < Rubee::BaseController
|
|
1218
|
+
authorize(admin: [:dashboard, :manage_users], model: :user, role_field: :role)
|
|
1219
|
+
|
|
1220
|
+
def dashboard
|
|
1221
|
+
response_with object: { message: "Admin dashboard" }, type: :json
|
|
1222
|
+
end
|
|
1223
|
+
|
|
1224
|
+
def manage_users
|
|
1225
|
+
response_with object: User.all, type: :json
|
|
1226
|
+
end
|
|
1227
|
+
end
|
|
1228
|
+
```
|
|
1229
|
+
Parameters
|
|
1230
|
+
|
|
1231
|
+
role - The role required to access the actions (e.g., :admin, :user)
|
|
1232
|
+
model - The model class to check roles against (default: :user)
|
|
1233
|
+
role_field - The field in the model that stores the role (default: :role)
|
|
1234
|
+
response_hash - Custom response hash for unauthorized access (optional)
|
|
1235
|
+
|
|
1236
|
+
```ruby
|
|
1237
|
+
class AdminController < Rubee::BaseController
|
|
1238
|
+
authorize(admin: [:dashboard],
|
|
1239
|
+
model: :user,
|
|
1240
|
+
response_hash: { object: { error: "Access denied" }, status: 403 })
|
|
1241
|
+
```
|
|
1242
|
+
|
|
1243
|
+
[Back to content](#content)
|
|
1244
|
+
|
|
1149
1245
|
## OAuth authentication
|
|
1150
1246
|
|
|
1151
1247
|
To plug in OAuth 2.0 authentication, add the `oauth2` gem to your Gemfile:
|
metadata
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ru.Bee
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oleg Saltykov
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
-
dependencies:
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: irb
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
12
26
|
description: Application web server written on Ruby
|
|
13
27
|
email:
|
|
14
28
|
- oleg.saltykov@gmail.com
|
|
@@ -238,6 +252,7 @@ files:
|
|
|
238
252
|
- lib/rubee/configuration.rb
|
|
239
253
|
- lib/rubee/controllers/base_controller.rb
|
|
240
254
|
- lib/rubee/controllers/extensions/auth_tokenable.rb
|
|
255
|
+
- lib/rubee/controllers/extensions/authorizable.rb
|
|
241
256
|
- lib/rubee/controllers/extensions/middlewarable.rb
|
|
242
257
|
- lib/rubee/controllers/middlewares/auth_token_middleware.rb
|
|
243
258
|
- lib/rubee/extensions/hookable.rb
|
|
@@ -245,11 +260,13 @@ files:
|
|
|
245
260
|
- lib/rubee/extensions/validatable.rb
|
|
246
261
|
- lib/rubee/features.rb
|
|
247
262
|
- lib/rubee/generator.rb
|
|
263
|
+
- lib/rubee/internal_middlewares/cookie_samesite_middleware.rb
|
|
248
264
|
- lib/rubee/logger.rb
|
|
249
265
|
- lib/rubee/models/assoc_array.rb
|
|
250
266
|
- lib/rubee/models/database_objectable.rb
|
|
251
267
|
- lib/rubee/models/db_tools.rb
|
|
252
268
|
- lib/rubee/models/sequel_object.rb
|
|
269
|
+
- lib/rubee/patches/io_ready.rb
|
|
253
270
|
- lib/rubee/pubsub/container.rb
|
|
254
271
|
- lib/rubee/pubsub/publisher.rb
|
|
255
272
|
- lib/rubee/pubsub/redis.rb
|
|
@@ -258,6 +275,7 @@ files:
|
|
|
258
275
|
- lib/rubee/router.rb
|
|
259
276
|
- lib/rubee/support/hash.rb
|
|
260
277
|
- lib/rubee/support/string.rb
|
|
278
|
+
- lib/rubee/support/time.rb
|
|
261
279
|
- lib/rubee/websocket/websocket.rb
|
|
262
280
|
- lib/rubee/websocket/websocket_connections.rb
|
|
263
281
|
- lib/tests/async/thread_async_test.rb
|
|
@@ -265,6 +283,7 @@ files:
|
|
|
265
283
|
- lib/tests/cli/db_test.rb
|
|
266
284
|
- lib/tests/configuration_test.rb
|
|
267
285
|
- lib/tests/controllers/auth_tokenable_test.rb
|
|
286
|
+
- lib/tests/controllers/authorizable_test.rb
|
|
268
287
|
- lib/tests/controllers/base_controller_test.rb
|
|
269
288
|
- lib/tests/controllers/hookable_test.rb
|
|
270
289
|
- lib/tests/controllers/rubeeapp_test.rb
|
|
@@ -285,6 +304,7 @@ files:
|
|
|
285
304
|
- lib/tests/models/user_model_test.rb
|
|
286
305
|
- lib/tests/rubee_attach_test.rb
|
|
287
306
|
- lib/tests/rubee_generator_test.rb
|
|
307
|
+
- lib/tests/support/time_extension_test.rb
|
|
288
308
|
- lib/tests/test_helper.rb
|
|
289
309
|
- readme.md
|
|
290
310
|
homepage: https://github.com/nucleom42/rubee
|
|
@@ -305,7 +325,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
305
325
|
- !ruby/object:Gem::Version
|
|
306
326
|
version: '0'
|
|
307
327
|
requirements: []
|
|
308
|
-
rubygems_version: 4.0.
|
|
328
|
+
rubygems_version: 4.0.15
|
|
309
329
|
specification_version: 4
|
|
310
330
|
summary: Fast and lightweight Ruby application server designed for minimalism and
|
|
311
331
|
flexibility
|