multi-tenant-support 1.3.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 +45 -15
- data/lib/multi-tenant-support.rb +40 -9
- data/lib/multi_tenant_support/concern/controller_concern.rb +1 -1
- data/lib/multi_tenant_support/concern/model_concern.rb +43 -31
- data/lib/multi_tenant_support/current.rb +16 -1
- data/lib/multi_tenant_support/railtie.rb +1 -1
- data/lib/multi_tenant_support/version.rb +1 -1
- metadata +2 -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
@@ -240,18 +240,49 @@ end
|
|
240
240
|
### Set current tenant global
|
241
241
|
|
242
242
|
```ruby
|
243
|
-
MultiTenantSupport
|
243
|
+
MultiTenantSupport.set_tenant_account(account)
|
244
244
|
```
|
245
245
|
|
246
|
-
###
|
246
|
+
### Temp set current tenant to nil
|
247
247
|
|
248
|
-
|
248
|
+
```ruby
|
249
|
+
MultiTenantSupport.without_current_tenant do
|
250
|
+
# ...
|
251
|
+
end
|
252
|
+
```
|
253
|
+
|
254
|
+
### 3 protection states
|
255
|
+
|
256
|
+
1. `MultiTenantSupport.full_protected?`
|
257
|
+
2. `MultiTenantSupport.allow_read_across_tenant?`
|
258
|
+
3. `MultiTenantSupport.unprotected?`
|
259
|
+
|
260
|
+
#### Full protection(default)
|
261
|
+
|
262
|
+
The default state is full protection. This gem disallow modify record across tenant by default.
|
263
|
+
|
264
|
+
If `MultiTenantSupport.current_tenant` exist, you can only modify those records under this tenant, otherwise, you will get some errors like:
|
265
|
+
|
266
|
+
- `MultiTenantSupport::MissingTenantError`
|
267
|
+
- `MultiTenantSupport::ImmutableTenantError`
|
268
|
+
- `MultiTenantSupport::NilTenantError`
|
269
|
+
- `MultiTenantSupport::InvalidTenantAccess`
|
270
|
+
- `ActiveRecord::RecordNotFound`
|
271
|
+
|
272
|
+
If `MultiTenantSupport.current_tenant` is missing, you cannot modify or create any tenanted records.
|
273
|
+
|
274
|
+
If you switched to other state, you can switch back through:
|
249
275
|
|
250
276
|
```ruby
|
251
|
-
MultiTenantSupport.
|
277
|
+
MultiTenantSupport.turn_on_full_protection
|
278
|
+
|
279
|
+
# Or
|
280
|
+
MultiTenantSupport.turn_on_full_protection do
|
281
|
+
# ...
|
282
|
+
end
|
252
283
|
```
|
253
284
|
|
254
|
-
|
285
|
+
#### Allow read across tenant for super admin
|
255
286
|
|
256
287
|
You can turn on the permission to read records across tenant through:
|
257
288
|
|
@@ -266,19 +297,18 @@ end
|
|
266
297
|
|
267
298
|
You can put it in a before action in SuperAdmin's controllers
|
268
299
|
|
269
|
-
|
300
|
+
#### Turn off protection
|
270
301
|
|
271
|
-
|
302
|
+
Sometimes, as a super admin, we need to execute certain maintenatn operations over all tenant records. You can do this through:
|
272
303
|
|
273
|
-
|
274
|
-
|
275
|
-
- `MultiTenantSupport::MissingTenantError`
|
276
|
-
- `MultiTenantSupport::ImmutableTenantError`
|
277
|
-
- `MultiTenantSupport::NilTenantError`
|
278
|
-
- `MultiTenantSupport::InvalidTenantAccess`
|
279
|
-
- `ActiveRecord::RecordNotFound`
|
304
|
+
```ruby
|
305
|
+
MultiTenantSupport.turn_off_protection
|
280
306
|
|
281
|
-
|
307
|
+
# Or
|
308
|
+
MultiTenantSupport.turn_off_protection do
|
309
|
+
# ...
|
310
|
+
end
|
311
|
+
```
|
282
312
|
|
283
313
|
### Set current tenant acccount in controller by default
|
284
314
|
|
data/lib/multi-tenant-support.rb
CHANGED
@@ -26,39 +26,70 @@ module MultiTenantSupport
|
|
26
26
|
Current.tenant_account&.send(model.tenant_account_primary_key)
|
27
27
|
end
|
28
28
|
|
29
|
+
def set_current_tenant(tenant)
|
30
|
+
Current.tenant_account = tenant
|
31
|
+
Current.protection_state = PROTECTED
|
32
|
+
end
|
33
|
+
|
29
34
|
def under_tenant(tenant_account, &block)
|
30
35
|
raise ArgumentError, "block is missing" if block.nil?
|
31
36
|
|
32
|
-
Current.set(tenant_account: tenant_account) do
|
37
|
+
Current.set(tenant_account: tenant_account, protection_state: PROTECTED) do
|
38
|
+
yield
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def without_current_tenant(&block)
|
43
|
+
raise ArgumentError, "block is missing" if block.nil?
|
44
|
+
|
45
|
+
Current.set(tenant_account: nil) do
|
33
46
|
yield
|
34
47
|
end
|
35
48
|
end
|
36
49
|
|
50
|
+
def full_protected?
|
51
|
+
current_tenant || Current.protection_state == PROTECTED
|
52
|
+
end
|
53
|
+
|
37
54
|
def allow_read_across_tenant?
|
38
|
-
|
55
|
+
current_tenant.nil? && [PROTECTED_EXCEPT_READ, UNPROTECTED].include?(Current.protection_state)
|
39
56
|
end
|
40
57
|
|
41
|
-
def
|
42
|
-
|
58
|
+
def unprotected?
|
59
|
+
current_tenant.nil? && Current.protection_state == UNPROTECTED
|
43
60
|
end
|
44
61
|
|
45
|
-
def
|
62
|
+
def turn_off_protection
|
63
|
+
raise 'Cannot turn off protection, try wrap in without_current_tenant' if current_tenant
|
64
|
+
|
46
65
|
if block_given?
|
47
|
-
Current.set(
|
66
|
+
Current.set(protection_state: UNPROTECTED) do
|
48
67
|
yield
|
49
68
|
end
|
50
69
|
else
|
51
|
-
Current.
|
70
|
+
Current.protection_state = UNPROTECTED
|
52
71
|
end
|
53
72
|
end
|
54
73
|
|
55
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
|
56
87
|
if block_given?
|
57
|
-
Current.set(
|
88
|
+
Current.set(protection_state: PROTECTED) do
|
58
89
|
yield
|
59
90
|
end
|
60
91
|
else
|
61
|
-
Current.
|
92
|
+
Current.protection_state = PROTECTED
|
62
93
|
end
|
63
94
|
end
|
64
95
|
|
@@ -12,7 +12,7 @@ module MultiTenantSupport
|
|
12
12
|
|
13
13
|
def set_current_tenant_account
|
14
14
|
tenant_account = find_current_tenant_account
|
15
|
-
MultiTenantSupport
|
15
|
+
MultiTenantSupport.set_current_tenant(tenant_account)
|
16
16
|
instance_variable_set("@#{MultiTenantSupport.current_tenant_account_method}", tenant_account)
|
17
17
|
end
|
18
18
|
|
@@ -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
|
@@ -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
|
@@ -13,7 +13,7 @@ module MultiTenantSupport
|
|
13
13
|
if ENV["ALLOW_READ_ACROSS_TENANT"] || MultiTenantSupport.console.allow_read_across_tenant_by_default
|
14
14
|
MultiTenantSupport.allow_read_across_tenant
|
15
15
|
else
|
16
|
-
MultiTenantSupport.
|
16
|
+
MultiTenantSupport.turn_on_full_protection
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
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
|