active_record_api-rest 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/active_record_api/rest/access_denied_exception.rb +13 -12
- data/lib/active_record_api/rest/application_policy.rb +36 -8
- data/lib/active_record_api/rest/bad_session_exception.rb +6 -0
- data/lib/active_record_api/rest/controller.rb +11 -5
- data/lib/active_record_api/rest/graceful_errors.rb +7 -3
- data/lib/active_record_api/rest/index_controller.rb +1 -1
- data/lib/active_record_api/rest/spec/rest_controller_shared_example.rb +62 -66
- data/lib/active_record_api/rest/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: d6c7bfa36f4f4e1cf3a3013003dc091864a44e6a2f0df9c47886ce37b7c4a88f
|
4
|
+
data.tar.gz: 5929dfad290e2789a5acbfa574cde379c409d4de125679966c0ed5e98f230edf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a524c311c0cd8ecda10d96d82a9900b68853e108c96d59109e5493dce0052a6ee31348807a2f423a0234ce011bbf272eccf3d39ca2063e095ca585ef951033b1
|
7
|
+
data.tar.gz: b3c7af25eb3864129781d0cdc564c33a65ba14da22001601b5450a517267466fa865e1dd63b0b48d6d26b89137c61b9f227c8a06ddbfe7675ba50abe5ce9b719
|
data/Gemfile.lock
CHANGED
@@ -1,12 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
1
|
+
module ActiveRecordApi
|
2
|
+
module Rest
|
3
|
+
class AccessDeniedException < Exception
|
4
|
+
attr_reader :action
|
5
|
+
attr_reader :controller
|
6
|
+
def initialize(controller, action, message)
|
7
|
+
super(message)
|
8
|
+
@action = action
|
9
|
+
@controller = controller
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -1,32 +1,60 @@
|
|
1
1
|
module ActiveRecordApi
|
2
2
|
module Rest
|
3
3
|
class ApplicationPolicy
|
4
|
+
class Scope
|
5
|
+
attr_reader :user, :scope
|
6
|
+
|
7
|
+
def initialize(user, scope)
|
8
|
+
@user = user
|
9
|
+
@scope = scope
|
10
|
+
end
|
11
|
+
|
12
|
+
def resolve
|
13
|
+
@scope.where(id: false)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
4
17
|
attr_reader :user
|
5
|
-
attr_reader :
|
18
|
+
attr_reader :model_class
|
19
|
+
|
20
|
+
READ = 'read'.freeze
|
21
|
+
MANAGE = 'manage'.freeze
|
6
22
|
|
7
|
-
def initialize(user,
|
23
|
+
def initialize(user, model_class)
|
8
24
|
@user = user
|
9
|
-
@
|
25
|
+
@model_class = model_class
|
10
26
|
end
|
11
27
|
|
12
28
|
def index?
|
13
|
-
|
29
|
+
can?(READ)
|
14
30
|
end
|
15
31
|
|
16
32
|
def show?
|
17
|
-
|
33
|
+
can?(READ)
|
18
34
|
end
|
19
35
|
|
20
36
|
def create?
|
21
|
-
|
37
|
+
can?(MANAGE)
|
22
38
|
end
|
23
39
|
|
24
40
|
def update?
|
25
|
-
|
41
|
+
can?(MANAGE)
|
26
42
|
end
|
27
43
|
|
28
44
|
def destroy?
|
29
|
-
|
45
|
+
can?(MANAGE)
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def can?(action)
|
51
|
+
user.full_permissions.include?("#{service_name}__#{@model_class.name.downcase}:#{action}")
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def service_name
|
57
|
+
File.basename(Rails.root.to_s)
|
30
58
|
end
|
31
59
|
end
|
32
60
|
end
|
@@ -60,7 +60,7 @@ module ActiveRecordApi
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def load_model
|
63
|
-
@model ||= model_klass.find(params[:id])
|
63
|
+
@model ||= scope_filter(model_klass).find(params[:id])
|
64
64
|
end
|
65
65
|
|
66
66
|
def initialize_model
|
@@ -68,10 +68,16 @@ module ActiveRecordApi
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def authorize
|
71
|
-
|
72
|
-
raise
|
73
|
-
|
74
|
-
|
71
|
+
raise BadSessionException.new(controller_name, action_name) if current_user.nil?
|
72
|
+
raise AccessDeniedException.new(controller_name, action_name, 'Insufficient permissions') unless policy.send("#{action_name}?")
|
73
|
+
end
|
74
|
+
|
75
|
+
def scope_filter(scope)
|
76
|
+
"#{policy.class.name}::Scope".constantize.new(current_user, scope).resolve
|
77
|
+
end
|
78
|
+
|
79
|
+
def policy
|
80
|
+
@policy ||= ("#{controller_name.classify}Policy".safe_constantize) ? "#{controller_name.classify}Policy".constantize.new(current_user, model_klass) : ApplicationPolicy.new(current_user, model_klass)
|
75
81
|
end
|
76
82
|
end
|
77
83
|
end
|
@@ -8,9 +8,13 @@ module ActiveRecordApi
|
|
8
8
|
render status: :not_found, json: { base: exception.message }
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
rescue_from BadSessionException do |exception|
|
12
|
+
render status: :unauthorized, json: { base: "No user for session on #{exception.action} #{exception.controller}" }
|
13
|
+
end
|
14
|
+
|
15
|
+
rescue_from AccessDeniedException do |exception|
|
16
|
+
render status: :forbidden, json: { base: "Access denied on #{exception.action} #{exception.controller}", message: exception.message }
|
17
|
+
end
|
14
18
|
end
|
15
19
|
end
|
16
20
|
end
|
@@ -1,15 +1,14 @@
|
|
1
1
|
shared_examples 'all::rest::actions' do
|
2
|
-
let(:authorize_read) { mock_valid_auth(permission_name(model_klass, '
|
3
|
-
let(:unauthorize_read) { mock_valid_auth(permission_name(model_klass, '')
|
4
|
-
let(:authorize_create) { mock_valid_auth(permission_name(model_klass, '
|
5
|
-
let(:unauthorize_create) { mock_valid_auth(permission_name(model_klass, '')
|
6
|
-
let(:authorize_update) { mock_valid_auth(permission_name(model_klass, '
|
7
|
-
let(:unauthorize_update) { mock_valid_auth(permission_name(model_klass, '')
|
8
|
-
let(:authorize_delete) { mock_valid_auth(permission_name(model_klass, '
|
9
|
-
let(:unauthorize_delete) { mock_valid_auth(permission_name(model_klass, '')
|
2
|
+
let(:authorize_read) { mock_valid_auth([permission_name(model_klass, 'read')]) }
|
3
|
+
let(:unauthorize_read) { mock_valid_auth([permission_name(model_klass, '')]) }
|
4
|
+
let(:authorize_create) { mock_valid_auth([permission_name(model_klass, 'manage')]) }
|
5
|
+
let(:unauthorize_create) { mock_valid_auth([permission_name(model_klass, '')]) }
|
6
|
+
let(:authorize_update) { mock_valid_auth([permission_name(model_klass, 'manage')]) }
|
7
|
+
let(:unauthorize_update) { mock_valid_auth([permission_name(model_klass, '')]) }
|
8
|
+
let(:authorize_delete) { mock_valid_auth([permission_name(model_klass, 'manage')]) }
|
9
|
+
let(:unauthorize_delete) { mock_valid_auth([permission_name(model_klass, '')]) }
|
10
10
|
let(:factory_symbol) { model_klass.to_s.underscore.to_sym }
|
11
|
-
let(:
|
12
|
-
let(:model_array) { [model] + create_list(factory_symbol, 4, organization_id: model.organization_id) }
|
11
|
+
let(:model_array) { [model] + create_list(factory_symbol, 4) }
|
13
12
|
let(:base_model_klass) do
|
14
13
|
if model_klass.superclass == ApplicationRecord
|
15
14
|
model_klass
|
@@ -59,14 +58,21 @@ shared_examples 'get::show' do
|
|
59
58
|
it { expect(JSON.parse(response.body)['base']).to include "Couldn't find #{base_model_klass} with 'id'=-1" }
|
60
59
|
end
|
61
60
|
end
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
61
|
+
context 'when not authorized' do
|
62
|
+
before(:each) do
|
63
|
+
unauthorize_read
|
64
|
+
get :show, params: { id: model.to_param }
|
65
|
+
end
|
66
|
+
it { expect(response.status).to eq 403 }
|
67
|
+
it { expect(JSON.parse(response.body)['base']).to include 'Access denied on show' }
|
68
|
+
end
|
69
|
+
context 'when bad session' do
|
70
|
+
before(:each) do
|
71
|
+
get :show, params: { id: model.to_param }
|
72
|
+
end
|
73
|
+
it { expect(response.status).to eq 401 }
|
74
|
+
it { expect(JSON.parse(response.body)['base']).to include 'No user for session on show' }
|
75
|
+
end
|
70
76
|
end
|
71
77
|
end
|
72
78
|
|
@@ -98,27 +104,17 @@ shared_examples 'get::index' do
|
|
98
104
|
it { expect(response.headers['x-link-next']).to eq "#{host}#{request.path}?limit=1&previous_id=#{next_previous_id}" }
|
99
105
|
it { expect(response.body).to be_json_eql model_klass.where('id > ?', previous_id).limit(1).map { |o| serializer.new(o) }.to_json }
|
100
106
|
end
|
101
|
-
|
102
|
-
# context 'when only access some of the data' do
|
103
|
-
# before(:each) do
|
104
|
-
# index_non_accessible_data
|
105
|
-
# get :index
|
106
|
-
# end
|
107
|
-
# it { expect(response.status).to eq 200 }
|
108
|
-
# it { expect(response.headers['x-total']).to eq 1 }
|
109
|
-
# it { expect(response.body).to be_json_eql [serializer.new(model)].to_json }
|
110
|
-
# end
|
111
107
|
end
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
108
|
+
context 'when not authorized' do
|
109
|
+
before(:each) do
|
110
|
+
unauthorize_read
|
111
|
+
end
|
112
|
+
before(:each) do
|
113
|
+
get :index
|
114
|
+
end
|
115
|
+
it { expect(response.status).to eq 403 }
|
116
|
+
it { expect(JSON.parse(response.body)['base']).to include 'Access denied on index' }
|
117
|
+
end
|
122
118
|
end
|
123
119
|
end
|
124
120
|
|
@@ -154,14 +150,14 @@ shared_examples 'put::update' do
|
|
154
150
|
it { expect(response.body).to be_json_eql({ base: 'Extra parameters are not allow: foobars' }.to_json) }
|
155
151
|
end
|
156
152
|
end
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
153
|
+
context 'when not authorized' do
|
154
|
+
before(:each) do
|
155
|
+
unauthorize_update
|
156
|
+
get :show, params: new_attributes
|
157
|
+
end
|
158
|
+
it { expect(response.status).to eq 403 }
|
159
|
+
it { expect(JSON.parse(response.body)['base']).to include 'Access denied on show' }
|
160
|
+
end
|
165
161
|
end
|
166
162
|
end
|
167
163
|
|
@@ -192,14 +188,14 @@ shared_examples 'post::create' do
|
|
192
188
|
end
|
193
189
|
end
|
194
190
|
end
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
191
|
+
context 'when not authorized' do
|
192
|
+
before(:each) do
|
193
|
+
unauthorize_create
|
194
|
+
post :create, params: new_attributes
|
195
|
+
end
|
196
|
+
it { expect(response.status).to eq 403 }
|
197
|
+
it { expect(JSON.parse(response.body)['base']).to include 'Access denied on create' }
|
198
|
+
end
|
203
199
|
end
|
204
200
|
end
|
205
201
|
|
@@ -210,19 +206,19 @@ shared_examples 'delete::delete' do
|
|
210
206
|
authorize_delete
|
211
207
|
end
|
212
208
|
it 'remove record' do
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
209
|
+
model
|
210
|
+
before_count = model_klass.count
|
211
|
+
delete :destroy, params: { id: model.id }
|
212
|
+
expect(model_klass.count).to eq(before_count - 1)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
context 'when not authorized' do
|
216
|
+
before(:each) do
|
217
|
+
unauthorize_delete
|
218
|
+
delete :destroy, params: { id: model.id }
|
217
219
|
end
|
220
|
+
it { expect(response.status).to eq 403 }
|
221
|
+
it { expect(JSON.parse(response.body)['base']).to include 'Access denied on destroy' }
|
218
222
|
end
|
219
|
-
# context 'when not authorized' do
|
220
|
-
# before(:each) do
|
221
|
-
# unauthorize_delete
|
222
|
-
# delete :destroy, params: { id: model.id }
|
223
|
-
# end
|
224
|
-
# it { expect(response.status).to eq 403 }
|
225
|
-
# it { expect(JSON.parse(response.body)['base']).to include 'Access denied on destroy' }
|
226
|
-
# end
|
227
223
|
end
|
228
224
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record_api-rest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Full Measure Education
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-03-
|
11
|
+
date: 2019-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|