multi-tenant-support 1.1.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +115 -14
- data/lib/generators/multi_tenant_support/templates/initializer.rb.tt +4 -0
- data/lib/multi-tenant-support.rb +44 -8
- data/lib/multi_tenant_support/active_job.rb +16 -0
- data/lib/multi_tenant_support/concern/controller_concern.rb +7 -7
- data/lib/multi_tenant_support/concern/model_concern.rb +43 -31
- data/lib/multi_tenant_support/config/console.rb +21 -0
- data/lib/multi_tenant_support/current.rb +16 -1
- data/lib/multi_tenant_support/minitest.rb +7 -0
- data/lib/multi_tenant_support/railtie.rb +8 -0
- data/lib/multi_tenant_support/rspec.rb +13 -0
- data/lib/multi_tenant_support/test/capybara.rb +57 -0
- data/lib/multi_tenant_support/test/integration.rb +29 -0
- data/lib/multi_tenant_support/test/system.rb +38 -0
- data/lib/multi_tenant_support/version.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2780344bf11a6b5bb065ebed1848d5630ba8b414ab1d5ed9d08a019cf7751265
|
4
|
+
data.tar.gz: feb76616d756d524e8c871afdf331d6a754e522c2951e6c3cf15d6cdf2463093
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e70944d15ffe56a7503f3de425b7c99c7a3bd9bbf17d9caa97912b5bddd692636acb4960ab670aba64c8996d6f2a74ced4cda071ee0e07728928a39dc42c953
|
7
|
+
data.tar.gz: 3543fc05811590fa14559213a02fd1a71569bc98ba562f93e6530483ba1c09e8a28fcb06f5ce0c80c07eee398ccc2bfa4bcf00b9321ca6a51315247bef7ea09e
|
data/README.md
CHANGED
@@ -237,32 +237,29 @@ MultiTenantSupport.under_tenant amazon do
|
|
237
237
|
end
|
238
238
|
```
|
239
239
|
|
240
|
-
###
|
241
|
-
|
242
|
-
This gem disallow read across tenant by default. You can check current state through:
|
240
|
+
### Set current tenant global
|
243
241
|
|
244
242
|
```ruby
|
245
|
-
MultiTenantSupport.
|
243
|
+
MultiTenantSupport.set_tenant_account(account)
|
246
244
|
```
|
247
245
|
|
248
|
-
###
|
249
|
-
|
250
|
-
You can turn on the permission to read records across tenant through:
|
246
|
+
### Temp set current tenant to nil
|
251
247
|
|
252
248
|
```ruby
|
253
|
-
MultiTenantSupport.
|
254
|
-
|
255
|
-
# Or
|
256
|
-
MultiTenantSupport.allow_read_across_tenant do
|
249
|
+
MultiTenantSupport.without_current_tenant do
|
257
250
|
# ...
|
258
251
|
end
|
259
252
|
```
|
260
253
|
|
261
|
-
|
254
|
+
### 3 protection states
|
262
255
|
|
263
|
-
|
256
|
+
1. `MultiTenantSupport.full_protected?`
|
257
|
+
2. `MultiTenantSupport.allow_read_across_tenant?`
|
258
|
+
3. `MultiTenantSupport.unprotected?`
|
264
259
|
|
265
|
-
|
260
|
+
#### Full protection(default)
|
261
|
+
|
262
|
+
The default state is full protection. This gem disallow modify record across tenant by default.
|
266
263
|
|
267
264
|
If `MultiTenantSupport.current_tenant` exist, you can only modify those records under this tenant, otherwise, you will get some errors like:
|
268
265
|
|
@@ -274,6 +271,45 @@ If `MultiTenantSupport.current_tenant` exist, you can only modify those records
|
|
274
271
|
|
275
272
|
If `MultiTenantSupport.current_tenant` is missing, you cannot modify or create any tenanted records.
|
276
273
|
|
274
|
+
If you switched to other state, you can switch back through:
|
275
|
+
|
276
|
+
```ruby
|
277
|
+
MultiTenantSupport.turn_on_full_protection
|
278
|
+
|
279
|
+
# Or
|
280
|
+
MultiTenantSupport.turn_on_full_protection do
|
281
|
+
# ...
|
282
|
+
end
|
283
|
+
```
|
284
|
+
|
285
|
+
#### Allow read across tenant for super admin
|
286
|
+
|
287
|
+
You can turn on the permission to read records across tenant through:
|
288
|
+
|
289
|
+
```ruby
|
290
|
+
MultiTenantSupport.allow_read_across_tenant
|
291
|
+
|
292
|
+
# Or
|
293
|
+
MultiTenantSupport.allow_read_across_tenant do
|
294
|
+
# ...
|
295
|
+
end
|
296
|
+
```
|
297
|
+
|
298
|
+
You can put it in a before action in SuperAdmin's controllers
|
299
|
+
|
300
|
+
#### Turn off protection
|
301
|
+
|
302
|
+
Sometimes, as a super admin, we need to execute certain maintenatn operations over all tenant records. You can do this through:
|
303
|
+
|
304
|
+
```ruby
|
305
|
+
MultiTenantSupport.turn_off_protection
|
306
|
+
|
307
|
+
# Or
|
308
|
+
MultiTenantSupport.turn_off_protection do
|
309
|
+
# ...
|
310
|
+
end
|
311
|
+
```
|
312
|
+
|
277
313
|
### Set current tenant acccount in controller by default
|
278
314
|
|
279
315
|
This gem has set a before action `set_current_tenant_account` on ActionController. It search tenant by subdomain or domain. Do remember to `skip_before_action :set_current_tenant_account` in super admin controllers.
|
@@ -306,6 +342,67 @@ Currently, we don't have a good way to protect this method. So please use `upser
|
|
306
342
|
|
307
343
|
This gem has override `unscoped` to prevent the default tenant scope be scoped out. But if you really want to scope out the default tenant scope, you can use `unscope_tenant`.
|
308
344
|
|
345
|
+
### Console
|
346
|
+
|
347
|
+
Console does not allow read across tenant by default. But you have several ways to change that:
|
348
|
+
|
349
|
+
1. Set `allow_read_across_tenant_by_default` in the initialize file
|
350
|
+
|
351
|
+
```ruby
|
352
|
+
console do |config|
|
353
|
+
config.allow_read_across_tenant_by_default = true
|
354
|
+
end
|
355
|
+
```
|
356
|
+
2. Set the environment variable `ALLOW_READ_ACROSS_TENANT` when call consoel command
|
357
|
+
|
358
|
+
```bash
|
359
|
+
ALLOW_READ_ACROSS_TENANT=1 rails console
|
360
|
+
```
|
361
|
+
3. Manual change it in console
|
362
|
+
|
363
|
+
```ruby
|
364
|
+
$ rails c
|
365
|
+
$ irb(main):001:0> MultiTenantSupport.allow_read_across_tenant
|
366
|
+
```
|
367
|
+
|
368
|
+
## Testing
|
369
|
+
### Minitest (Rails default)
|
370
|
+
|
371
|
+
```ruby
|
372
|
+
# test/test_helper.rb
|
373
|
+
require 'multi_tenant_support/minitet'
|
374
|
+
```
|
375
|
+
### RSpec (with Capybara)
|
376
|
+
|
377
|
+
```ruby
|
378
|
+
# spec/rails_helper.rb or spec/spec_helper.rb
|
379
|
+
require 'multi_tenant_support/rspec'
|
380
|
+
```
|
381
|
+
|
382
|
+
Above code will make sure the `MultiTenantSupport.current_tenant` won't accidentally be reset during integration and system tests. For example:
|
383
|
+
|
384
|
+
With above testing requre code
|
385
|
+
|
386
|
+
```ruby
|
387
|
+
# Integration test
|
388
|
+
test "a integration test" do
|
389
|
+
host! "apple.example.com"
|
390
|
+
|
391
|
+
assert_no_changes "MultiTenantSupport.current_tenant" do
|
392
|
+
get users_path
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
# System test
|
397
|
+
test "a system test" do
|
398
|
+
Capybara.app_host = "http://apple.example.com"
|
399
|
+
|
400
|
+
assert_no_changes "MultiTenantSupport.current_tenant" do
|
401
|
+
visit users_path
|
402
|
+
end
|
403
|
+
end
|
404
|
+
```
|
405
|
+
|
309
406
|
## Code Example
|
310
407
|
|
311
408
|
### Database Schema
|
@@ -340,6 +437,10 @@ MultiTenantSupport.configure do
|
|
340
437
|
config.excluded_subdomains = ['www']
|
341
438
|
config.host = 'example.com'
|
342
439
|
end
|
440
|
+
|
441
|
+
console do |config|
|
442
|
+
config.allow_read_across_tenant_by_default = false
|
443
|
+
end
|
343
444
|
end
|
344
445
|
```
|
345
446
|
|
@@ -12,6 +12,10 @@ MultiTenantSupport.configure do
|
|
12
12
|
config.excluded_subdomains = ['www']
|
13
13
|
config.host = 'REPLACE.ME'
|
14
14
|
end
|
15
|
+
|
16
|
+
console do |config|
|
17
|
+
config.allow_read_across_tenant_by_default = false
|
18
|
+
end
|
15
19
|
end
|
16
20
|
|
17
21
|
# Uncomment if you are using sidekiq without ActiveJob
|
data/lib/multi-tenant-support.rb
CHANGED
@@ -4,6 +4,7 @@ require "multi_tenant_support/errors"
|
|
4
4
|
require "multi_tenant_support/config/app"
|
5
5
|
require "multi_tenant_support/config/controller"
|
6
6
|
require "multi_tenant_support/config/model"
|
7
|
+
require "multi_tenant_support/config/console"
|
7
8
|
require "multi_tenant_support/current"
|
8
9
|
require "multi_tenant_support/find_tenant_account"
|
9
10
|
require "multi_tenant_support/concern/controller_concern"
|
@@ -25,35 +26,70 @@ module MultiTenantSupport
|
|
25
26
|
Current.tenant_account&.send(model.tenant_account_primary_key)
|
26
27
|
end
|
27
28
|
|
29
|
+
def set_current_tenant(tenant)
|
30
|
+
Current.tenant_account = tenant
|
31
|
+
Current.protection_state = PROTECTED
|
32
|
+
end
|
33
|
+
|
28
34
|
def under_tenant(tenant_account, &block)
|
29
35
|
raise ArgumentError, "block is missing" if block.nil?
|
30
36
|
|
31
|
-
Current.set(tenant_account: tenant_account) do
|
37
|
+
Current.set(tenant_account: tenant_account, protection_state: PROTECTED) do
|
32
38
|
yield
|
33
39
|
end
|
34
40
|
end
|
35
41
|
|
36
|
-
def
|
37
|
-
|
42
|
+
def without_current_tenant(&block)
|
43
|
+
raise ArgumentError, "block is missing" if block.nil?
|
44
|
+
|
45
|
+
Current.set(tenant_account: nil) do
|
46
|
+
yield
|
47
|
+
end
|
38
48
|
end
|
39
49
|
|
40
|
-
def
|
50
|
+
def full_protected?
|
51
|
+
current_tenant || Current.protection_state == PROTECTED
|
52
|
+
end
|
53
|
+
|
54
|
+
def allow_read_across_tenant?
|
55
|
+
current_tenant.nil? && [PROTECTED_EXCEPT_READ, UNPROTECTED].include?(Current.protection_state)
|
56
|
+
end
|
57
|
+
|
58
|
+
def unprotected?
|
59
|
+
current_tenant.nil? && Current.protection_state == UNPROTECTED
|
60
|
+
end
|
61
|
+
|
62
|
+
def turn_off_protection
|
63
|
+
raise 'Cannot turn off protection, try wrap in without_current_tenant' if current_tenant
|
64
|
+
|
41
65
|
if block_given?
|
42
|
-
Current.set(
|
66
|
+
Current.set(protection_state: UNPROTECTED) do
|
43
67
|
yield
|
44
68
|
end
|
45
69
|
else
|
46
|
-
Current.
|
70
|
+
Current.protection_state = UNPROTECTED
|
47
71
|
end
|
48
72
|
end
|
49
73
|
|
50
74
|
def allow_read_across_tenant
|
75
|
+
raise 'Cannot read across tenant, try wrap in without_current_tenant' if current_tenant
|
76
|
+
|
77
|
+
if block_given?
|
78
|
+
Current.set(protection_state: PROTECTED_EXCEPT_READ) do
|
79
|
+
yield
|
80
|
+
end
|
81
|
+
else
|
82
|
+
Current.protection_state = PROTECTED_EXCEPT_READ
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def turn_on_full_protection
|
51
87
|
if block_given?
|
52
|
-
Current.set(
|
88
|
+
Current.set(protection_state: PROTECTED) do
|
53
89
|
yield
|
54
90
|
end
|
55
91
|
else
|
56
|
-
Current.
|
92
|
+
Current.protection_state = PROTECTED
|
57
93
|
end
|
58
94
|
end
|
59
95
|
|
@@ -8,6 +8,7 @@ module MultiTenantSupport
|
|
8
8
|
end
|
9
9
|
|
10
10
|
class_methods do
|
11
|
+
|
11
12
|
if Gem::Version.new(Rails.version) < Gem::Version.new("7.0.0.alpha1")
|
12
13
|
def perform_now(*args)
|
13
14
|
job = job_or_instantiate(*args)
|
@@ -23,6 +24,21 @@ module MultiTenantSupport
|
|
23
24
|
end
|
24
25
|
")
|
25
26
|
end
|
27
|
+
|
28
|
+
def execute(job_data)
|
29
|
+
keep_current_tenant_unchange do
|
30
|
+
super(job_data)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def keep_current_tenant_unchange
|
37
|
+
_current_tenant = MultiTenantSupport::Current.tenant_account
|
38
|
+
yield
|
39
|
+
ensure
|
40
|
+
MultiTenantSupport::Current.tenant_account = _current_tenant
|
41
|
+
end
|
26
42
|
end
|
27
43
|
|
28
44
|
def perform_now
|
@@ -10,13 +10,9 @@ module MultiTenantSupport
|
|
10
10
|
|
11
11
|
private
|
12
12
|
|
13
|
-
define_method(MultiTenantSupport.current_tenant_account_method) do
|
14
|
-
instance_variable_get("@#{MultiTenantSupport.current_tenant_account_method}")
|
15
|
-
end
|
16
|
-
|
17
13
|
def set_current_tenant_account
|
18
14
|
tenant_account = find_current_tenant_account
|
19
|
-
MultiTenantSupport
|
15
|
+
MultiTenantSupport.set_current_tenant(tenant_account)
|
20
16
|
instance_variable_set("@#{MultiTenantSupport.current_tenant_account_method}", tenant_account)
|
21
17
|
end
|
22
18
|
|
@@ -31,8 +27,12 @@ module MultiTenantSupport
|
|
31
27
|
end
|
32
28
|
|
33
29
|
module ViewHelper
|
34
|
-
|
35
|
-
|
30
|
+
extend ActiveSupport::Concern
|
31
|
+
|
32
|
+
included do
|
33
|
+
define_method(MultiTenantSupport.current_tenant_account_method) do
|
34
|
+
instance_variable_get("@#{MultiTenantSupport.current_tenant_account_method}")
|
35
|
+
end
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -18,7 +18,7 @@ module MultiTenantSupport
|
|
18
18
|
|
19
19
|
def set_default_scope_under_current_tenant(foreign_key)
|
20
20
|
default_scope lambda {
|
21
|
-
if MultiTenantSupport.
|
21
|
+
if MultiTenantSupport.full_protected?
|
22
22
|
scope_under_current_tenant
|
23
23
|
else
|
24
24
|
where(nil)
|
@@ -37,7 +37,7 @@ module MultiTenantSupport
|
|
37
37
|
|
38
38
|
override_unscoped = Module.new {
|
39
39
|
define_method :unscoped do |&block|
|
40
|
-
if MultiTenantSupport.
|
40
|
+
if MultiTenantSupport.full_protected?
|
41
41
|
block ? relation.scope_under_current_tenant.scoping { block.call } : relation.scope_under_current_tenant
|
42
42
|
else
|
43
43
|
super(&block)
|
@@ -48,13 +48,13 @@ module MultiTenantSupport
|
|
48
48
|
|
49
49
|
override_insert_all = Module.new {
|
50
50
|
define_method :insert_all do |attributes, **arguments|
|
51
|
-
raise MissingTenantError unless MultiTenantSupport.current_tenant
|
51
|
+
raise MissingTenantError unless MultiTenantSupport.unprotected? || MultiTenantSupport.current_tenant
|
52
52
|
|
53
53
|
super(attributes, **arguments)
|
54
54
|
end
|
55
55
|
|
56
56
|
define_method :insert_all! do |attributes, **arguments|
|
57
|
-
raise MissingTenantError unless MultiTenantSupport.current_tenant
|
57
|
+
raise MissingTenantError unless MultiTenantSupport.unprotected? || MultiTenantSupport.current_tenant
|
58
58
|
|
59
59
|
super(attributes, **arguments)
|
60
60
|
end
|
@@ -63,7 +63,7 @@ module MultiTenantSupport
|
|
63
63
|
|
64
64
|
override_upsert_all = Module.new {
|
65
65
|
define_method :upsert_all do |attributes, **arguments|
|
66
|
-
warn "[WARNING] You are using upsert_all(or upsert) which may update records across tenants"
|
66
|
+
warn "[WARNING] You are using upsert_all(or upsert) which may update records across tenants" unless MultiTenantSupport.unprotected?
|
67
67
|
|
68
68
|
super(attributes, **arguments)
|
69
69
|
end
|
@@ -71,13 +71,16 @@ module MultiTenantSupport
|
|
71
71
|
extend override_upsert_all
|
72
72
|
|
73
73
|
after_initialize do |object|
|
74
|
-
if
|
75
|
-
|
76
|
-
|
77
|
-
|
74
|
+
next if object.new_record? && MultiTenantSupport.unprotected?
|
75
|
+
next if object.persisted? && MultiTenantSupport.allow_read_across_tenant?
|
76
|
+
|
77
|
+
raise MissingTenantError unless MultiTenantSupport.current_tenant
|
78
|
+
raise InvalidTenantAccess if object.send(foreign_key) != MultiTenantSupport.current_tenant_id
|
78
79
|
end
|
79
80
|
|
80
81
|
before_save do |object|
|
82
|
+
next if MultiTenantSupport.unprotected?
|
83
|
+
|
81
84
|
raise MissingTenantError unless MultiTenantSupport.current_tenant
|
82
85
|
raise NilTenantError if object.send(foreign_key).nil?
|
83
86
|
raise InvalidTenantAccess if object.send(foreign_key) != MultiTenantSupport.current_tenant_id
|
@@ -85,28 +88,24 @@ module MultiTenantSupport
|
|
85
88
|
|
86
89
|
override_update_columns_module = Module.new {
|
87
90
|
define_method :update_columns do |attributes|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
+
unless MultiTenantSupport.unprotected?
|
92
|
+
raise MissingTenantError unless MultiTenantSupport.current_tenant
|
93
|
+
raise NilTenantError if send(foreign_key).nil?
|
94
|
+
raise InvalidTenantAccess if send(foreign_key) != MultiTenantSupport.current_tenant_id
|
95
|
+
end
|
91
96
|
|
92
97
|
super(attributes)
|
93
98
|
end
|
94
|
-
|
95
|
-
define_method :update_column do |name, value|
|
96
|
-
raise MissingTenantError unless MultiTenantSupport.current_tenant
|
97
|
-
raise NilTenantError if send(foreign_key).nil?
|
98
|
-
raise InvalidTenantAccess if send(foreign_key) != MultiTenantSupport.current_tenant_id
|
99
|
-
|
100
|
-
super(name, value)
|
101
|
-
end
|
102
99
|
}
|
103
100
|
|
104
101
|
include override_update_columns_module
|
105
102
|
|
106
103
|
override_delete = Module.new {
|
107
104
|
define_method :delete do
|
108
|
-
|
109
|
-
|
105
|
+
unless MultiTenantSupport.unprotected?
|
106
|
+
raise MissingTenantError unless MultiTenantSupport.current_tenant
|
107
|
+
raise InvalidTenantAccess if send(foreign_key) != MultiTenantSupport.current_tenant_id
|
108
|
+
end
|
110
109
|
|
111
110
|
super()
|
112
111
|
end
|
@@ -115,7 +114,9 @@ module MultiTenantSupport
|
|
115
114
|
|
116
115
|
override_delete_by = Module.new {
|
117
116
|
define_method :delete_by do |*args|
|
118
|
-
|
117
|
+
unless MultiTenantSupport.unprotected?
|
118
|
+
raise MissingTenantError unless MultiTenantSupport.current_tenant
|
119
|
+
end
|
119
120
|
|
120
121
|
super(*args)
|
121
122
|
end
|
@@ -123,8 +124,10 @@ module MultiTenantSupport
|
|
123
124
|
extend override_delete_by
|
124
125
|
|
125
126
|
before_destroy do |object|
|
126
|
-
|
127
|
-
|
127
|
+
unless MultiTenantSupport.unprotected?
|
128
|
+
raise MissingTenantError unless MultiTenantSupport.current_tenant
|
129
|
+
raise InvalidTenantAccess if object.send(foreign_key) != MultiTenantSupport.current_tenant_id
|
130
|
+
end
|
128
131
|
end
|
129
132
|
end
|
130
133
|
|
@@ -132,9 +135,12 @@ module MultiTenantSupport
|
|
132
135
|
readonly_tenant_module = Module.new {
|
133
136
|
|
134
137
|
define_method "#{tenant_name}=" do |tenant|
|
138
|
+
return super(tenant_account) if MultiTenantSupport.unprotected?
|
139
|
+
|
135
140
|
raise NilTenantError if tenant.nil?
|
136
141
|
raise MissingTenantError unless MultiTenantSupport.current_tenant
|
137
142
|
|
143
|
+
|
138
144
|
if new_record? && tenant == MultiTenantSupport.current_tenant
|
139
145
|
super tenant
|
140
146
|
else
|
@@ -143,6 +149,8 @@ module MultiTenantSupport
|
|
143
149
|
end
|
144
150
|
|
145
151
|
define_method "#{foreign_key}=" do |key|
|
152
|
+
return super(tenant_account) if MultiTenantSupport.unprotected?
|
153
|
+
|
146
154
|
raise NilTenantError if key.nil?
|
147
155
|
raise MissingTenantError unless MultiTenantSupport.current_tenant
|
148
156
|
|
@@ -170,9 +178,11 @@ ActiveSupport.on_load(:active_record) do |base|
|
|
170
178
|
|
171
179
|
override_delete_all = Module.new {
|
172
180
|
define_method :delete_all do
|
173
|
-
|
174
|
-
|
175
|
-
|
181
|
+
unless MultiTenantSupport.unprotected?
|
182
|
+
current_tenant_exist = MultiTenantSupport.current_tenant
|
183
|
+
is_global_model = !MultiTenantSupport.model.tenanted_models.include?(klass.name)
|
184
|
+
raise MultiTenantSupport::MissingTenantError unless current_tenant_exist || is_global_model
|
185
|
+
end
|
176
186
|
|
177
187
|
super()
|
178
188
|
end
|
@@ -181,9 +191,11 @@ ActiveSupport.on_load(:active_record) do |base|
|
|
181
191
|
|
182
192
|
override_update_all = Module.new {
|
183
193
|
define_method :update_all do |updates|
|
184
|
-
|
185
|
-
|
186
|
-
|
194
|
+
unless MultiTenantSupport.unprotected?
|
195
|
+
current_tenant_exist = MultiTenantSupport.current_tenant
|
196
|
+
is_global_model = !MultiTenantSupport.model.tenanted_models.include?(klass.name)
|
197
|
+
raise MultiTenantSupport::MissingTenantError unless current_tenant_exist || is_global_model
|
198
|
+
end
|
187
199
|
|
188
200
|
super(updates)
|
189
201
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module MultiTenantSupport
|
2
|
+
|
3
|
+
module Config
|
4
|
+
class Console
|
5
|
+
attr_writer :allow_read_across_tenant_by_default
|
6
|
+
|
7
|
+
def allow_read_across_tenant_by_default
|
8
|
+
@allow_read_across_tenant_by_default ||= false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module_function
|
14
|
+
def console
|
15
|
+
@console ||= Config::Console.new
|
16
|
+
return @console unless block_given?
|
17
|
+
|
18
|
+
yield @console
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -1,8 +1,23 @@
|
|
1
1
|
require 'active_support'
|
2
2
|
|
3
3
|
module MultiTenantSupport
|
4
|
+
|
5
|
+
# Scoped and proteced
|
6
|
+
PROTECTED = 1
|
7
|
+
|
8
|
+
# Scoped and protected except read across tenant
|
9
|
+
PROTECTED_EXCEPT_READ = 2
|
10
|
+
|
11
|
+
# Scoped but unprotected
|
12
|
+
UNPROTECTED = 3
|
13
|
+
|
14
|
+
# This class is for internal usage only
|
4
15
|
class Current < ActiveSupport::CurrentAttributes
|
5
16
|
attribute :tenant_account,
|
6
|
-
:
|
17
|
+
:protection_state
|
18
|
+
|
19
|
+
def protection_state
|
20
|
+
attributes[:protection_state] ||= PROTECTED
|
21
|
+
end
|
7
22
|
end
|
8
23
|
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
require_relative "./test/integration"
|
2
|
+
require_relative "./test/system"
|
3
|
+
require_relative "./test/capybara"
|
4
|
+
|
5
|
+
ActionDispatch::IntegrationTest.prepend(MultiTenantSupport::Test::Integration)
|
6
|
+
ActionDispatch::SystemTestCase.prepend(MultiTenantSupport::Test::System)
|
7
|
+
Capybara::Node::Element.prepend(MultiTenantSupport::Test::Capybara)
|
@@ -9,5 +9,13 @@ module MultiTenantSupport
|
|
9
9
|
config.app_generators.templates.unshift(active_record_templates)
|
10
10
|
end
|
11
11
|
|
12
|
+
console do
|
13
|
+
if ENV["ALLOW_READ_ACROSS_TENANT"] || MultiTenantSupport.console.allow_read_across_tenant_by_default
|
14
|
+
MultiTenantSupport.allow_read_across_tenant
|
15
|
+
else
|
16
|
+
MultiTenantSupport.turn_on_full_protection
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
12
20
|
end
|
13
21
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative "./test/integration"
|
2
|
+
require_relative "./test/system"
|
3
|
+
require_relative "./test/capybara"
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.include MultiTenantSupport::Test::Integration, type: :request
|
7
|
+
config.include MultiTenantSupport::Test::Integration, type: :controller
|
8
|
+
|
9
|
+
config.include MultiTenantSupport::Test::System, type: :system
|
10
|
+
config.include MultiTenantSupport::Test::System, type: :feature
|
11
|
+
end
|
12
|
+
|
13
|
+
Capybara::Node::Element.prepend(MultiTenantSupport::Test::Capybara)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module MultiTenantSupport
|
2
|
+
module Test
|
3
|
+
module Capybara
|
4
|
+
|
5
|
+
def set(value, **options)
|
6
|
+
keep_context_tenant_unchange do
|
7
|
+
super(value, **options)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def select_option(wait: nil)
|
12
|
+
keep_context_tenant_unchange do
|
13
|
+
super(wait: wait)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def unselect_option(wait: nil)
|
18
|
+
keep_context_tenant_unchange do
|
19
|
+
super(wait: wait)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def perform_click_action(keys, wait: nil, **options)
|
24
|
+
keep_context_tenant_unchange do
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def trigger(event)
|
30
|
+
keep_context_tenant_unchange do
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def evaluate_script(script, *args)
|
36
|
+
keep_context_tenant_unchange do
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def evaluate_async_script(script, *args)
|
42
|
+
keep_context_tenant_unchange do
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def keep_context_tenant_unchange
|
48
|
+
_current_tenant = MultiTenantSupport::Current.tenant_account
|
49
|
+
MultiTenantSupport::Current.tenant_account = nil # Simulate real circumstance
|
50
|
+
yield
|
51
|
+
ensure
|
52
|
+
MultiTenantSupport::Current.tenant_account = _current_tenant
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module MultiTenantSupport
|
2
|
+
module Test
|
3
|
+
module Integration
|
4
|
+
|
5
|
+
%i[get post patch put delete head options].each do |method|
|
6
|
+
define_method method do |path, **args|
|
7
|
+
keep_context_tenant_unchange do
|
8
|
+
super(path, **args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def follow_redirect(**args)
|
14
|
+
keep_context_tenant_unchange do
|
15
|
+
super(**args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def keep_context_tenant_unchange
|
20
|
+
_current_tenant = MultiTenantSupport::Current.tenant_account
|
21
|
+
MultiTenantSupport::Current.tenant_account = nil # Simulate real circumstance
|
22
|
+
yield
|
23
|
+
ensure
|
24
|
+
MultiTenantSupport::Current.tenant_account = _current_tenant
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module MultiTenantSupport
|
2
|
+
module Test
|
3
|
+
module System
|
4
|
+
|
5
|
+
%i[
|
6
|
+
visit refresh click_on go_back go_forward
|
7
|
+
check choose click_button click_link
|
8
|
+
fill_in uncheck check unselect select
|
9
|
+
execute_script evaluate_script
|
10
|
+
].each do |method|
|
11
|
+
if RUBY_VERSION >= '2.7'
|
12
|
+
class_eval <<~METHOD, __FILE__, __LINE__ + 1
|
13
|
+
def #{method}(...)
|
14
|
+
keep_context_tenant_unchange do
|
15
|
+
super(...)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
METHOD
|
19
|
+
else
|
20
|
+
define_method method do |*args, &block|
|
21
|
+
keep_context_tenant_unchange do
|
22
|
+
super(*args, &block)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def keep_context_tenant_unchange
|
29
|
+
_current_tenant = MultiTenantSupport::Current.tenant_account
|
30
|
+
MultiTenantSupport::Current.tenant_account = nil # Simulate real circumstance
|
31
|
+
yield
|
32
|
+
ensure
|
33
|
+
MultiTenantSupport::Current.tenant_account = _current_tenant
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: multi-tenant-support
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hopper Gee
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-10-
|
11
|
+
date: 2021-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -45,13 +45,19 @@ files:
|
|
45
45
|
- lib/multi_tenant_support/concern/controller_concern.rb
|
46
46
|
- lib/multi_tenant_support/concern/model_concern.rb
|
47
47
|
- lib/multi_tenant_support/config/app.rb
|
48
|
+
- lib/multi_tenant_support/config/console.rb
|
48
49
|
- lib/multi_tenant_support/config/controller.rb
|
49
50
|
- lib/multi_tenant_support/config/model.rb
|
50
51
|
- lib/multi_tenant_support/current.rb
|
51
52
|
- lib/multi_tenant_support/errors.rb
|
52
53
|
- lib/multi_tenant_support/find_tenant_account.rb
|
54
|
+
- lib/multi_tenant_support/minitest.rb
|
53
55
|
- lib/multi_tenant_support/railtie.rb
|
56
|
+
- lib/multi_tenant_support/rspec.rb
|
54
57
|
- lib/multi_tenant_support/sidekiq.rb
|
58
|
+
- lib/multi_tenant_support/test/capybara.rb
|
59
|
+
- lib/multi_tenant_support/test/integration.rb
|
60
|
+
- lib/multi_tenant_support/test/system.rb
|
55
61
|
- lib/multi_tenant_support/version.rb
|
56
62
|
- lib/tasks/multi_tenant_support_tasks.rake
|
57
63
|
homepage: https://github.com/hoppergee/multi-tenant-support
|