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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9a0f7c5067f07151a631c2b67b7b4ab6fb65da04f5b67c8020fec6d89ffe0f6
4
- data.tar.gz: dbde15d968752339fbc72ec3aed925fa706863c88cdf5f06cb0036fda4c8679c
3
+ metadata.gz: 2780344bf11a6b5bb065ebed1848d5630ba8b414ab1d5ed9d08a019cf7751265
4
+ data.tar.gz: feb76616d756d524e8c871afdf331d6a754e522c2951e6c3cf15d6cdf2463093
5
5
  SHA512:
6
- metadata.gz: 38e715d1ad5361a4db0cbb2b4b8cde6df8572a72186be05a54949649aad6460e69ef13e2080dbbbe678c64caeb6d7e71e7b9bafc86734266dabe957b947493ca
7
- data.tar.gz: 3bf0ff4690f643d531b897d487e3ca519df588ea2e08cd7036d3ba65dbbad3e68f129c0c6596b92bf222833112ac59d1ba6eb2c042ac8db8e990fc0267656c67
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::Current.tenant_account = account
243
+ MultiTenantSupport.set_tenant_account(account)
244
244
  ```
245
245
 
246
- ### Disallow read across tenant by default
246
+ ### Temp set current tenant to nil
247
247
 
248
- This gem disallow read across tenant by default. You can check current state through:
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.disallow_read_across_tenant?
277
+ MultiTenantSupport.turn_on_full_protection
278
+
279
+ # Or
280
+ MultiTenantSupport.turn_on_full_protection do
281
+ # ...
282
+ end
252
283
  ```
253
284
 
254
- ### Allow read across tenant for super admin
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
- ### Disallow modify records tenant
300
+ #### Turn off protection
270
301
 
271
- This gem disallow modify record across tenant no matter you are super admin or not.
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
- If `MultiTenantSupport.current_tenant` exist, you can only modify those records under this tenant, otherwise, you will get some errors like:
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
- If `MultiTenantSupport.current_tenant` is missing, you cannot modify or create any tenanted records.
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
 
@@ -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
- !disallow_read_across_tenant?
55
+ current_tenant.nil? && [PROTECTED_EXCEPT_READ, UNPROTECTED].include?(Current.protection_state)
39
56
  end
40
57
 
41
- def disallow_read_across_tenant?
42
- !Current.allow_read_across_tenant
58
+ def unprotected?
59
+ current_tenant.nil? && Current.protection_state == UNPROTECTED
43
60
  end
44
61
 
45
- def disallow_read_across_tenant
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(allow_read_across_tenant: false) do
66
+ Current.set(protection_state: UNPROTECTED) do
48
67
  yield
49
68
  end
50
69
  else
51
- Current.allow_read_across_tenant = false
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(allow_read_across_tenant: true) do
88
+ Current.set(protection_state: PROTECTED) do
58
89
  yield
59
90
  end
60
91
  else
61
- Current.allow_read_across_tenant = true
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::Current.tenant_account = tenant_account
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.disallow_read_across_tenant? || MultiTenantSupport.current_tenant
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.disallow_read_across_tenant? || MultiTenantSupport.current_tenant
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 MultiTenantSupport.disallow_read_across_tenant? || object.new_record?
75
- raise MissingTenantError unless MultiTenantSupport.current_tenant
76
- raise InvalidTenantAccess if object.send(foreign_key) != MultiTenantSupport.current_tenant_id
77
- end
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
- raise MissingTenantError unless MultiTenantSupport.current_tenant
89
- raise NilTenantError if send(foreign_key).nil?
90
- raise InvalidTenantAccess if send(foreign_key) != MultiTenantSupport.current_tenant_id
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
- raise MissingTenantError unless MultiTenantSupport.current_tenant
109
- raise InvalidTenantAccess if send(foreign_key) != MultiTenantSupport.current_tenant_id
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
- raise MissingTenantError unless MultiTenantSupport.current_tenant
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
- raise MissingTenantError unless MultiTenantSupport.current_tenant
127
- raise InvalidTenantAccess if object.send(foreign_key) != MultiTenantSupport.current_tenant_id
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
- current_tenant_exist = MultiTenantSupport.current_tenant
174
- is_global_model = !MultiTenantSupport.model.tenanted_models.include?(klass.name)
175
- raise MultiTenantSupport::MissingTenantError unless current_tenant_exist || is_global_model
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
- current_tenant_exist = MultiTenantSupport.current_tenant
185
- is_global_model = !MultiTenantSupport.model.tenanted_models.include?(klass.name)
186
- raise MultiTenantSupport::MissingTenantError unless current_tenant_exist || is_global_model
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
- :allow_read_across_tenant
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.disallow_read_across_tenant
16
+ MultiTenantSupport.turn_on_full_protection
17
17
  end
18
18
  end
19
19
 
@@ -1,3 +1,3 @@
1
1
  module MultiTenantSupport
2
- VERSION = '1.3.1'
2
+ VERSION = '1.4.0'
3
3
  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.3.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-10 00:00:00.000000000 Z
11
+ date: 2021-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails