easy-admin-rails 0.2.5 → 0.2.7

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.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/builds/easy_admin.base.js +95 -0
  3. data/app/assets/builds/easy_admin.base.js.map +3 -3
  4. data/app/assets/builds/easy_admin.css +226 -0
  5. data/app/components/easy_admin/fields/form/belongs_to_component.rb +0 -1
  6. data/app/components/easy_admin/form_layout_component.rb +553 -0
  7. data/app/components/easy_admin/navbar_component.rb +19 -4
  8. data/app/components/easy_admin/permissions/user_role_permissions_component.rb +1 -3
  9. data/app/components/easy_admin/profile/change_password_modal_component.rb +75 -0
  10. data/app/components/easy_admin/profile/profile_tab_component.rb +92 -0
  11. data/app/components/easy_admin/profile/security_tab_component.rb +53 -0
  12. data/app/components/easy_admin/profile/settings_component.rb +103 -0
  13. data/app/components/easy_admin/show_layout_component.rb +694 -24
  14. data/app/components/easy_admin/two_factor/backup_codes_component.rb +118 -0
  15. data/app/components/easy_admin/two_factor/setup_component.rb +124 -0
  16. data/app/components/easy_admin/two_factor/status_component.rb +92 -0
  17. data/app/controllers/concerns/easy_admin/two_factor.rb +110 -0
  18. data/app/controllers/easy_admin/application_controller.rb +10 -5
  19. data/app/controllers/easy_admin/batch_actions_controller.rb +0 -1
  20. data/app/controllers/easy_admin/concerns/inline_field_editing.rb +4 -11
  21. data/app/controllers/easy_admin/concerns/resource_loading.rb +10 -9
  22. data/app/controllers/easy_admin/concerns/resource_pagination.rb +3 -0
  23. data/app/controllers/easy_admin/dashboards_controller.rb +0 -1
  24. data/app/controllers/easy_admin/profile_controller.rb +25 -0
  25. data/app/controllers/easy_admin/resources_controller.rb +1 -5
  26. data/app/controllers/easy_admin/row_actions_controller.rb +1 -4
  27. data/app/controllers/easy_admin/sessions_controller.rb +107 -1
  28. data/app/helpers/easy_admin/fields_helper.rb +8 -22
  29. data/app/javascript/easy_admin/controllers/infinite_scroll_controller.js +12 -0
  30. data/app/javascript/easy_admin/controllers/vertical_tabs_controller.js +112 -0
  31. data/app/javascript/easy_admin/controllers.js +3 -1
  32. data/app/models/easy_admin/admin_user.rb +3 -0
  33. data/app/views/easy_admin/profile/backup_codes_regenerated.turbo_stream.erb +12 -0
  34. data/app/views/easy_admin/profile/change_password.html.erb +24 -0
  35. data/app/views/easy_admin/profile/index.html.erb +1 -0
  36. data/app/views/easy_admin/profile/password_error.turbo_stream.erb +6 -0
  37. data/app/views/easy_admin/profile/password_invalid_current.turbo_stream.erb +6 -0
  38. data/app/views/easy_admin/profile/password_updated.turbo_stream.erb +9 -0
  39. data/app/views/easy_admin/profile/two_factor_backup_codes.html.erb +24 -0
  40. data/app/views/easy_admin/profile/two_factor_enabled.turbo_stream.erb +12 -0
  41. data/app/views/easy_admin/profile/two_factor_invalid_code.turbo_stream.erb +6 -0
  42. data/app/views/easy_admin/profile/two_factor_not_enabled.turbo_stream.erb +6 -0
  43. data/app/views/easy_admin/profile/two_factor_setup.html.erb +24 -0
  44. data/app/views/easy_admin/profile/two_factor_unavailable.turbo_stream.erb +6 -0
  45. data/app/views/easy_admin/resources/edit.html.erb +2 -2
  46. data/app/views/easy_admin/resources/new.html.erb +2 -2
  47. data/app/views/easy_admin/resources/show.html.erb +3 -1
  48. data/app/views/easy_admin/sessions/two_factor_verification.html.erb +48 -0
  49. data/app/views/easy_admin/sessions/verify_2fa_error.turbo_stream.erb +13 -0
  50. data/config/routes.rb +20 -1
  51. data/lib/easy-admin-rails.rb +1 -0
  52. data/lib/easy_admin/field.rb +3 -2
  53. data/lib/easy_admin/layouts/builders/base_layout_builder.rb +245 -0
  54. data/lib/easy_admin/layouts/builders/form_layout_builder.rb +208 -0
  55. data/lib/easy_admin/layouts/builders/index_layout_builder.rb +22 -0
  56. data/lib/easy_admin/layouts/builders/show_layout_builder.rb +199 -0
  57. data/lib/easy_admin/layouts/dsl.rb +200 -0
  58. data/lib/easy_admin/layouts/layout_context.rb +189 -0
  59. data/lib/easy_admin/layouts/nodes/base_node.rb +88 -0
  60. data/lib/easy_admin/layouts/nodes/divider.rb +27 -0
  61. data/lib/easy_admin/layouts/nodes/field_node.rb +57 -0
  62. data/lib/easy_admin/layouts/nodes/grid.rb +60 -0
  63. data/lib/easy_admin/layouts/nodes/render_node.rb +41 -0
  64. data/lib/easy_admin/layouts/nodes/root.rb +25 -0
  65. data/lib/easy_admin/layouts/nodes/section.rb +46 -0
  66. data/lib/easy_admin/layouts/nodes/spacer.rb +17 -0
  67. data/lib/easy_admin/layouts/nodes/stubs.rb +109 -0
  68. data/lib/easy_admin/layouts/nodes/tab.rb +40 -0
  69. data/lib/easy_admin/layouts/nodes/tabs.rb +40 -0
  70. data/lib/easy_admin/layouts.rb +28 -0
  71. data/lib/easy_admin/permissions/resource_permissions.rb +1 -5
  72. data/lib/easy_admin/resource/base.rb +2 -2
  73. data/lib/easy_admin/resource/dsl.rb +2 -11
  74. data/lib/easy_admin/resource/field_registry.rb +58 -2
  75. data/lib/easy_admin/resource.rb +0 -9
  76. data/lib/easy_admin/resource_modules.rb +21 -4
  77. data/lib/easy_admin/two_factor_authentication.rb +156 -0
  78. data/lib/easy_admin/version.rb +1 -1
  79. data/lib/generators/easy_admin/permissions/install_generator.rb +0 -10
  80. data/lib/generators/easy_admin/permissions/templates/migrations/create_permission_tables.rb +33 -3
  81. data/lib/generators/easy_admin/two_factor/templates/README +29 -0
  82. data/lib/generators/easy_admin/two_factor/templates/migration.rb +10 -0
  83. data/lib/generators/easy_admin/two_factor/two_factor_generator.rb +22 -0
  84. metadata +49 -9
  85. data/lib/easy_admin/resource/form_builder.rb +0 -123
  86. data/lib/easy_admin/resource/layout_builder.rb +0 -249
  87. data/lib/easy_admin/resource/show_builder.rb +0 -359
  88. data/lib/generators/easy_admin/permissions/templates/migrations/update_users_for_permissions.rb +0 -6
  89. data/lib/generators/easy_admin/rbac/rbac_generator.rb +0 -244
  90. data/lib/generators/easy_admin/rbac/templates/add_rbac_to_admin_users.rb +0 -23
  91. data/lib/generators/easy_admin/rbac/templates/super_admin.rb +0 -34
