live_record 0.2.7 → 0.2.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +15 -12
- data/app/channels/live_record/base_channel.rb +24 -5
- data/app/channels/live_record/changes_channel.rb +8 -10
- data/app/channels/live_record/publications_channel.rb +11 -9
- data/app/channels/live_record/publications_channel/search_adapters.rb +2 -2
- data/lib/live_record/generators/templates/model.rb.rb +2 -2
- data/lib/live_record/version.rb +1 -1
- data/spec/internal/app/models/post.rb +2 -21
- data/spec/internal/app/models/user.rb +1 -3
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d08ed348f467c4dad7084528d36552775a03f365
|
4
|
+
data.tar.gz: 3698f07abf6f4e6edc1430870fb8eb7ad6862bad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 482a0751d39af81591ac4137fc2a8f12206fa4e687aeb5b1508c1ce60fb85fe1d92465ff853affad114017f1f97e19a859a2d39553f3d92d387d05ccc5840630
|
7
|
+
data.tar.gz: '081b8691a3f37984961031d230afa749603c9c61e402347d63a914ed854170cf6c2df561c68d8be8514e0bfc04cfc139cfbe1878573ff72fc5057a94bb0401b8'
|
data/README.md
CHANGED
@@ -101,9 +101,9 @@
|
|
101
101
|
# Add attributes to this array that you would like `current_user` to have access to when syncing this particular `book`
|
102
102
|
# empty array means not-authorised
|
103
103
|
if book.user == current_user
|
104
|
-
[:title, :author, :created_at, :updated_at, :reference_id, :origin_address]
|
104
|
+
[:id, :title, :author, :created_at, :updated_at, :reference_id, :origin_address]
|
105
105
|
elsif current_user.present?
|
106
|
-
[:title, :author, :created_at, :updated_at]
|
106
|
+
[:id ,:title, :author, :created_at, :updated_at]
|
107
107
|
else
|
108
108
|
[]
|
109
109
|
end
|
@@ -113,7 +113,7 @@
|
|
113
113
|
# Add attributes to this array that you would like `current_user` to be able to query upon on the `subscribe({where: {...}}) function`
|
114
114
|
# empty array means not-authorised
|
115
115
|
if current_user.isAdmin?
|
116
|
-
[:title, :author, :created_at, :updated_at, :reference_id, :origin_address]
|
116
|
+
[:id, :title, :author, :created_at, :updated_at, :reference_id, :origin_address]
|
117
117
|
else
|
118
118
|
[]
|
119
119
|
end
|
@@ -127,7 +127,7 @@
|
|
127
127
|
1. Add the following to your `Gemfile`:
|
128
128
|
|
129
129
|
```ruby
|
130
|
-
gem 'live_record', '~> 0.2.
|
130
|
+
gem 'live_record', '~> 0.2.8'
|
131
131
|
```
|
132
132
|
|
133
133
|
2. Run:
|
@@ -180,13 +180,13 @@
|
|
180
180
|
def self.live_record_whitelisted_attributes(book, current_user)
|
181
181
|
# Add attributes to this array that you would like current_user to have access to when syncing.
|
182
182
|
# Defaults to empty array, thereby blocking everything by default, only unless explicitly stated here so.
|
183
|
-
[:title, :author, :created_at, :updated_at]
|
183
|
+
[:id, :title, :author, :created_at, :updated_at]
|
184
184
|
end
|
185
185
|
|
186
186
|
def self.live_record_queryable_attributes(current_user)
|
187
187
|
# Add attributes to this array that you would like current_user to query upon when using `.subscribe({where: {...})`
|
188
188
|
# Defaults to empty array, thereby blocking everything by default, only unless explicitly stated here so.
|
189
|
-
[:title, :author, :created_at, :updated_at]
|
189
|
+
[:id, :title, :author, :created_at, :updated_at]
|
190
190
|
end
|
191
191
|
end
|
192
192
|
```
|
@@ -203,9 +203,9 @@
|
|
203
203
|
# Notice that from above, you also have access to `book` (the record currently requested by the client to be synced),
|
204
204
|
# and the `current_user`, the current user who is trying to sync the `book` record.
|
205
205
|
if book.user == current_user
|
206
|
-
[:title, :author, :created_at, :updated_at, :reference_id, :origin_address]
|
206
|
+
[:id, :title, :author, :created_at, :updated_at, :reference_id, :origin_address]
|
207
207
|
elsif current_user.present?
|
208
|
-
[:title, :author, :created_at, :updated_at]
|
208
|
+
[:id, :title, :author, :created_at, :updated_at]
|
209
209
|
else
|
210
210
|
[]
|
211
211
|
end
|
@@ -215,7 +215,7 @@
|
|
215
215
|
# this method should look like your `live_record_whitelisted_attributes` above, only except if you want to further customise this for flexibility
|
216
216
|
# or... that you may just simply return `[]` (empty array) if you do not want to allow users to use `subscribe()`
|
217
217
|
# also take note that this method only has `current_user` argument compared to `live_record_whitelisted_attributes` above which also has the `book` argument. This is intended for SQL performance reasons
|
218
|
-
[:title, :author, :created_at, :updated_at]
|
218
|
+
[:id, :title, :author, :created_at, :updated_at]
|
219
219
|
end
|
220
220
|
end
|
221
221
|
```
|
@@ -439,14 +439,14 @@
|
|
439
439
|
has_many :live_record_updates, as: :recordable, dependent: :destroy
|
440
440
|
|
441
441
|
def self.live_record_whitelisted_attributes(book, current_user)
|
442
|
-
[:title, :is_enabled]
|
442
|
+
[:id, :title, :is_enabled]
|
443
443
|
end
|
444
444
|
|
445
445
|
## this method will be invoked when `subscribe()` is called
|
446
446
|
## but, you should not use this method when using `ransack` gem!
|
447
447
|
## ransack's methods like `ransackable_attributes` below will be invoked instead
|
448
448
|
# def self.live_record_queryable_attributes(book, current_user)
|
449
|
-
# [:title, :is_enabled]
|
449
|
+
# [:id, :title, :is_enabled]
|
450
450
|
# end
|
451
451
|
|
452
452
|
private
|
@@ -659,11 +659,14 @@
|
|
659
659
|
* MIT
|
660
660
|
|
661
661
|
## Changelog
|
662
|
+
* 0.2.8
|
663
|
+
* You can now specify `:id` into `live_record_whitelisted_attributes` for verbosity; used to be automatically-included by default. Needed to do this otherwise there was this minor bug where `subscribe()` still receives records (having just `:id` attribute though) even when it is specified to be not-authorized.
|
664
|
+
* fixed minor bug when `live_record_whitelisted_attributes` is not returning anything, throwing a `NoMethodError`
|
662
665
|
* 0.2.7
|
663
666
|
* improved performance when using `subscribe({reload: true})`, but by doing so, I am forced to a conscious decision to have another separate model method for "queryable" attributes: `live_record_queryable_attributes`, which both has `pro` and `cons`
|
664
667
|
* pros:
|
665
668
|
* greatly sped up SQL for loading of data
|
666
|
-
* pro & con: now two methods in model: `live_record_whitelisted_attributes` and `live_record_queryable_attributes` which should have mostly the same code [(see some differences above)](
|
669
|
+
* pro & con: now two methods in model: `live_record_whitelisted_attributes` and `live_record_queryable_attributes` which should have mostly the same code [(see some differences above)](#example-1---simple-usage) which makes it repetitive to some degree, although this allows more flexibility.
|
667
670
|
* added `matches` and `does_not_match` filters
|
668
671
|
* 0.2.6
|
669
672
|
* fixed minor bug where `MODELINSTANCE.changes` do not accurately work on NULL values.
|
@@ -1,13 +1,32 @@
|
|
1
1
|
class LiveRecord::BaseChannel < ActionCable::Channel::Base
|
2
2
|
|
3
|
-
|
3
|
+
module Helpers
|
4
|
+
def self.whitelisted_attributes(record, current_user)
|
5
|
+
whitelisted_attributes = record.class.live_record_whitelisted_attributes(record, current_user)
|
6
|
+
|
7
|
+
unless whitelisted_attributes.is_a? Array
|
8
|
+
raise "#{record.class}.live_record_whitelisted_attributes should return an array"
|
9
|
+
end
|
10
|
+
|
11
|
+
whitelisted_attributes = whitelisted_attributes.map(&:to_s)
|
12
|
+
|
13
|
+
if !whitelisted_attributes.empty? && !whitelisted_attributes.include?('id')
|
14
|
+
raise "#{record.class}.live_record_whitelisted_attributes should return an array that also includes the :id attribute, as you are authorizing at least one other attribute along with it."
|
15
|
+
end
|
4
16
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
17
|
+
whitelisted_attributes.to_set
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.queryable_attributes(model_class, current_user)
|
21
|
+
queryable_attributes = model_class.live_record_queryable_attributes(current_user)
|
22
|
+
raise "#{model_class}.live_record_queryable_attributes should return an array" unless queryable_attributes.is_a? Array
|
23
|
+
queryable_attributes = queryable_attributes.map(&:to_s)
|
24
|
+
queryable_attributes.to_set
|
25
|
+
end
|
9
26
|
end
|
10
27
|
|
28
|
+
protected
|
29
|
+
|
11
30
|
def filtered_message(message, filters)
|
12
31
|
message['attributes'].slice!(*filters) if message['attributes'].present?
|
13
32
|
message
|
@@ -5,20 +5,19 @@ class LiveRecord::ChangesChannel < LiveRecord::BaseChannel
|
|
5
5
|
|
6
6
|
def subscribed
|
7
7
|
find_record_from_params(params) do |record|
|
8
|
-
|
8
|
+
whitelisted_attributes = Helpers.whitelisted_attributes(record, current_user)
|
9
9
|
|
10
|
-
if
|
10
|
+
if whitelisted_attributes.present?
|
11
11
|
stream_for record, coder: ActiveSupport::JSON do |message|
|
12
12
|
begin
|
13
13
|
record.reload
|
14
14
|
rescue ActiveRecord::RecordNotFound
|
15
15
|
end
|
16
16
|
|
17
|
-
|
17
|
+
whitelisted_attributes = Helpers.whitelisted_attributes(record, current_user)
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
response = filtered_message(message, authorised_attributes)
|
19
|
+
if whitelisted_attributes.size > 0
|
20
|
+
response = filtered_message(message, whitelisted_attributes)
|
22
21
|
transmit response if response.present?
|
23
22
|
else
|
24
23
|
respond_with_error(:forbidden)
|
@@ -36,10 +35,9 @@ class LiveRecord::ChangesChannel < LiveRecord::BaseChannel
|
|
36
35
|
params = data.symbolize_keys
|
37
36
|
|
38
37
|
find_record_from_params(params) do |record|
|
39
|
-
|
38
|
+
whitelisted_attributes = Helpers.whitelisted_attributes(record, current_user)
|
40
39
|
|
41
|
-
|
42
|
-
if authorised_attributes.size > 1
|
40
|
+
if whitelisted_attributes.size > 0
|
43
41
|
live_record_updates = nil
|
44
42
|
|
45
43
|
if params[:stale_since].present?
|
@@ -55,7 +53,7 @@ class LiveRecord::ChangesChannel < LiveRecord::BaseChannel
|
|
55
53
|
# then we update the record in the client-side
|
56
54
|
if params[:stale_since].blank? || live_record_updates.exists?
|
57
55
|
message = { 'action' => 'update', 'attributes' => record.attributes }
|
58
|
-
response = filtered_message(message,
|
56
|
+
response = filtered_message(message, whitelisted_attributes)
|
59
57
|
transmit response if response.present?
|
60
58
|
end
|
61
59
|
else
|
@@ -14,7 +14,7 @@ class LiveRecord::PublicationsChannel < LiveRecord::BaseChannel
|
|
14
14
|
reject_subscription
|
15
15
|
end
|
16
16
|
|
17
|
-
if !(model_class &&
|
17
|
+
if !(model_class && Helpers.queryable_attributes(model_class, current_user).present?)
|
18
18
|
respond_with_error(:forbidden, 'You do not have privileges to query')
|
19
19
|
reject_subscription
|
20
20
|
end
|
@@ -29,11 +29,11 @@ class LiveRecord::PublicationsChannel < LiveRecord::BaseChannel
|
|
29
29
|
)
|
30
30
|
|
31
31
|
if active_record_relation.exists?(id: newly_created_record.id)
|
32
|
-
|
33
|
-
|
34
|
-
if
|
32
|
+
whitelisted_attributes = Helpers.whitelisted_attributes(newly_created_record, current_user)
|
33
|
+
|
34
|
+
if whitelisted_attributes.size > 0
|
35
35
|
message = { 'action' => 'create', 'attributes' => message['attributes'] }
|
36
|
-
response = filtered_message(message,
|
36
|
+
response = filtered_message(message, whitelisted_attributes)
|
37
37
|
transmit response if response.present?
|
38
38
|
end
|
39
39
|
end
|
@@ -61,11 +61,13 @@ class LiveRecord::PublicationsChannel < LiveRecord::BaseChannel
|
|
61
61
|
# we `transmmit` a message back to client for each matching record
|
62
62
|
active_record_relation.find_each do |record|
|
63
63
|
# but first, check for the authorised attributes, if exists
|
64
|
-
|
64
|
+
whitelisted_attributes = Helpers.whitelisted_attributes(record, current_user)
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
|
66
|
+
if whitelisted_attributes.size > 0
|
67
|
+
message = { 'action' => 'create', 'attributes' => record.attributes }
|
68
|
+
response = filtered_message(message, whitelisted_attributes)
|
69
|
+
transmit response if response.present?
|
70
|
+
end
|
69
71
|
end
|
70
72
|
else
|
71
73
|
respond_with_error(:bad_request, 'Not a correct model name')
|
@@ -28,12 +28,12 @@ class LiveRecord::PublicationsChannel
|
|
28
28
|
current_user = args.fetch(:current_user)
|
29
29
|
|
30
30
|
current_active_record_relation = model_class.all
|
31
|
-
queryable_attributes =
|
31
|
+
queryable_attributes = LiveRecord::BaseChannel::Helpers.queryable_attributes(model_class, current_user)
|
32
32
|
|
33
33
|
conditions_hash.each do |key, value|
|
34
34
|
operator = key.split('_').last
|
35
35
|
# to get attribute_name, we subtract the end part of the string with size of operator substring; i.e.: created_at_lteq -> created_at
|
36
|
-
attribute_name = key[0..(-1 - operator.size - 1)]
|
36
|
+
attribute_name = key[0..(-1 - operator.size - 1)]
|
37
37
|
|
38
38
|
if queryable_attributes.include?(attribute_name)
|
39
39
|
case operator
|
@@ -20,7 +20,7 @@ class <%= class_name %> < <%= parent_class_name.classify %>
|
|
20
20
|
# then only these whitelisted attributes will be sent to this current_user client
|
21
21
|
# Empty array means unauthorized
|
22
22
|
# Example:
|
23
|
-
# [:email, :name, :is_admin, :group_id, :created_at, :updated_at]
|
23
|
+
# [:id, :email, :name, :is_admin, :group_id, :created_at, :updated_at]
|
24
24
|
[]
|
25
25
|
end
|
26
26
|
|
@@ -36,7 +36,7 @@ class <%= class_name %> < <%= parent_class_name.classify %>
|
|
36
36
|
# if you're using `ransack` gem, use `ransackable_attributes`
|
37
37
|
# Empty array means unauthorized
|
38
38
|
# Example:
|
39
|
-
# [:email, :name, :is_admin, :group_id, :created_at, :updated_at]
|
39
|
+
# [:id, :email, :name, :is_admin, :group_id, :created_at, :updated_at]
|
40
40
|
[]
|
41
41
|
end
|
42
42
|
end
|
data/lib/live_record/version.rb
CHANGED
@@ -5,29 +5,10 @@ class Post < ApplicationRecord
|
|
5
5
|
has_many :live_record_updates, as: :recordable, dependent: :destroy
|
6
6
|
|
7
7
|
def self.live_record_whitelisted_attributes(post, current_user)
|
8
|
-
|
9
|
-
# Defaults to empty array, thereby blocking everything by default, only unless explicitly stated here so.
|
10
|
-
# i.e. if this file is a User model, and that a User has been created/updated in the backend,
|
11
|
-
# then only these whitelisted attributes will be sent to this current_user client
|
12
|
-
# Empty array means unauthorized
|
13
|
-
# Example:
|
14
|
-
# [:email, :name, :is_admin, :group_id, :created_at, :updated_at]
|
15
|
-
[:title, :is_enabled, :user_id, :created_at, :updated_at]
|
8
|
+
[:id, :title, :is_enabled, :user_id, :created_at, :updated_at]
|
16
9
|
end
|
17
10
|
|
18
11
|
def self.live_record_queryable_attributes(current_user)
|
19
|
-
|
20
|
-
# If you're using ransack gem, instead of this method, use one or more of the ransack methods:
|
21
|
-
# see https://github.com/activerecord-hackery/ransack#authorization-whitelistingblacklisting
|
22
|
-
#
|
23
|
-
# Add attributes to this array that you would like current_user client to be able to query upon when "subscribing"
|
24
|
-
# Defaults to empty array, thereby blocking everything by default, only unless explicitly stated here so.
|
25
|
-
# i.e. if a current_user client subscribes to "new records creation" using `.subscribe({where: {...}})`,
|
26
|
-
# then only these attributes will be considered in the "{where: ...}" argument
|
27
|
-
# if you're using `ransack` gem, use `ransackable_attributes`
|
28
|
-
# Empty array means unauthorized
|
29
|
-
# Example:
|
30
|
-
# [:email, :name, :is_admin, :group_id, :created_at, :updated_at]
|
31
|
-
[:title, :is_enabled, :user_id, :created_at, :updated_at]
|
12
|
+
[:id, :title, :is_enabled, :user_id, :created_at, :updated_at]
|
32
13
|
end
|
33
14
|
end
|
@@ -5,9 +5,7 @@ class User < ApplicationRecord
|
|
5
5
|
has_many :posts
|
6
6
|
|
7
7
|
def self.live_record_whitelisted_attributes(user, current_user)
|
8
|
-
|
9
|
-
# Defaults to empty array, thereby blocking everything by default, only unless explicitly stated here so.
|
10
|
-
[:email, :created_at, :updated_at]
|
8
|
+
[:id, :email, :created_at, :updated_at]
|
11
9
|
end
|
12
10
|
|
13
11
|
def self.live_record_queryable_attributes(current_user)
|