multi-tenant-support 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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