@@ -1,359 +0,0 @@
1
- module EasyAdmin
2
- module ResourceModules
3
- # ShowBuilder class for show layout DSL
4
- # Handles all show page layout definitions with rows, columns, cards, and tabs
5
- class ShowBuilder
6
- PREDEFINED_COLORS = %w[primary secondary success danger warning info light dark white transparent].freeze
7
- COLUMN_SIZES = [1, 2, 3, 4, 6, 8, 12].freeze
8
- SPACING_OPTIONS = %w[none small medium large].freeze
9
- PADDING_OPTIONS = %w[none small medium large].freeze
10
- HEADING_SIZES = %w[small medium large].freeze
11
- DIVIDER_STYLES = %w[default dashed dotted thick].freeze
12
- SHADOW_OPTIONS = %w[none small medium large].freeze
13
-
14
- def initialize(resource_class)
15
- @resource_class = resource_class
16
- @context_stack = []
17
- @current_row = nil
18
- @current_column = nil
19
- @current_card = nil
20
- @current_tab_container = nil
21
- @current_tab = nil
22
- end
23
-
24
- # Layout methods
25
- def row(columns: 1, spacing: "medium", &block)
26
- validate_spacing!(spacing)
27
-
28
- row_config = {
29
- type: :row,
30
- columns_count: columns,
31
- spacing: spacing,
32
- columns: []
33
- }
34
-
35
- push_context
36
- @current_row = row_config
37
-
38
- if @current_column
39
- @current_column[:elements] << row_config
40
- else
41
- @resource_class.add_show_layout_element(row_config)
42
- end
43
-
44
- instance_eval(&block) if block_given?
45
- pop_context
46
- end
47
-
48
- def column(size: nil, &block)
49
- raise "column must be called within a row block" unless @current_row
50
-
51
- calculated_size = size || (12 / [@current_row[:columns_count], 1].max)
52
- validate_column_size!(calculated_size)
53
-
54
- column_config = {
55
- type: :column,
56
- size: calculated_size,
57
- elements: []
58
- }
59
-
60
- push_context
61
- @current_column = column_config
62
- @current_row[:columns] << column_config
63
-
64
- instance_eval(&block) if block_given?
65
- pop_context
66
- end
67
-
68
- # Card methods
69
- def card(title: nil, color: "light", padding: "medium", shadow: "small", &block)
70
- raise "card must be called within a column block" unless @current_column
71
- validate_color!(color)
72
- validate_padding!(padding)
73
- validate_shadow!(shadow)
74
-
75
- card_config = {
76
- type: :card,
77
- title: title,
78
- color: color,
79
- padding: padding,
80
- shadow: shadow,
81
- elements: []
82
- }
83
-
84
- push_context
85
- @current_card = card_config
86
- @current_column[:elements] << card_config
87
-
88
- instance_eval(&block) if block_given?
89
- pop_context
90
- end
91
-
92
- def metric_card(title:, value:, color: "primary", icon: nil, trend: nil)
93
- raise "metric_card must be called within a column block" unless @current_column
94
- validate_color!(color)
95
-
96
- metric_config = {
97
- type: :metric_card,
98
- title: title,
99
- value: value,
100
- icon: icon,
101
- color: color,
102
- trend: trend
103
- }
104
-
105
- @current_column[:elements] << metric_config
106
- end
107
-
108
- def chart_card(title:, chart_type:, **options, &block)
109
- raise "chart_card must be called within a column block" unless @current_column
110
-
111
- chart_config = {
112
- type: :chart_card,
113
- title: title,
114
- chart_type: chart_type,
115
- height: options[:height] || 350,
116
- data_source: options[:data_source],
117
- css_classes: options[:class] || "card",
118
- config: {}
119
- }
120
-
121
- if block_given?
122
- chart_builder = ChartBuilder.new
123
- chart_builder.instance_eval(&block)
124
- chart_config[:config] = chart_builder.config
125
- end
126
-
127
- @current_column[:elements] << chart_config
128
- end
129
-
130
- # Tab methods
131
- def tabs(**options, &block)
132
- container = @current_card || @current_column
133
- raise "tabs must be called within a card or column block" unless container
134
-
135
- tabs_config = {
136
- type: :tabs,
137
- css_classes: options[:class] || "tabs-menu",
138
- tabs: []
139
- }
140
-
141
- push_context
142
- @current_tab_container = tabs_config
143
- container[:elements] << tabs_config
144
-
145
- instance_eval(&block) if block_given?
146
- pop_context
147
- end
148
-
149
- def tab(name, **options, &block)
150
- raise "tab must be called within a tabs block" unless @current_tab_container
151
-
152
- tab_config = {
153
- name: name,
154
- label: options[:label] || name,
155
- icon: options[:icon],
156
- active: options[:active] || false,
157
- elements: []
158
- }
159
-
160
- push_context
161
- @current_tab = tab_config
162
- @current_tab_container[:tabs] << tab_config
163
-
164
- instance_eval(&block) if block_given?
165
- pop_context
166
- end
167
-
168
- # Field display methods
169
- def field(name, **options)
170
- container = @current_tab || @current_card || @current_column
171
- raise "field must be called within a tab, card, or column block" unless container
172
-
173
- label_value = if options.key?(:label) && options[:label] == false
174
- nil
175
- else
176
- options[:label] || name.to_s.humanize
177
- end
178
-
179
- field_config = {
180
- type: :field,
181
- name: name,
182
- label: label_value,
183
- field_type: options[:field_type] || :default,
184
- format: options[:format],
185
- css_classes: options[:class],
186
- show_label: options.fetch(:show_label, true)
187
- }
188
-
189
- container[:elements] << field_config
190
- end
191
-
192
- def fields(*field_names, **options)
193
- field_names.each do |name|
194
- field(name, **options)
195
- end
196
- end
197
-
198
- # Component rendering support with context
199
- def render(component_class_or_instance, **options)
200
- container = @current_tab || @current_card || @current_column
201
- raise "render must be called within a tab, card, or column block" unless container
202
-
203
- component_config = {
204
- type: :custom_component,
205
- component_class: component_class_or_instance.class,
206
- component_options: options,
207
- component_instance: component_class_or_instance
208
- }
209
-
210
- container[:elements] << component_config
211
- component_class_or_instance
212
- end
213
-
214
- # Content methods
215
- def content(html = nil, **options, &block)
216
- container = @current_tab || @current_card || @current_column
217
- raise "content must be called within a tab, card, or column block" unless container
218
-
219
- content_config = {
220
- type: :content,
221
- html: html,
222
- css_classes: options[:class],
223
- block: block
224
- }
225
-
226
- container[:elements] << content_config
227
- end
228
-
229
- def heading(text, level: 2, size: "medium")
230
- container = @current_tab || @current_card || @current_column
231
- raise "heading must be called within a tab, card, or column block" unless container
232
-
233
- validate_heading_level!(level)
234
- validate_heading_size!(size)
235
-
236
- heading_config = {
237
- type: :heading,
238
- text: text,
239
- level: level,
240
- size: size
241
- }
242
-
243
- container[:elements] << heading_config
244
- end
245
-
246
- def divider(style: "default")
247
- container = @current_tab || @current_card || @current_column
248
- raise "divider must be called within a tab, card, or column block" unless container
249
-
250
- validate_divider_style!(style)
251
-
252
- divider_config = {
253
- type: :divider,
254
- style: style
255
- }
256
-
257
- container[:elements] << divider_config
258
- end
259
-
260
- private
261
-
262
- def validate_color!(color)
263
- return if PREDEFINED_COLORS.include?(color.to_s)
264
- raise ArgumentError, "Invalid color '#{color}'. Available colors: #{PREDEFINED_COLORS.join(', ')}"
265
- end
266
-
267
- def validate_column_size!(size)
268
- return if COLUMN_SIZES.include?(size)
269
- raise ArgumentError, "Invalid column size '#{size}'. Available sizes: #{COLUMN_SIZES.join(', ')}"
270
- end
271
-
272
- def validate_spacing!(spacing)
273
- return if SPACING_OPTIONS.include?(spacing.to_s)
274
- raise ArgumentError, "Invalid spacing '#{spacing}'. Available options: #{SPACING_OPTIONS.join(', ')}"
275
- end
276
-
277
- def validate_padding!(padding)
278
- return if PADDING_OPTIONS.include?(padding.to_s)
279
- raise ArgumentError, "Invalid padding '#{padding}'. Available options: #{PADDING_OPTIONS.join(', ')}"
280
- end
281
-
282
- def validate_shadow!(shadow)
283
- return if SHADOW_OPTIONS.include?(shadow.to_s)
284
- raise ArgumentError, "Invalid shadow '#{shadow}'. Available options: #{SHADOW_OPTIONS.join(', ')}"
285
- end
286
-
287
- def validate_heading_level!(level)
288
- return if (1..6).include?(level)
289
- raise ArgumentError, "Invalid heading level '#{level}'. Must be between 1 and 6"
290
- end
291
-
292
- def validate_heading_size!(size)
293
- return if HEADING_SIZES.include?(size.to_s)
294
- raise ArgumentError, "Invalid heading size '#{size}'. Available sizes: #{HEADING_SIZES.join(', ')}"
295
- end
296
-
297
- def validate_divider_style!(style)
298
- return if DIVIDER_STYLES.include?(style.to_s)
299
- raise ArgumentError, "Invalid divider style '#{style}'. Available styles: #{DIVIDER_STYLES.join(', ')}"
300
- end
301
-
302
- def push_context
303
- @context_stack.push({
304
- row: @current_row,
305
- column: @current_column,
306
- card: @current_card,
307
- tab_container: @current_tab_container,
308
- tab: @current_tab
309
- })
310
- end
311
-
312
- def pop_context
313
- if @context_stack.any?
314
- context = @context_stack.pop
315
- @current_row = context[:row]
316
- @current_column = context[:column]
317
- @current_card = context[:card]
318
- @current_tab_container = context[:tab_container]
319
- @current_tab = context[:tab]
320
- else
321
- @current_row = nil
322
- @current_column = nil
323
- @current_card = nil
324
- @current_tab_container = nil
325
- @current_tab = nil
326
- end
327
- end
328
- end
329
-
330
- # ChartBuilder class for chart configuration
331
- class ChartBuilder
332
- attr_reader :config
333
-
334
- def initialize
335
- @config = {}
336
- end
337
-
338
- def series(data)
339
- @config[:series] = data
340
- end
341
-
342
- def categories(data)
343
- @config[:categories] = data
344
- end
345
-
346
- def colors(data)
347
- @config[:colors] = data
348
- end
349
-
350
- def title(text)
351
- @config[:title] = text
352
- end
353
-
354
- def subtitle(text)
355
- @config[:subtitle] = text
356
- end
357
- end
358
- end
359
- end
@@ -1,6 +0,0 @@
1
- class UpdateUsersForEasyAdminPermissions < ActiveRecord::Migration<%= migration_version %>
2
- def change
3
- add_column :<%= user_model_name.tableize %>, :permissions_cache, :json, default: {}, null: false
4
- add_index :<%= user_model_name.tableize %>, :permissions_cache, using: :gin
5
- end
6
- end
@@ -1,244 +0,0 @@
1
- require 'rails/generators'
2
- require 'rails/generators/migration'
3
-
4
- module EasyAdmin
5
- module Generators
6
- class RbacGenerator < Rails::Generators::Base
7
- include Rails::Generators::Migration
8
-
9
- source_root File.expand_path('../templates', __FILE__)
10
-
11
- desc 'Generate EasyAdmin Role-Based Access Control setup'
12
-
13
- def self.next_migration_number(path)
14
- Time.current.utc.strftime("%Y%m%d%H%M%S")
15
- end
16
-
17
- def copy_migration
18
- migration_template(
19
- 'add_rbac_to_admin_users.rb',
20
- 'db/migrate/add_rbac_to_admin_users.rb',
21
- migration_version: migration_version
22
- )
23
- end
24
-
25
- def create_role_models
26
- template 'super_admin.rb', 'app/models/easy_admin/super_admin.rb'
27
- template 'admin.rb', 'app/models/easy_admin/admin.rb'
28
- template 'editor.rb', 'app/models/easy_admin/editor.rb'
29
- template 'viewer.rb', 'app/models/easy_admin/viewer.rb'
30
- end
31
-
32
- def update_admin_user_model
33
- inject_into_file 'app/models/easy_admin/admin_user.rb', after: "self.table_name = \"easy_admin_admin_users\"\n" do
34
- <<-RUBY
35
-
36
- # STI configuration for role-based access
37
- self.inheritance_column = :type
38
-
39
- # Associations
40
- has_many :audit_logs, class_name: 'EasyAdmin::AuditLog', foreign_key: :admin_user_id, dependent: :destroy
41
-
42
- # Scopes
43
- scope :by_role, ->(role) { where(type: "EasyAdmin::\#{role}") }
44
-
45
- # Cache permissions per request
46
- def permissions_cache
47
- @permissions_cache ||= {}
48
- end
49
-
50
- # Core authorization methods (to be overridden by subclasses)
51
- def can_access_resource?(resource_name)
52
- accessible_resources.include?(resource_name.to_s)
53
- end
54
-
55
- def can_perform_action?(resource_name, action)
56
- return false unless can_access_resource?(resource_name)
57
- allowed_actions_for(resource_name).include?(action.to_s)
58
- end
59
-
60
- def can_access_field?(resource_name, field_name, action = :read)
61
- return false unless can_access_resource?(resource_name)
62
- !restricted_fields_for(resource_name, action).include?(field_name.to_s)
63
- end
64
-
65
- # Override in subclasses
66
- def accessible_resources
67
- []
68
- end
69
-
70
- def allowed_actions_for(resource_name)
71
- [:index, :show]
72
- end
73
-
74
- def restricted_fields_for(resource_name, action)
75
- []
76
- end
77
-
78
- def apply_resource_scope(resource_name, scope)
79
- scope
80
- end
81
-
82
- # Helper for caching expensive operations
83
- def cached_permission(key, &block)
84
- permissions_cache[key] ||= block.call
85
- end
86
- RUBY
87
- end
88
- end
89
-
90
- def create_permission_cache
91
- template 'permission_cache.rb', 'lib/easy_admin/permission_cache.rb'
92
- end
93
-
94
- def create_audit_log_model
95
- template 'audit_log.rb', 'app/models/easy_admin/audit_log.rb'
96
-
97
- migration_template(
98
- 'create_audit_logs.rb',
99
- 'db/migrate/create_easy_admin_audit_logs.rb',
100
- migration_version: migration_version
101
- )
102
- end
103
-
104
- def update_application_controller
105
- inject_into_file 'app/controllers/easy_admin/application_controller.rb',
106
- before: "include Pagy::Backend" do
107
- <<-RUBY
108
- class UnauthorizedError < StandardError; end
109
-
110
- RUBY
111
- end
112
-
113
- inject_into_file 'app/controllers/easy_admin/application_controller.rb',
114
- after: "before_action :authenticate_easy_admin_admin_user!\n" do
115
- <<-RUBY
116
- before_action :check_resource_access!, except: [:index]
117
-
118
- rescue_from UnauthorizedError do |exception|
119
- respond_to do |format|
120
- format.html { redirect_to easy_admin.root_path, alert: 'You are not authorized to access this resource.' }
121
- format.turbo_stream do
122
- render turbo_stream: turbo_stream.replace("notifications",
123
- EasyAdmin::NotificationComponent.new(
124
- message: 'You are not authorized to access this resource',
125
- type: :error
126
- ).call
127
- )
128
- end
129
- end
130
- end
131
- RUBY
132
- end
133
-
134
- inject_into_file 'app/controllers/easy_admin/application_controller.rb',
135
- before: "def authenticate_admin_user!" do
136
- <<-RUBY
137
- def check_resource_access!
138
- return unless defined?(@resource_class)
139
-
140
- unless current_admin_user.can_access_resource?(@resource_class.resource_name)
141
- raise UnauthorizedError, "Access denied to \#{@resource_class.resource_name}"
142
- end
143
-
144
- check_action_permission!
145
- end
146
-
147
- def check_action_permission!
148
- action_map = {
149
- 'index' => :index,
150
- 'show' => :show,
151
- 'new' => :new,
152
- 'create' => :create,
153
- 'edit' => :edit,
154
- 'update' => :update,
155
- 'destroy' => :destroy,
156
- 'execute' => :batch_action
157
- }
158
-
159
- mapped_action = action_map[action_name] || action_name.to_sym
160
-
161
- unless current_admin_user.can_perform_action?(@resource_class.resource_name, mapped_action)
162
- raise UnauthorizedError, "Cannot perform \#{mapped_action} on \#{@resource_class.resource_name}"
163
- end
164
- end
165
-
166
- def apply_role_scoping(scope)
167
- current_admin_user.apply_resource_scope(@resource_class.resource_name, scope)
168
- end
169
-
170
- def permission_cache
171
- @permission_cache ||= EasyAdmin::PermissionCache.new(current_admin_user)
172
- end
173
-
174
- RUBY
175
- end
176
-
177
- inject_into_file 'app/controllers/easy_admin/application_controller.rb',
178
- after: "helper_method :current_admin_user, :admin_user_signed_in?" do
179
- ", :permission_cache"
180
- end
181
- end
182
-
183
- def create_seeds
184
- create_file 'db/seeds/easy_admin_rbac.rb', <<~RUBY
185
- # Create default admin users with different roles
186
- if Rails.env.development?
187
- # Super Admin - full access
188
- EasyAdmin::SuperAdmin.find_or_create_by(email: 'super@example.com') do |admin|
189
- admin.password = 'password'
190
- admin.password_confirmation = 'password'
191
- admin.first_name = 'Super'
192
- admin.last_name = 'Admin'
193
- admin.confirmed_at = Time.current
194
- end
195
-
196
- # Standard Admin - most access except system settings
197
- EasyAdmin::Admin.find_or_create_by(email: 'admin@example.com') do |admin|
198
- admin.password = 'password'
199
- admin.password_confirmation = 'password'
200
- admin.first_name = 'Admin'
201
- admin.last_name = 'User'
202
- admin.confirmed_at = Time.current
203
- end
204
-
205
- # Editor - content management only
206
- EasyAdmin::Editor.find_or_create_by(email: 'editor@example.com') do |admin|
207
- admin.password = 'password'
208
- admin.password_confirmation = 'password'
209
- admin.first_name = 'Content'
210
- admin.last_name = 'Editor'
211
- admin.confirmed_at = Time.current
212
- end
213
-
214
- # Viewer - read-only access
215
- EasyAdmin::Viewer.find_or_create_by(email: 'viewer@example.com') do |admin|
216
- admin.password = 'password'
217
- admin.password_confirmation = 'password'
218
- admin.first_name = 'Read'
219
- admin.last_name = 'Only'
220
- admin.confirmed_at = Time.current
221
- end
222
-
223
- puts "EasyAdmin RBAC users created!"
224
- puts "Roles available:"
225
- puts " SuperAdmin - super@example.com / password (full access)"
226
- puts " Admin - admin@example.com / password (standard admin)"
227
- puts " Editor - editor@example.com / password (content only)"
228
- puts " Viewer - viewer@example.com / password (read-only)"
229
- end
230
- RUBY
231
- end
232
-
233
- def show_readme
234
- readme 'RBAC_README'
235
- end
236
-
237
- private
238
-
239
- def migration_version
240
- "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
241
- end
242
- end
243
- end
244
- end
@@ -1,23 +0,0 @@
1
- class AddRbacToAdminUsers < ActiveRecord::Migration<%= migration_version %>
2
- def change
3
- # Add type column for STI
4
- add_column :easy_admin_admin_users, :type, :string
5
- add_column :easy_admin_admin_users, :permissions, :json, default: {}
6
- add_column :easy_admin_admin_users, :resource_access, :json, default: {}
7
-
8
- # Add indexes for performance
9
- add_index :easy_admin_admin_users, :type
10
- add_index :easy_admin_admin_users, [:type, :locked_at]
11
-
12
- # Migrate existing users to Admin type
13
- reversible do |dir|
14
- dir.up do
15
- execute <<-SQL
16
- UPDATE easy_admin_admin_users
17
- SET type = 'EasyAdmin::Admin'
18
- WHERE type IS NULL
19
- SQL
20
- end
21
- end
22
- end
23
- end
@@ -1,34 +0,0 @@
1
- module EasyAdmin
2
- class SuperAdmin < AdminUser
3
- def accessible_resources
4
- cached_permission(:resources) do
5
- EasyAdmin::ResourceRegistry.all_resources.map(&:resource_name)
6
- end
7
- end
8
-
9
- def allowed_actions_for(resource_name)
10
- [:index, :show, :new, :create, :edit, :update, :destroy, :batch_action, :row_action].map(&:to_s)
11
- end
12
-
13
- def restricted_fields_for(resource_name, action)
14
- [] # No restrictions for super admin
15
- end
16
-
17
- def apply_resource_scope(resource_name, scope)
18
- scope # No filtering for super admin
19
- end
20
-
21
- # Super admin specific methods
22
- def super_admin?
23
- true
24
- end
25
-
26
- def role_name
27
- 'Super Admin'
28
- end
29
-
30
- def role_badge_color
31
- 'red'
32
- end
33
- end
34
- end