avo 2.17.1.pre.3 → 2.17.1.pre.4.issue.1342
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of avo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/app/components/avo/fields/date_time_field/edit_component.html.erb +1 -0
- data/app/components/avo/fields/date_time_field/index_component.html.erb +1 -0
- data/app/components/avo/fields/date_time_field/show_component.html.erb +1 -0
- data/app/components/avo/panel_component.html.erb +1 -1
- data/app/components/avo/views/resource_index_component.html.erb +2 -4
- data/app/controllers/avo/application_controller.rb +9 -17
- data/app/controllers/avo/associations_controller.rb +1 -1
- data/app/javascript/js/controllers/fields/date_field_controller.js +12 -0
- data/lib/avo/concerns/has_fields.rb +25 -0
- data/lib/avo/configuration.rb +2 -0
- data/lib/avo/fields/date_time_field.rb +2 -0
- data/lib/avo/fields/time_field.rb +1 -7
- data/lib/avo/services/authorization_clients/pundit_client.rb +51 -0
- data/lib/avo/services/authorization_service.rb +43 -62
- data/lib/avo/version.rb +1 -1
- data/lib/avo.rb +4 -0
- data/lib/generators/avo/templates/initializer/avo.tt +1 -0
- data/public/avo-assets/avo.base.js +73 -73
- data/public/avo-assets/avo.base.js.map +2 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0cb67ed800f6808830e072dfeaf5acb53a9c8960d7ca1b1fc01a5ac27ab478b4
|
4
|
+
data.tar.gz: 861965614539773f8c6242e211bb7e3449b8393ad2306cf3aa1bcbb030a36212
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5a2d565a8a5839e25f23cd4bdac2308c07a5b89dacadd090b8ddcaf5180b58fa4d7f844ebafbd02c167d60d9f0f5193c6b7cd2790c61f538fc8aadfa50e6f32b
|
7
|
+
data.tar.gz: e1a6e356003227ff73440bd51a8aecdce3f4522be2c4cb0829fbd35a88d9916ffcb860638428f19296d66e054396f8683f759852282c22559b6f76fd415a8d81
|
data/Gemfile.lock
CHANGED
@@ -8,6 +8,7 @@
|
|
8
8
|
date_field_disable_mobile_value: @field.disable_mobile,
|
9
9
|
date_field_time24_hr_value: @field.time_24hr,
|
10
10
|
date_field_timezone_value: @field.timezone,
|
11
|
+
date_field_relative_value: @field.relative,
|
11
12
|
date_field_field_type_value: "dateTime",
|
12
13
|
date_field_picker_options_value: @field.picker_options,
|
13
14
|
} do %>
|
@@ -5,6 +5,7 @@
|
|
5
5
|
date_field_enable_time_value: true,
|
6
6
|
date_field_format_value: @field.format,
|
7
7
|
date_field_timezone_value: @field.timezone,
|
8
|
+
date_field_relative_value: @field.relative,
|
8
9
|
date_field_picker_format_value: @field.picker_format,
|
9
10
|
date_field_field_type_value: "dateTime",
|
10
11
|
} do %>
|
@@ -5,6 +5,7 @@
|
|
5
5
|
date_field_enable_time_value: true,
|
6
6
|
date_field_format_value: @field.format,
|
7
7
|
date_field_timezone_value: @field.timezone,
|
8
|
+
date_field_relative_value: @field.relative,
|
8
9
|
date_field_picker_format_value: @field.picker_format,
|
9
10
|
date_field_field_type_value: "dateTime",
|
10
11
|
} do %>
|
@@ -25,7 +25,7 @@
|
|
25
25
|
<% end %>
|
26
26
|
<% if body? %>
|
27
27
|
<div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:gap-4 w-full">
|
28
|
-
<div class="relative flex-1
|
28
|
+
<div class="relative flex-1 <%= white_panel_classes %> <%= @body_classes %> <% if sidebar? %> w-2/3 overflow-auto <% else %> w-full <% end %>">
|
29
29
|
<%= body %>
|
30
30
|
</div>
|
31
31
|
<% if sidebar? %>
|
@@ -46,10 +46,8 @@
|
|
46
46
|
</div>
|
47
47
|
<% if view_type.to_sym == :table %>
|
48
48
|
<% if @resources.present? %>
|
49
|
-
<div class="w-full
|
50
|
-
|
51
|
-
<%= render(Avo::Index::ResourceTableComponent.new(resources: @resources, resource: @resource, reflection: @reflection, parent_model: @parent_model, parent_resource: @parent_resource, pagy: @pagy, query: @query)) %>
|
52
|
-
</div>
|
49
|
+
<div class="w-full relative flex-1 flex mac-styled-scrollbar overflow-auto mt-0">
|
50
|
+
<%= render(Avo::Index::ResourceTableComponent.new(resources: @resources, resource: @resource, reflection: @reflection, parent_model: @parent_model, parent_resource: @parent_resource, pagy: @pagy, query: @query)) %>
|
53
51
|
</div>
|
54
52
|
<% else %>
|
55
53
|
<%= helpers.empty_state resource_name: @resource.name.downcase.pluralize, related_name: params[:related_name], view_type: view_type, add_background: true %>
|
@@ -1,11 +1,5 @@
|
|
1
1
|
module Avo
|
2
2
|
class ApplicationController < ::ActionController::Base
|
3
|
-
if defined?(Pundit::Authorization)
|
4
|
-
include Pundit::Authorization
|
5
|
-
else
|
6
|
-
include Pundit
|
7
|
-
end
|
8
|
-
|
9
3
|
include Pagy::Backend
|
10
4
|
include Avo::ApplicationHelper
|
11
5
|
include Avo::UrlHelpers
|
@@ -24,7 +18,7 @@ module Avo
|
|
24
18
|
before_action :set_view
|
25
19
|
before_action :set_sidebar_open
|
26
20
|
|
27
|
-
rescue_from
|
21
|
+
rescue_from Avo::NotAuthorizedError, with: :render_unauthorized
|
28
22
|
rescue_from ActiveRecord::RecordInvalid, with: :exception_logger
|
29
23
|
|
30
24
|
helper_method :_current_user, :resources_path, :resource_path, :new_resource_path, :edit_resource_path, :resource_attach_path, :resource_detach_path, :related_resources_path, :turbo_frame_request?, :resource_view_path
|
@@ -257,18 +251,16 @@ module Avo
|
|
257
251
|
instance_eval(&Avo.configuration.authenticate)
|
258
252
|
end
|
259
253
|
|
260
|
-
def render_unauthorized(
|
261
|
-
|
262
|
-
flash.now[:notice] = t "avo.not_authorized"
|
263
|
-
|
264
|
-
redirect_url = if request.referrer.blank? || (request.referrer == request.url)
|
265
|
-
root_url
|
266
|
-
else
|
267
|
-
request.referrer
|
268
|
-
end
|
254
|
+
def render_unauthorized(_exception)
|
255
|
+
flash.now[:notice] = t "avo.not_authorized"
|
269
256
|
|
270
|
-
|
257
|
+
redirect_url = if request.referrer.blank? || (request.referrer == request.url)
|
258
|
+
root_url
|
259
|
+
else
|
260
|
+
request.referrer
|
271
261
|
end
|
262
|
+
|
263
|
+
redirect_to(redirect_url)
|
272
264
|
end
|
273
265
|
|
274
266
|
def set_authorization
|
@@ -157,7 +157,7 @@ module Avo
|
|
157
157
|
private
|
158
158
|
|
159
159
|
def set_related_authorization
|
160
|
-
@
|
160
|
+
@related_authorization = if related_resource
|
161
161
|
related_resource.authorization(user: _current_user)
|
162
162
|
else
|
163
163
|
Services::AuthorizationService.new _current_user
|
@@ -60,6 +60,18 @@ export default class extends Controller {
|
|
60
60
|
return this.viewValue === 'show'
|
61
61
|
}
|
62
62
|
|
63
|
+
get fieldIsDate() {
|
64
|
+
return this.fieldTypeValue === 'date'
|
65
|
+
}
|
66
|
+
|
67
|
+
get fieldIsDateTime() {
|
68
|
+
return this.fieldTypeValue === 'dateTime'
|
69
|
+
}
|
70
|
+
|
71
|
+
get fieldIsTime() {
|
72
|
+
return this.fieldTypeValue === 'time'
|
73
|
+
}
|
74
|
+
|
63
75
|
// Parse the time as if it were UTC
|
64
76
|
get parsedValue() {
|
65
77
|
return DateTime.fromISO(this.initialValue, { zone: 'UTC' })
|
@@ -10,6 +10,8 @@ module Avo
|
|
10
10
|
class_attribute :tabs_tabs_holder
|
11
11
|
class_attribute :raw_tabs
|
12
12
|
class_attribute :tools_holder
|
13
|
+
|
14
|
+
class_attribute :backup_items_holder
|
13
15
|
end
|
14
16
|
|
15
17
|
class_methods do
|
@@ -103,6 +105,25 @@ module Avo
|
|
103
105
|
end
|
104
106
|
end
|
105
107
|
|
108
|
+
def with_temporary_items(&block)
|
109
|
+
# back-up the previous items
|
110
|
+
self.backup_items_holder = items_holder
|
111
|
+
|
112
|
+
self.items_holder = Avo::ItemsHolder.new
|
113
|
+
|
114
|
+
instance_eval(&block)
|
115
|
+
end
|
116
|
+
|
117
|
+
def restore_items_from_backup
|
118
|
+
self.items_holder = backup_items_holder if backup_items_holder.present?
|
119
|
+
end
|
120
|
+
|
121
|
+
def with_new_items(&block)
|
122
|
+
self.items_holder = Avo::ItemsHolder.new
|
123
|
+
|
124
|
+
instance_eval(&block)
|
125
|
+
end
|
126
|
+
|
106
127
|
private
|
107
128
|
|
108
129
|
def extract_fields_from_items(thing)
|
@@ -311,6 +332,10 @@ module Avo
|
|
311
332
|
[main_panel, *panelfull_items]
|
312
333
|
end
|
313
334
|
|
335
|
+
def with_new_items(&block)
|
336
|
+
self.class.with_new_items(&block)
|
337
|
+
end
|
338
|
+
|
314
339
|
private
|
315
340
|
|
316
341
|
def check_license
|
data/lib/avo/configuration.rb
CHANGED
@@ -37,6 +37,7 @@ module Avo
|
|
37
37
|
attr_accessor :model_resource_mapping
|
38
38
|
attr_accessor :tabs_style
|
39
39
|
attr_accessor :resource_default_view
|
40
|
+
attr_accessor :authorization_client
|
40
41
|
attr_writer :branding
|
41
42
|
|
42
43
|
def initialize
|
@@ -85,6 +86,7 @@ module Avo
|
|
85
86
|
@model_resource_mapping = {}
|
86
87
|
@tabs_style = :tabs
|
87
88
|
@resource_default_view = :show
|
89
|
+
@authorization_client = nil
|
88
90
|
end
|
89
91
|
|
90
92
|
def current_user_method(&block)
|
@@ -5,6 +5,7 @@ module Avo
|
|
5
5
|
attr_reader :picker_format
|
6
6
|
attr_reader :time_24hr
|
7
7
|
attr_reader :timezone
|
8
|
+
attr_reader :relative
|
8
9
|
|
9
10
|
def initialize(id, **args, &block)
|
10
11
|
super(id, **args, &block)
|
@@ -13,6 +14,7 @@ module Avo
|
|
13
14
|
add_string_prop args, :picker_format, "Y-m-d H:i:S"
|
14
15
|
add_string_prop args, :format, "yyyy-LL-dd TT"
|
15
16
|
add_string_prop args, :timezone
|
17
|
+
add_boolean_prop args, :relative, true
|
16
18
|
end
|
17
19
|
|
18
20
|
def formatted_value
|
@@ -1,20 +1,14 @@
|
|
1
1
|
module Avo
|
2
2
|
module Fields
|
3
|
-
class TimeField <
|
3
|
+
class TimeField < DateTimeField
|
4
4
|
attr_reader :format
|
5
5
|
attr_reader :picker_format
|
6
|
-
attr_reader :time_24hr
|
7
|
-
attr_reader :timezone
|
8
|
-
attr_reader :relative
|
9
6
|
|
10
7
|
def initialize(id, **args, &block)
|
11
8
|
super(id, **args, &block)
|
12
9
|
|
13
|
-
add_boolean_prop args, :time_24hr
|
14
10
|
add_string_prop args, :picker_format, "H:i:S"
|
15
11
|
add_string_prop args, :format, "TT"
|
16
|
-
add_string_prop args, :timezone
|
17
|
-
add_boolean_prop args, :relative, true
|
18
12
|
end
|
19
13
|
|
20
14
|
def formatted_value
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Avo
|
2
|
+
module Services
|
3
|
+
module AuthorizationClients
|
4
|
+
class PunditClient
|
5
|
+
def authorize(user, record, action, policy_class: nil)
|
6
|
+
Pundit.authorize(user, record, action, policy_class: policy_class)
|
7
|
+
rescue Pundit::NotDefinedError
|
8
|
+
raise NoPolicyError
|
9
|
+
rescue Pundit::NotAuthorizedError
|
10
|
+
raise NotAuthorizedError
|
11
|
+
end
|
12
|
+
|
13
|
+
def policy(user, record)
|
14
|
+
Pundit.policy(user, record)
|
15
|
+
end
|
16
|
+
|
17
|
+
def policy!(user, record)
|
18
|
+
Pundit.policy!(user, record)
|
19
|
+
rescue Pundit::NotDefinedError
|
20
|
+
raise NoPolicyError
|
21
|
+
end
|
22
|
+
|
23
|
+
def apply_policy(user, model, policy_class: nil)
|
24
|
+
# Try and figure out the scope from a given policy or auto-detected one
|
25
|
+
scope_from_policy_class = scope_for_policy_class(policy_class)
|
26
|
+
|
27
|
+
# If we discover one use it.
|
28
|
+
# Else fallback to pundit.
|
29
|
+
if scope_from_policy_class.present?
|
30
|
+
scope_from_policy_class.new(user, model).resolve
|
31
|
+
else
|
32
|
+
Pundit.policy_scope!(user, model)
|
33
|
+
end
|
34
|
+
rescue Pundit::NotDefinedError => error
|
35
|
+
raise NoPolicyError
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Fetches the scope for a given policy
|
41
|
+
def scope_for_policy_class(policy_class = nil)
|
42
|
+
return if policy_class.blank?
|
43
|
+
|
44
|
+
if policy_class.present? && defined?(policy_class::Scope)
|
45
|
+
policy_class::Scope
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -3,29 +3,30 @@ module Avo
|
|
3
3
|
class AuthorizationService
|
4
4
|
attr_accessor :user
|
5
5
|
attr_accessor :record
|
6
|
+
attr_accessor :policy_class
|
6
7
|
|
7
8
|
class << self
|
9
|
+
def client
|
10
|
+
(configuration_client || default_client).new
|
11
|
+
end
|
12
|
+
|
8
13
|
def authorize(user, record, action, policy_class: nil, **args)
|
9
14
|
return true if skip_authorization
|
10
15
|
return true if user.nil?
|
11
16
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
false
|
26
|
-
else
|
27
|
-
raise error
|
28
|
-
end
|
17
|
+
client.authorize user, record, action, policy_class: policy_class
|
18
|
+
|
19
|
+
true
|
20
|
+
rescue NoPolicyError => error
|
21
|
+
# By default, Avo allows anything if you don't have a policy present.
|
22
|
+
return true unless Avo.configuration.raise_error_on_missing_policy
|
23
|
+
|
24
|
+
raise error
|
25
|
+
rescue => error
|
26
|
+
if args[:raise_exception] == false
|
27
|
+
false
|
28
|
+
else
|
29
|
+
raise error
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
@@ -35,7 +36,7 @@ module Avo
|
|
35
36
|
# If no action passed we should raise error if the user wants that.
|
36
37
|
# If not, just allow it.
|
37
38
|
if action.nil?
|
38
|
-
raise
|
39
|
+
raise NoPolicyError.new "Policy method is missing" if Avo.configuration.raise_error_on_missing_policy
|
39
40
|
|
40
41
|
return true
|
41
42
|
end
|
@@ -48,44 +49,27 @@ module Avo
|
|
48
49
|
def apply_policy(user, model, policy_class: nil)
|
49
50
|
return model if skip_authorization || user.nil?
|
50
51
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
# Else fallback to pundit.
|
57
|
-
if scope_from_policy_class.present?
|
58
|
-
scope_from_policy_class.new(user, model).resolve
|
59
|
-
else
|
60
|
-
Pundit.policy_scope!(user, model)
|
61
|
-
end
|
62
|
-
rescue Pundit::NotDefinedError => e
|
63
|
-
return model unless Avo.configuration.raise_error_on_missing_policy
|
64
|
-
|
65
|
-
raise e
|
66
|
-
end
|
52
|
+
client.apply_policy(user, model, policy_class: policy_class)
|
53
|
+
rescue NoPolicyError => error
|
54
|
+
return model unless Avo.configuration.raise_error_on_missing_policy
|
55
|
+
|
56
|
+
raise error
|
67
57
|
end
|
68
58
|
|
69
59
|
def skip_authorization
|
70
60
|
Avo::App.license.lacks_with_trial :authorization
|
71
61
|
end
|
72
62
|
|
73
|
-
def authorized_methods(user, record)
|
74
|
-
[:new, :edit, :update, :show, :destroy].map do |method|
|
75
|
-
[method, authorize(user, record, Avo.configuration.authorization_methods[method])]
|
76
|
-
end.to_h
|
77
|
-
end
|
78
|
-
|
79
63
|
def defined_methods(user, record, policy_class: nil, **args)
|
80
|
-
return
|
64
|
+
return client.policy!(user, record).methods if policy_class.nil?
|
81
65
|
|
82
66
|
# I'm aware this will not raise a Pundit error.
|
83
67
|
# Should the policy not exist, it will however raise an uninitialized constant error, which is probably what we want when specifying a custom policy
|
84
68
|
policy_class.new(user, record).methods
|
85
|
-
rescue
|
69
|
+
rescue NoPolicyError => error
|
86
70
|
return [] unless Avo.configuration.raise_error_on_missing_policy
|
87
71
|
|
88
|
-
raise
|
72
|
+
raise error
|
89
73
|
rescue => error
|
90
74
|
if args[:raise_exception] == false
|
91
75
|
[]
|
@@ -94,24 +78,27 @@ module Avo
|
|
94
78
|
end
|
95
79
|
end
|
96
80
|
|
97
|
-
|
98
|
-
|
99
|
-
return if policy_class.blank?
|
81
|
+
def configuration_client
|
82
|
+
client = Avo.configuration.authorization_client
|
100
83
|
|
101
|
-
if
|
102
|
-
|
84
|
+
return if client.blank?
|
85
|
+
|
86
|
+
if client.is_a?(String)
|
87
|
+
client.safe_constantize
|
88
|
+
else
|
89
|
+
client
|
103
90
|
end
|
104
91
|
end
|
92
|
+
|
93
|
+
def default_client
|
94
|
+
Avo::Services::AuthorizationClients::PunditClient
|
95
|
+
end
|
105
96
|
end
|
106
97
|
|
107
98
|
def initialize(user = nil, record = nil, policy_class: nil)
|
108
99
|
@user = user
|
109
100
|
@record = record
|
110
|
-
@policy_class = policy_class ||
|
111
|
-
end
|
112
|
-
|
113
|
-
def authorize(action, **args)
|
114
|
-
self.class.authorize(user, record, action, policy_class: @policy_class, **args)
|
101
|
+
@policy_class = policy_class || self.class.client.policy(user, record)&.class
|
115
102
|
end
|
116
103
|
|
117
104
|
def set_record(record)
|
@@ -120,22 +107,16 @@ module Avo
|
|
120
107
|
self
|
121
108
|
end
|
122
109
|
|
123
|
-
def set_user(user)
|
124
|
-
@user = user
|
125
|
-
|
126
|
-
self
|
127
|
-
end
|
128
|
-
|
129
110
|
def authorize_action(action, **args)
|
130
|
-
self.class.authorize_action(user, record, action, policy_class:
|
111
|
+
self.class.authorize_action(user, record, action, policy_class: policy_class, **args)
|
131
112
|
end
|
132
113
|
|
133
114
|
def apply_policy(model)
|
134
|
-
self.class.apply_policy(user, model, policy_class:
|
115
|
+
self.class.apply_policy(user, model, policy_class: policy_class)
|
135
116
|
end
|
136
117
|
|
137
118
|
def defined_methods(model, **args)
|
138
|
-
self.class.defined_methods(user, model, policy_class:
|
119
|
+
self.class.defined_methods(user, model, policy_class: policy_class, **args)
|
139
120
|
end
|
140
121
|
|
141
122
|
def has_method?(method, **args)
|
data/lib/avo/version.rb
CHANGED
data/lib/avo.rb
CHANGED
@@ -44,6 +44,10 @@ module Avo
|
|
44
44
|
class LicenseVerificationTemperedError < StandardError; end
|
45
45
|
|
46
46
|
class LicenseInvalidError < StandardError; end
|
47
|
+
|
48
|
+
class NotAuthorizedError < StandardError; end
|
49
|
+
|
50
|
+
class NoPolicyError < StandardError; end
|
47
51
|
end
|
48
52
|
|
49
53
|
loader.eager_load
|