easy-admin-rails 0.2.0 → 0.2.2
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 +4 -4
- data/app/components/easy_admin/permissions/user_role_permissions_component.rb +25 -16
- data/app/components/easy_admin/sidebar_component.rb +12 -7
- data/app/controllers/easy_admin/application_controller.rb +17 -11
- data/app/controllers/easy_admin/concerns/resource_authorization.rb +46 -39
- data/lib/easy_admin/permissions/dsl.rb +22 -42
- data/lib/easy_admin/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87d3c4867f51a6c89bd00852824be9345c18dbac643010d91bbbcaed411eb683
|
4
|
+
data.tar.gz: f37721aeeebfc654554f1d544b0f5a649ea4b764f36ae35a52b5993741c42b69
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9aaf3cba5a2b398313e7d916cb6be2d0754a3fff3ec7c8e31ff70c03638186f7656925d30c5f49150c20c5f27040202dc9625ae306d2daed331e089d86817157
|
7
|
+
data.tar.gz: 95e1b5e2579cd2fe1a1aab49a2f92e2c5f2ffce0a2a7bb254c5f52044c161c133415a6df4e242380ca0c584ec760269600a988ec055e316858fa2ddc32469159
|
@@ -13,7 +13,7 @@ module EasyAdmin
|
|
13
13
|
|
14
14
|
# Get actual permissions from permissions_cache
|
15
15
|
@user_permissions = get_user_permissions_from_cache(user)
|
16
|
-
@
|
16
|
+
@available_resources = EasyAdmin::Permissions.available_resources
|
17
17
|
|
18
18
|
Rails.logger.debug "UserRolePermissionsComponent: user=#{@user&.id}, role=#{@current_role&.name}, cached_permissions=#{@user_permissions.size}"
|
19
19
|
end
|
@@ -59,7 +59,8 @@ module EasyAdmin
|
|
59
59
|
|
60
60
|
div do
|
61
61
|
span(class: "font-medium text-gray-500") { "Permissions Count:" }
|
62
|
-
|
62
|
+
enabled_count = @user_permissions.count { |name, granted| granted == "true" || granted == true }
|
63
|
+
p(class: "text-gray-900") { enabled_count.to_s }
|
63
64
|
end
|
64
65
|
|
65
66
|
div do
|
@@ -85,12 +86,11 @@ module EasyAdmin
|
|
85
86
|
end
|
86
87
|
|
87
88
|
# Get permissions that are granted (true) from cache
|
88
|
-
granted_permission_names = @user_permissions.select { |name, granted| granted == "true" }.keys
|
89
|
-
granted_permissions = @all_permissions.select { |p| granted_permission_names.include?(p.name) }
|
89
|
+
granted_permission_names = @user_permissions.select { |name, granted| granted == "true" || granted == true }.keys
|
90
90
|
|
91
|
-
if
|
91
|
+
if granted_permission_names.any?
|
92
92
|
# Group permissions by resource type
|
93
|
-
grouped_permissions =
|
93
|
+
grouped_permissions = group_permissions_by_resource(granted_permission_names)
|
94
94
|
|
95
95
|
div(class: "space-y-6") do
|
96
96
|
grouped_permissions.each do |resource_type, resource_permissions|
|
@@ -115,14 +115,16 @@ module EasyAdmin
|
|
115
115
|
|
116
116
|
# Permissions grid
|
117
117
|
div(class: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3") do
|
118
|
-
permissions.each do |
|
119
|
-
render_permission_card(
|
118
|
+
permissions.each do |permission_name|
|
119
|
+
render_permission_card(permission_name)
|
120
120
|
end
|
121
121
|
end
|
122
122
|
end
|
123
123
|
end
|
124
124
|
|
125
|
-
def render_permission_card(
|
125
|
+
def render_permission_card(permission_name)
|
126
|
+
resource_type, action = permission_name.split(':')
|
127
|
+
|
126
128
|
div(class: "flex items-start p-3 bg-green-50 border border-green-200 rounded-lg") do
|
127
129
|
# Permission icon
|
128
130
|
div(class: "flex-shrink-0 mr-3") do
|
@@ -132,18 +134,14 @@ module EasyAdmin
|
|
132
134
|
# Permission details
|
133
135
|
div(class: "flex-1 min-w-0") do
|
134
136
|
div(class: "flex items-center mb-1") do
|
135
|
-
span(class: "text-sm font-medium text-gray-900 capitalize") {
|
137
|
+
span(class: "text-sm font-medium text-gray-900 capitalize") { action.humanize }
|
136
138
|
span(class: "ml-2 inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-green-100 text-green-800") do
|
137
|
-
|
139
|
+
action
|
138
140
|
end
|
139
141
|
end
|
140
142
|
|
141
|
-
if permission.description.present?
|
142
|
-
p(class: "text-xs text-gray-600 leading-relaxed") { permission.description }
|
143
|
-
end
|
144
|
-
|
145
143
|
# Permission name (technical)
|
146
|
-
p(class: "text-xs text-gray-500 font-mono mt-1") {
|
144
|
+
p(class: "text-xs text-gray-500 font-mono mt-1") { permission_name }
|
147
145
|
end
|
148
146
|
end
|
149
147
|
end
|
@@ -181,6 +179,17 @@ module EasyAdmin
|
|
181
179
|
{}
|
182
180
|
end
|
183
181
|
end
|
182
|
+
|
183
|
+
# Group permission names by resource type
|
184
|
+
def group_permissions_by_resource(permission_names)
|
185
|
+
grouped = {}
|
186
|
+
permission_names.each do |permission_name|
|
187
|
+
resource_type, action = permission_name.split(':')
|
188
|
+
grouped[resource_type] ||= []
|
189
|
+
grouped[resource_type] << permission_name
|
190
|
+
end
|
191
|
+
grouped
|
192
|
+
end
|
184
193
|
end
|
185
194
|
end
|
186
195
|
end
|
@@ -213,13 +213,18 @@ module EasyAdmin
|
|
213
213
|
# Allow non-resource items (like Dashboard, Settings, etc.)
|
214
214
|
return true unless item[:resource]
|
215
215
|
|
216
|
-
# For resource items, check permission
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
216
|
+
# For resource items, check permission only if EasyAdmin::Permissions is available
|
217
|
+
if defined?(EasyAdmin::Permissions) && EasyAdmin::Permissions.enabled?
|
218
|
+
resource_name = item[:resource]
|
219
|
+
# Convert complex resource names to permission format
|
220
|
+
permission_resource_name = convert_resource_to_permission_name(resource_name)
|
221
|
+
permission_name = "#{permission_resource_name}:read"
|
222
|
+
|
223
|
+
EasyAdmin::Permissions.authorized?(@current_user, permission_name)
|
224
|
+
else
|
225
|
+
# If permissions are not available, allow access to all resource items
|
226
|
+
true
|
227
|
+
end
|
223
228
|
end
|
224
229
|
|
225
230
|
def convert_resource_to_permission_name(resource_name)
|
@@ -1,7 +1,17 @@
|
|
1
1
|
module EasyAdmin
|
2
2
|
class ApplicationController < ActionController::Base
|
3
3
|
include Pagy::Backend
|
4
|
-
|
4
|
+
|
5
|
+
# Only include ActionPolicy if it's available
|
6
|
+
if defined?(ActionPolicy)
|
7
|
+
include ActionPolicy::Controller
|
8
|
+
|
9
|
+
# ActionPolicy authorization context
|
10
|
+
authorize :user, through: :current_admin_user
|
11
|
+
|
12
|
+
# Handle ActionPolicy authorization failures
|
13
|
+
rescue_from ActionPolicy::Unauthorized, with: :handle_authorization_failure
|
14
|
+
end
|
5
15
|
|
6
16
|
helper EasyAdmin::FieldsHelper
|
7
17
|
helper EasyAdmin::PagyHelper
|
@@ -11,16 +21,12 @@ module EasyAdmin
|
|
11
21
|
before_action :set_feature_toggles
|
12
22
|
before_action :set_paper_trail_whodunnit
|
13
23
|
|
14
|
-
# ActionPolicy
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
# Configure ActionPolicy to use ApplicationPolicy as default for all models
|
21
|
-
def policy_for(record:, **opts)
|
22
|
-
# Always use ApplicationPolicy for all models (EasyAdmin and regular models)
|
23
|
-
ApplicationPolicy.new(record, **authorization_context, **opts)
|
24
|
+
# Configure ActionPolicy to use ApplicationPolicy as default for all models (only if ActionPolicy is available)
|
25
|
+
if defined?(ActionPolicy)
|
26
|
+
def policy_for(record:, **opts)
|
27
|
+
# Always use ApplicationPolicy for all models (EasyAdmin and regular models)
|
28
|
+
ApplicationPolicy.new(record, **authorization_context, **opts)
|
29
|
+
end
|
24
30
|
end
|
25
31
|
|
26
32
|
protected
|
@@ -2,53 +2,60 @@ module EasyAdmin
|
|
2
2
|
module Concerns
|
3
3
|
# ResourceAuthorization concern handles authorization checks for resource actions
|
4
4
|
# Provides before_action callbacks and authorization methods for all CRUD operations
|
5
|
+
# Only active when ActionPolicy is available
|
5
6
|
module ResourceAuthorization
|
6
7
|
extend ActiveSupport::Concern
|
7
8
|
|
8
9
|
included do
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
:
|
14
|
-
:
|
15
|
-
|
16
|
-
|
17
|
-
|
10
|
+
# Only add authorization callbacks if ActionPolicy is available
|
11
|
+
if defined?(ActionPolicy)
|
12
|
+
before_action :authorize_resource_access!, only: [:index]
|
13
|
+
before_action :authorize_record_access!, only: [:show]
|
14
|
+
before_action :authorize_record_creation!, only: [:new, :create]
|
15
|
+
before_action :authorize_record_update!, only: [
|
16
|
+
:edit, :update, :edit_field, :update_field,
|
17
|
+
:belongs_to_reattach, :belongs_to_edit_attached, :update_belongs_to_attached
|
18
|
+
]
|
19
|
+
before_action :authorize_record_destruction!, only: [:destroy]
|
20
|
+
before_action :authorize_versioning_access!, only: [:versions, :revert_version, :version_diff]
|
21
|
+
end
|
18
22
|
end
|
19
23
|
|
20
24
|
private
|
21
25
|
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
#
|
50
|
-
|
51
|
-
|
26
|
+
# Authorization methods - only defined if ActionPolicy is available
|
27
|
+
if defined?(ActionPolicy)
|
28
|
+
# Authorize access to resource index
|
29
|
+
def authorize_resource_access!
|
30
|
+
authorize! @resource_class.model_class, to: :index?
|
31
|
+
end
|
32
|
+
|
33
|
+
# Authorize access to view a specific record
|
34
|
+
def authorize_record_access!
|
35
|
+
authorize! @record, to: :show?
|
36
|
+
end
|
37
|
+
|
38
|
+
# Authorize creation of new records
|
39
|
+
def authorize_record_creation!
|
40
|
+
authorize! @resource_class.model_class, to: :create?
|
41
|
+
end
|
42
|
+
|
43
|
+
# Authorize updating of existing records
|
44
|
+
def authorize_record_update!
|
45
|
+
authorize! @record, to: :update?
|
46
|
+
end
|
47
|
+
|
48
|
+
# Authorize destruction of existing records
|
49
|
+
def authorize_record_destruction!
|
50
|
+
authorize! @record, to: :destroy?
|
51
|
+
end
|
52
|
+
|
53
|
+
# Authorize access to versioning features (PaperTrail integration)
|
54
|
+
def authorize_versioning_access!
|
55
|
+
# Check if user can view record and has versioning permissions
|
56
|
+
authorize! @record, to: :show?
|
57
|
+
authorize! @record, to: :manage_versions?
|
58
|
+
end
|
52
59
|
end
|
53
60
|
end
|
54
61
|
end
|
@@ -119,51 +119,31 @@ module EasyAdmin
|
|
119
119
|
}
|
120
120
|
end
|
121
121
|
|
122
|
-
# Create
|
122
|
+
# Create roles in the database (simplified - no permissions table)
|
123
123
|
def self.seed_database(data)
|
124
|
-
#
|
125
|
-
|
126
|
-
EasyAdmin::Permissions::Permission
|
127
|
-
EasyAdmin::Permissions::Role
|
128
|
-
EasyAdmin::Permissions::RolePermission
|
129
|
-
rescue NameError => e
|
130
|
-
Rails.logger.info "EasyAdmin::Permissions models not yet loaded: #{e.message}"
|
131
|
-
return
|
132
|
-
end
|
124
|
+
# Skip if tables don't exist yet (during migrations or first setup)
|
125
|
+
return unless ActiveRecord::Base.connection.table_exists?(:easy_admin_roles)
|
133
126
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
127
|
+
# Defer execution until Rails is fully initialized
|
128
|
+
if defined?(Rails) && Rails.application && !Rails.application.initialized?
|
129
|
+
Rails.application.config.after_initialize do
|
130
|
+
# Check if models are available (avoid loading during initialization)
|
131
|
+
begin
|
132
|
+
role_class = "EasyAdmin::Permissions::Role".constantize
|
133
|
+
rescue NameError => e
|
134
|
+
Rails.logger.debug "EasyAdmin::Permissions models not yet loaded: #{e.message}"
|
135
|
+
return
|
142
136
|
end
|
143
|
-
end
|
144
|
-
|
145
|
-
# Create roles and assign permissions
|
146
|
-
data[:roles].each do |role_data|
|
147
|
-
role = EasyAdmin::Permissions::Role.find_or_create_by(slug: role_data[:slug]) do |r|
|
148
|
-
r.name = role_data[:name]
|
149
|
-
r.description = role_data[:description]
|
150
|
-
r.active = role_data[:active]
|
151
|
-
r.metadata = role_data[:metadata]
|
152
|
-
end
|
153
|
-
|
154
|
-
# Clear existing permissions for this role
|
155
|
-
role.role_permissions.destroy_all
|
156
137
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
Rails.logger.warn "Permission '#{permission_name}' not found for role '#{role_data[:name]}'"
|
138
|
+
ActiveRecord::Base.transaction do
|
139
|
+
# Create roles only (permissions are managed via JSON field)
|
140
|
+
data[:roles].each do |role_data|
|
141
|
+
role = role_class.find_or_initialize_by(slug: role_data[:slug])
|
142
|
+
role.assign_attributes(
|
143
|
+
name: role_data[:name],
|
144
|
+
description: role_data[:description],
|
145
|
+
)
|
146
|
+
role.save! if role.changed?
|
167
147
|
end
|
168
148
|
end
|
169
149
|
end
|
@@ -177,4 +157,4 @@ module EasyAdmin
|
|
177
157
|
DSL.seed_database(data)
|
178
158
|
end
|
179
159
|
end
|
180
|
-
end
|
160
|
+
end
|
data/lib/easy_admin/version.rb
CHANGED